/*
   mb-applet-powerctrl - a screen blanking applet

   Copyright 2007 Hu Yong <ecc_hy@hotmail.com>

   Based on:
     mb-applet-menu-launcher    (Copyright 2002 Matthew Allum)
     mb-applet-cards            (Copyright 2004 Alexander Chukov)
     mb-applet-blanker          (Copyright 2004 Matt Purvis)

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#include <gtk/gtk.h>
#include <gdk/gdkx.h>

#define IMG_PREFIX	"/usr/share/pixmaps/"
#define IMG_YES	   "/usr/share/pixmaps/powerctrl-yes.png"
#define IMG_NUL	   "/usr/share/pixmaps/powerctrl-nul.png"

Display  *g_pDisplay;

GtkWidget *wnd_blanker;
Bool PopupIsMapped = False;

Bool g_bUsbFunction = True;
Bool g_bScreensaver = True;
Bool g_bSuspend = True;
Bool g_bScreenBlank = False;
Bool g_bMute = False;
int volumeMuted;

struct
{
   Bool bEnable;
   int iCardSD;
   char sNameSD[255];
   int iCardCF;
   char sNameCF[255];
   int iUsbStorage;
} g_cards;
Bool g_bSwapCtrl = True;

struct
{
   int ac_backlight;
   int ac_screensaver;
   int ac_suspend;

   int ba_backlight;
   int ba_screensaver;
   int ba_suspend;
} lnp;

int backlight_now = 0;
gint backlight_timer;

#include <apm.h>
#define TIME_LEFT  0
#define PERCENTAGE 1
#define AC_POWER   2
int g_apm_vals[3];

#ifndef AC_LINE_STATUS_ON
#define AC_LINE_STATUS_ON 1
#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef USE_DNOTIFY
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>

#include <libmb/mb.h>

#ifdef ENABLE_NLS
# include <libintl.h>
# define _(text) gettext(text)
#else
# define _(text) (text)
#endif

#ifdef USE_LIBSN
#define SN_API_NOT_YET_FROZEN 1
#include <libsn/sn.h>
#endif

#ifndef MB_HAVE_PNG
#include <setjmp.h>
#endif

char g_sMenuFont[64] = "Verdana-12";

struct {
   /* cpu data  */
   int loadIndex;
   int samples;
   u_int64_t *load, *total;
 
   /* memory data  */
   u_int64_t mem_used;
   u_int64_t mem_max;
   u_int64_t swap_used;
   u_int64_t swap_max;
   unsigned int swap_percent;  /* swap used, in percent */
   unsigned int mem_percent;   /* memory used, in percent */
} msd;

#ifndef MAXPATHLEN
    #define MAXPATHLEN 1024
#endif
char kistat[MAXPATHLEN];

struct
{
   MBTrayApp   *tray_app;
   MBMenu      *mbmenu;
   MBPixbuf    *pb;

   Atom        mbtheme_atom;
   Atom        mbcommand_atom;

   char        *theme_name;

#ifdef USE_LIBSN
   SnDisplay   *sn_display;
#endif

} g_app_data = {0};

typedef struct
{
   int x, y, w, h;
} RECT;

typedef struct
{
   unsigned char r, g, b;
} RGB;

struct
{
   int  scaled_w, scaled_h;
   RECT rcCpu, rcMem, rcBattery, rcFreq;
   RECT rcCpuOut, rcMemOut, rcBatteryOut;
   int  freq_gap;

   MBPixbufImage *pOrig;
   MBPixbufImage *pScaled;
   MBPixbufImage *pDraw;
} g_img = {0};

typedef struct
{
   Bool bDisplay;
   RECT rc;
   int  iOutline;
   RGB  rgbBack, rgbOutline, rgbBegin, rgbStep;
} DISP;

DISP g_dispcpu = {0};
DISP g_dispmem = {0};
DISP g_dispfreq = {0};
DISP g_dispba = {0};

struct
{
   RGB rgbBackAC;
   int iUsg0;
   RGB rgbUsg0;
   RGB rgbStep0;
   int iUsg1;
   RGB rgbUsg1;
   RGB rgbStep1;
   int iUsg2;
   RGB rgbUsg2;
   RGB rgbStep2;
} g_dispba_ext;

#ifndef MAXBLNUM
  #define  MAXBLNUM  6
#endif
int g_backlight[MAXBLNUM];

#ifndef MAXFREQNUM
  #define  MAXFREQNUM  6
#endif

#define FREQ_NUM    0
#define FREQ_CCCR   1
#define FREQ_VCORE  2
#define FREQ_STABLE 3

struct
{
   char sCPU[32 + 1];
   int  iCurIdx;
   int  iDefaultIdx;
   int  iMaxIdx;

   char sPathCCCR[255];
   char sPathVCORE[255];

   int  aiVal[MAXFREQNUM + 1][4];
} g_freq;

Bool g_bSpeedStep = False;
int  g_iBatteryWarn;
long g_lOverProtect;
Bool g_bProtected = False;
int  g_iStepDelay;

struct
{
   int iUsgHigher;
   int iUsgLower;
   int iMaxFreq;
   int iMinFreq;
} g_ac_step;

struct
{
   int iUsgHigher;
   int iUsgLower;
   int iMaxFreq;
   int iMinFreq;
} g_ba_step;

#ifndef MAXVOLNUM
  #define  MAXVOLNUM  11
#endif
struct
{
   Bool bEnable;
   int  iVolume;
   int  aiPre[MAXVOLNUM];
} g_vol = {0};

//MBMenuMenu  *root;
//MBMenuMenu  *active[10];
//int          MenuWasActive   = False;

//jmp_buf      Jbuf;

/*
static void reap_children (int signum);
static void fork_exec (char *cmd);
void menu_build (void);
//static void catch_sigsegv (int sig);
void load_icons(void);
static void screensaver_toggle_cb(MBMenuItem *item);
static void suspend_toggle_cb(MBMenuItem *item);
static void backlight_timeout_cb ( MBTrayApp *app );

#ifdef USE_LIBSN
static void sn_exec(char* name, char* bin_name, char *desc);
#endif
*/

#define TRAY_IMG "powerctrl-usage.png"

#define MBMAX(x,y) ((x>y)?(x):(y))

#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\\' \
                          || (c) == '\t' || (c)=='\n' || (c)=='?')

//#define DEBUG

#ifdef DEBUG
#define DBG(txt, args... ) fprintf(stderr, "DEBUG: " txt , ##args )
#else
#define DBG(txt, args... ) /* nothing */
#endif


/*---------------------------------------------------------------------/
/
/  Description:  CfgOpts is based on GETOPTS by Bob Stout.  It will
/                process a configuration file based one words and
/                store it in a structure pointing to physical data
/                area for each storage item.
/  i.e. ???.CFG:
/    Port=1
/    work_space=C:\temp
/    menus=Yes
/    user=Jeffry Brickley
/  will write to the following structure:
/    struct Config_Tag configs[] = {
/    {"port",       Word_Tag,    &port_number},
/    {"work_space", String_Tag,  work_space, sizeof(work_space)},
/    {"menus",      Bool_Tag, &menu_flag},
/    {"user",       String_Tag,  User_name, sizeof(User_name)},
/    {NULL,         Error_Tag,   NULL}
/    };
/  Note that the structure must always be terminated by a NULL row as
/     was the same with GETOPTS.  This however is slightly more
/     complicated than scaning the command line (but not by much) for
/     data as there can be more variety in words than letters and an
/     number of data items limited only by memory.  Currently CfgOpts
/     is not case sensitive, but this can be changed by replacing all
/     "strcasecmp" functions with "strcmp" functions.
/
/  Like the original code from which this was taken, this is released
/  to the Public Domain.  I cannot make any guarentees other than these
/  work for me and I find them usefull.  Feel free to pass these on to
/  a friend, but please do not charge him....
/
/---------------------------------------------------------------------*/
typedef enum 
{
   Error_Tag,
   Byte_Tag,
   Bool_Tag,
   Word_Tag,
   Int_Tag,
   Long_Tag,
   Oct_Tag,
   OctLong_Tag,
   Hex_Tag,
   HexLong_Tag,
   Float_Tag,
   Double_Tag,
   Char_Tag,
   String_Tag,
   Path_Tag,
   Function_Tag
} TAG_TYPE;

struct Config_Tag 
{
   const char  *code;              /* Option switch        */
   TAG_TYPE    type;                /* Type of option       */
   void        *buf;                /* Storage location     */
   int       buf_size;            /* Storage size for String_Tag - max. 32k */
   char        stat;                /* internal flag for update_config */
};

/*
 * FCOPY.C - copy one file to another.  Returns the (positive)
 *           number of bytes copied, or -1 if an error occurred.
 * by: Bob Jarvis
 */

/*---------------------------------------------------------------------/
/   copy one file to another.
/---------------------------------------------------------------------*/
/*>>------[       fcopy()      ]-------------[ 08-02-95 14:02PM ]------/
/ return value:
/     long                    ; Number of bytes copied or -1 on error
/ parameters:
/     char *dest              ; Destination file name
/     char *source            ; Source file name
/-------------------------------------------------------------------<<*/
long fcopy(const char *dest, const char *source)
{
   FILE * d, *s;
   char    buffer[1024];
   size_t bufsize = sizeof(buffer);
   size_t incount, outcount;
   long    totcount = 0L;

   s = fopen(source, "rb");
   if (s == NULL)
      return - 1L;

   d = fopen(dest, "wb");
   if (d == NULL) 
   {
      fclose(s);
      return - 1L;
   }

   totcount = 0;
   incount = outcount = 0;
   while (!feof(s)) 
   {
      incount = fread(buffer, sizeof(char), bufsize, s);
      if (incount <= 0)
      {
         break;
      }           
      totcount += (long)incount;
      outcount += fwrite(buffer, sizeof(char), incount, d);
   }

   fclose(s);
   fclose(d);

   if (outcount < totcount)
      return -1L;             /* disk full? */

   return totcount;
}

#define SPACE   ' '
#define TABULA  '\t'
char *trim(char *buffer)
{

   if (buffer != NULL) 
   {
      int i, linelen = strlen(buffer);

      for (i = 0; i < linelen; i++)
         if (buffer[i] != SPACE && buffer[i] != TABULA)
            break;

      if (i > 0 && i < linelen) 
      {
         linelen -= i;
         memmove(buffer, buffer + i, linelen);   /* trim spaces on left */
      }

      for (i = linelen; i > 0; i--) 
      {
         int j = i-1;
         if (buffer[j] != SPACE && buffer[j] != TABULA)
            break;
      }

      buffer[i] = '\0';                       /* trim spaces on right */
   }

   return buffer;
}


#define REM1    '#'
#define REM2    ';'
char *strip_comment(char *line)
{
   int i, j;

   if (line == NULL)
       return NULL;

   j = strlen(line);
   for (i = 0; i < j; i++)
   {
      if (line[i] == REM1 || line[i] == REM2) 
      {
         line[i] = '\0';
         break;
      }
   }
   return line;
}

/*---------------------------------------------------------------------/
/   reads from an input configuration (INI) file.
/---------------------------------------------------------------------*/
/*>>------[   get_config()   ]-------------[ 08-02-95 14:02PM ]------/
/ return value:
/     int                     ; number of records read or -1 on error
/ parameters:
/     char *filename          ; filename of INI style file
/     struct Config_Tag configs[]; Configuration structure
/     char *header            ; INI header name (i.e. "[TEST]")
/-------------------------------------------------------------------<<*/
int get_config(const char *filename, char *header, struct Config_Tag configs[])
{
   struct Config_Tag *ptr;
   int count = 0, lineno = 0, temp;
   FILE * file;
   char *fptr, *tok, *next;
   char line[1024];

   // DBG("%s . %s\n", filename, header);
   file = fopen(filename, "rt");
   if ( file == NULL )
   {
      fprintf(stderr, "failed to open [%s].\n", filename);
      return -1;  /* return error designation. */
   }
    
   // fgets, The string is then terminated with a null byte
   // serach header
   char sNewHeader[255] = {0};
   if ( header != NULL || strlen(header) > sizeof(sNewHeader) - 3)
   {
      sprintf(sNewHeader, "[%s]", header);
      do {
          fptr = trim(fgets(line, sizeof(line), file));  /* get input line */
      } while ( (strncasecmp(line, sNewHeader, strlen(sNewHeader)) != 0) && !feof(file));
   }

   if ( feof(file) ) 
   {
      fprintf(stderr, "failed to get header [%s].\n", header);
      fclose(file);
      return count;
   }

   do 
   {
      fptr = trim(fgets(line, sizeof(line), file));  /* get input line */
      if ( fptr == NULL ) 
         continue;
      lineno++;
      strip_comment(line);

      if ( line[0] == '[' ) 
         continue;    /* next header is the end of our section */

      tok = trim(strtok(line, "=\n\r"));   /* get first token */
      if ( tok == NULL ) 
         continue;
           
      next = trim(strtok(NULL, "\n\r")); /* get actual config information */
      for ( ptr = configs; ptr->buf; ++ptr )   /* scan for token */ 
      {
         if ( strcasecmp( tok, ptr->code ) != 0 )  /* not got a match? */ 
         {
            continue;
         }
         if (next == NULL) 
         {
            if ( ptr->type == Path_Tag || ptr->type == String_Tag ) 
            {/* path or string may be empty */
               *(char *)ptr->buf = 0;
               ++count;
            } else {
               fprintf(stderr, ">>> Missing value in Config file %s on line %d !!!\n", filename, lineno);
            }
            continue;
         }
         // DBG("%s . %s . %s \n", header, tok, next);
         switch ( ptr->type )     /* check type */ 
         {
         case Bool_Tag:
            *((Bool * )(ptr->buf)) = (!strcasecmp(next, "False") || !strcasecmp(next, "No")) ? False : True;
            ++count;
            break;

         case Byte_Tag:
            sscanf(next, "%d", &temp);
            *((char *)(ptr->buf)) = (char)temp;
            ++count;
            break;

         case Word_Tag:
            sscanf(next, "%hd", (short *)(ptr->buf));
            ++count;
            break;

         case Int_Tag:
            sscanf(next, "%d",  (int *)(ptr->buf));
            ++count;
            break;

         case Long_Tag:
            sscanf(next, "%ld", (long *)(ptr->buf));
            ++count;
            break;

         case Oct_Tag:
            sscanf(next, "%o",  (int *)(ptr->buf));
            ++count;
            break;

         case OctLong_Tag:
            sscanf(next, "%lo", (long *)(ptr->buf));
            ++count;
            break;

         case Hex_Tag:
            sscanf(next, "%x",  (int *)(ptr->buf));
            ++count;
            break;

         case HexLong_Tag:
            sscanf(next, "%lx", (long *)(ptr->buf));
            ++count;
            break;

         case Float_Tag:
            sscanf(next, "%g", (float *)ptr->buf);
            ++count;
            break;

         case Double_Tag:
            sscanf(next, "%lg", (double *)ptr->buf);
            ++count;
            break;

         case Char_Tag:
            *(char *)ptr->buf = *next;
            ++count;
            break;

         case Path_Tag:
         case String_Tag:
            if (ptr->buf_size > 0) 
            {
               char *cptr = (char *)ptr->buf;
               int bufsize = ptr->buf_size;
               strncpy(cptr, next, bufsize);
               cptr[bufsize-1] = '\0'; /* EOS */
               ++count;
            } else {
               fprintf(stderr, ">>> Wrong buf_size in Config_Tag struct: directive %s, buf_size %d !!!\n", ptr->code, ptr->buf_size);
            }
            break;

         case Function_Tag:
         case Error_Tag:
         default:
            fprintf(stderr, "Error in Config file %s on line %d\n",
                    filename, lineno);
            break;
         }
      }
   } while ( fptr != NULL && line[0] != '[');
   fclose(file);
   return count;
}

void str2rect(RECT *pRC, char *pStr)
{
   if (pStr == NULL || pStr[0] == 0)
      return;
   char *pToken;
   /* get first token */
   pToken = trim(strtok(pStr, ","));
   if ( pToken == NULL ) return;
   pRC->x = atoi(pToken);
      
   pToken = trim(strtok(NULL, ","));
   if ( pToken == NULL ) return;
   pRC->y = atoi(pToken);
      
   pToken = trim(strtok(NULL, ","));
   if ( pToken == NULL ) return;
   pRC->w = atoi(pToken);

   pToken = trim(strtok(NULL, ","));
   if ( pToken == NULL ) return;
   pRC->h = atoi(pToken);
}
   
void str2rgb(RGB *pRGB, char *pStr)
{
   char *pToken;
   /* get first token */
   pToken = trim(strtok(pStr, ","));
   if ( pToken == NULL ) return;
   pRGB->r = atoi(pToken);
      
   pToken = trim(strtok(NULL, ","));
   if ( pToken == NULL ) return;
   pRGB->g = atoi(pToken);
      
   pToken = trim(strtok(NULL, ","));
   if ( pToken == NULL ) return;
   pRGB->b = atoi(pToken);
}

void load_ba_ext(char *psConfig, char *pHdr)
{
   char sRgbBackAC[64];
   char sRgbUsg0[64];
   char sRgbStep0[64];
   char sRgbUsg1[64];
   char sRgbStep1[64];
   char sRgbUsg2[64];
   char sRgbStep2[64];
   
   memset(&g_dispba_ext, 0, sizeof(g_dispba_ext));
   struct Config_Tag disp_freq[] = 
   {
      {"rgbBackAC",     String_Tag, sRgbBackAC, sizeof(sRgbBackAC) },
      {"usage0",        Int_Tag,    &g_dispba_ext.iUsg0 },
      {"rgbUsage0",     String_Tag, sRgbUsg0, sizeof(sRgbUsg0) },
      {"rgbStep0",      String_Tag, sRgbStep0, sizeof(sRgbStep0) },
      {"usage1",        Int_Tag,    &g_dispba_ext.iUsg1 },
      {"rgbUsage1",     String_Tag, sRgbUsg1, sizeof(sRgbUsg1) },
      {"rgbStep1",      String_Tag, sRgbStep1, sizeof(sRgbStep1) },
      {"usage2",        Int_Tag,    &g_dispba_ext.iUsg2 },
      {"rgbUsage2",     String_Tag, sRgbUsg2, sizeof(sRgbUsg2) },
      {"rgbStep2",      String_Tag, sRgbStep2, sizeof(sRgbStep2) },
      {NULL,            Error_Tag,  NULL }
   };
   if (get_config(psConfig, pHdr, disp_freq) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config %s display.\n", pHdr);
      exit(1); 
   }
   str2rgb(&g_dispba_ext.rgbBackAC, sRgbBackAC);
   str2rgb(&g_dispba_ext.rgbUsg0, sRgbUsg0);
   str2rgb(&g_dispba_ext.rgbStep0, sRgbStep0);
   str2rgb(&g_dispba_ext.rgbUsg1, sRgbUsg1);
   str2rgb(&g_dispba_ext.rgbStep1, sRgbStep1);
   str2rgb(&g_dispba_ext.rgbUsg2, sRgbUsg2);
   str2rgb(&g_dispba_ext.rgbStep2, sRgbStep2);
}

void load_disp(char *psConfig, char *pHdr, DISP *pDisp)
{
   char sRect[64];
   char sRgbBack[64];
   char sRgbOutline[64];
   char sRgbBegin[64];
   char sRgbStep[64];

   struct Config_Tag disp_freq[] = 
   {
      {"bDisplay",      Bool_Tag,   &pDisp->bDisplay },
      {"rect",          String_Tag, sRect, sizeof(sRect) },
      {"Outline",      Int_Tag,     &pDisp->iOutline },
      {"rgbOutLine",    String_Tag, sRgbOutline, sizeof(sRgbOutline) },
      {"rgbBack",       String_Tag, sRgbBack, sizeof(sRgbBack) },
      {"rgbBegin",      String_Tag, sRgbBegin, sizeof(sRgbBegin) },
      {"rgbStep",       String_Tag, sRgbStep, sizeof(sRgbStep) },
      {NULL,            Error_Tag,  NULL }
   };
   if (get_config(psConfig, pHdr, disp_freq) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config %s display.\n", pHdr);
      exit(1); 
   }
   str2rect(&pDisp->rc, sRect);
   str2rgb(&pDisp->rgbOutline, sRgbOutline);
   str2rgb(&pDisp->rgbBack, sRgbBack);
   str2rgb(&pDisp->rgbBegin, sRgbBegin);
   str2rgb(&pDisp->rgbStep, sRgbStep);
}

void load_config_cpu(char *psConfig)
{
   struct Config_Tag cpu_config[] = 
   {
      {"PathCCCR",      String_Tag, g_freq.sPathCCCR, sizeof(g_freq.sPathCCCR) },
      {"PathVCORE",     String_Tag, g_freq.sPathVCORE, sizeof(g_freq.sPathVCORE) },
      {"MaxFreq",       Int_Tag,    &g_freq.iMaxIdx },
      {"DefaultFreq",   Int_Tag,    &g_freq.iDefaultIdx },
      {NULL,            Error_Tag,  NULL }
   };
   if (get_config(psConfig, g_freq.sCPU, cpu_config) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config CPU.\n");
      exit(1); 
   }
   
   char sBackLight[255];
   char asFreqs[MAXFREQNUM][255];
   memset(asFreqs, 0, sizeof(asFreqs));
   struct Config_Tag freq_config[] = 
   {
      {"backlight",  String_Tag, sBackLight, sizeof(sBackLight) },
      {"freq0",      String_Tag, asFreqs[0], 255 },
      {"freq1",      String_Tag, asFreqs[1], 255 },
      {"freq2",      String_Tag, asFreqs[2], 255 },
      {"freq3",      String_Tag, asFreqs[3], 255 },
      {"freq4",      String_Tag, asFreqs[4], 255 },
      {"freq5",      String_Tag, asFreqs[5], 255 },
      {NULL,           Error_Tag,   NULL }
   };
   if (get_config(psConfig, g_freq.sCPU, freq_config) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config BACKLIGHT and FREQ.\n");
      exit(1);
   }
   if (sBackLight[0] == 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config BACKLIGHT.\n");
      exit(1);
   }
   
   char *pToken;
   pToken = trim(strtok(sBackLight, ","));   /* get first token */
   if ( pToken != NULL )
   {
      sscanf(pToken, "%x", &g_backlight[0]);
   }
   int i;
   for (i = 1; i < MAXBLNUM; i++)
   {
      pToken = trim(strtok(NULL, ",")); /* get actual config information */
      if ( pToken == NULL ) continue;

      sscanf(pToken, "%x", &g_backlight[i]);
   }

   for (i = 0; i < MAXFREQNUM; i++)
   {
      if (asFreqs[i][0] == 0) continue;
      
      pToken = trim(strtok(asFreqs[i], ","));   /* get first token */
      if ( pToken == NULL ) continue;
      g_freq.aiVal[i][FREQ_NUM] = atoi(pToken);
      
      pToken = trim(strtok(NULL, ",")); /* get CCCR */
      if ( pToken == NULL ) continue;
      sscanf(pToken, "%x", &g_freq.aiVal[i][FREQ_CCCR]);
      
      pToken = trim(strtok(NULL, ",")); /* get VCORE */
      if ( pToken == NULL ) continue;
      sscanf(pToken, "%x", &g_freq.aiVal[i][FREQ_VCORE]);

      pToken = trim(strtok(NULL, ",")); /* get stable or unstable */
      if ( pToken == NULL ) 
      {
         g_freq.aiVal[i][FREQ_STABLE] = 1;
      } else {
         sscanf(pToken, "%x", &g_freq.aiVal[i][FREQ_STABLE]);
      }
   }
}

void load_config_vol(char *psConfig)
{
   g_vol.bEnable = False;
   
   char sVol[255];
   struct Config_Tag vol_config[] = 
   {
      {"bVolumeCtrl", Bool_Tag, &g_vol.bEnable },
      {"volumes",  String_Tag, sVol, sizeof(sVol) },
      {NULL,       Error_Tag,  NULL }
   };
   if (get_config(psConfig, "powerctrl", vol_config) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config Volume.\n");
      exit(1);
   }
   if (sVol[0] == 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config Volume.");
      exit(1);
   }
   
   memset(&g_vol.aiPre, -1, sizeof(g_vol.aiPre));

   char *pToken;
   pToken = trim(strtok(sVol, ","));   /* get first token */
   if ( pToken != NULL )
   {
      sscanf(pToken, "%x", &g_vol.aiPre[0]);
   }
   int i;
   for (i = 1; i < MAXVOLNUM; i++)
   {
      pToken = trim(strtok(NULL, ",")); /* get actual config information */
      if ( pToken == NULL ) continue;

      g_vol.aiPre[i] = atoi(pToken);
   }
}

void load_config(int argc, char *argv[])
{
   char sConfig[MAXPATHLEN];
   strcpy(sConfig, getenv("HOME"));
   strcat(sConfig, "/Choices/powerctrl.cfg");

   // read config file path
   Bool bTestOnly = False;
   int i;
   for (i = 1; i < argc; i++) 
   {
      if (!strcmp ("-c", argv[i])) 
      {
         if (++i >= argc)
         {
            fprintf(stderr, "%s (%s) usage:\n\t%s -c <config file> -t\n",
                    argv[0], VERSION, argv[0]);
            exit(1);
         }
         strncpy(sConfig, argv[i], MAXPATHLEN);
         sConfig[MAXPATHLEN - 1] = 0;
         continue;
      }
      if (!strcmp ("-t", argv[i])) 
      {
         bTestOnly = True;
         continue;
      }
      fprintf(stderr, "%s (%s) usage:\n\t%s -c <config file> -t\n",
         argv[0], VERSION, argv[0]);
      exit(1);
   }

   // check config can be opened or not
   char sDefault[MAXPATHLEN] = "/etc/powerctrl.cfg";
   FILE *f;
   if ( (f = fopen(sConfig, "r")) == NULL)
   {
      if (!strcmp(sConfig, sDefault))
      {
         fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", sDefault);
         exit(1);
      }
      if ( (f = fopen(sDefault, "r")) == NULL)
      {
         fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", sDefault);
         exit(1);
      }
      fclose(f);
      DBG("copy %s to %s\n", sDefault, sConfig);
      if (fcopy(sConfig, sDefault) < 0l)
      {  // can not be copied, use /etc/powerctrl.cfg
         fprintf(stderr, "mb-applet-powerctrl: failed to copy %s to %s.\n", sDefault, sConfig);
         strcpy(sConfig, sDefault);
      }
   } else {
      fclose(f);
   }

   DBG("Read config [%s]\n", sConfig);
   // read model file path
   char sPathModel0[MAXPATHLEN] = {0};
   char sPathModel1[MAXPATHLEN] = {0};
   struct Config_Tag sys_config[] = 
   {
      {"MenuFont",    String_Tag, g_sMenuFont,  sizeof(g_sMenuFont) },
      {"PathModel0",  String_Tag, sPathModel0, sizeof(sPathModel0) },
      {"PathModel1",  String_Tag, sPathModel1, sizeof(sPathModel1) },
      {"bCardCtrl",   Bool_Tag,   &g_cards.bEnable },
      {"bSwapCtrl",   Bool_Tag,   &g_bSwapCtrl },
      {"bUsbFunction",   Bool_Tag,   &g_bUsbFunction },
      {NULL,          Error_Tag,  NULL}
   };
   if (get_config(sConfig, "PowerCtrl", sys_config) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to read PathModel.\n");
      exit(1); 
   }
   // read Model name
   char sModel[255] = {0};
   if ((f = fopen(sPathModel0, "r")) == NULL)
   {
      if ((f = fopen(sPathModel1, "r")) == NULL)
      {
        fprintf(stderr, "mb-applet-powerctrl: failed to open %s and %s\n", 
                sPathModel0, sPathModel1);
        exit(1);
      }
   }

   if (fgets(sModel, sizeof(sModel), f) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to read Model.\n");
      exit(1);
   }
   fclose(f);
   
   char *pToken;
   pToken = trim(strtok(sModel, "\n\r"));   /* get first token */
   if (pToken == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to read Model name.\n");
      exit(1);
   }
   DBG("Product [%s]\n", pToken);

   memset(&g_freq, 0, sizeof(g_freq));
   // Model name convert to CPU name
   struct Config_Tag model_config[] = 
   {
      {pToken,     String_Tag, g_freq.sCPU, sizeof(g_freq.sCPU)},
      {NULL,        Error_Tag, NULL}
   };
   if (get_config(sConfig, "PowerCtrl", model_config) <= 0)
   {
      exit(1); 
   }

   if (strlen(g_freq.sCPU) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to read CPU name.\n");
      exit(1);
   }
   DBG("CPU [%s]\n", g_freq.sCPU);

   // get config - backlight, freq
   load_config_cpu(sConfig);
   if (bTestOnly == True)
   {
      for (i = 0; i < MAXBLNUM; i++)
      {
         fprintf(stderr, "Read backlight %d - 0x%x\n", i, g_backlight[i]);
      }

      int iCCCR;  // cpu register
      int iRegL, iRegM, iRegN;
	   
	   int aiListL[] = {0, 27, 32, 36, 40, 45};
	   int aiListM[] = {0, 1, 2, 4};
	   float afListN[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5};
	   
	   int iDataL, iDataM;
	   float fDataN;
	   
	   float fMem, fRun, fTurbo, fPXBus;
      for (i = 0; i < MAXFREQNUM; i++)
      {
	      iCCCR = g_freq.aiVal[i][FREQ_CCCR];
         if (iCCCR == 0)
            continue;
	      
         fprintf(stderr, "Read Freq %d - %d, 0x%x, 0x%x\n", i, 
            g_freq.aiVal[i][FREQ_NUM], g_freq.aiVal[i][FREQ_CCCR], g_freq.aiVal[i][FREQ_VCORE]);

	      // Based on qclockchange-0.1a
	      //	iCCCR = iRegN * 128 + iRegM * 32 + iRegL;
	      iRegL = iCCCR & 0x001f;
	      iRegM = (iCCCR >> 5) & 0x0003;
	      iRegN = (iCCCR >> 7) & 0x0007;
	      
	      iDataL = 0;
	      if (iRegL >= 0 && iRegL < sizeof(aiListL)/sizeof(int))   iDataL = aiListL[iRegL];
	      iDataM = 0;
	      if (iRegM >= 0 && iRegM > sizeof(aiListM)/sizeof(int))   iDataM = aiListM[iRegM];
	      fDataN = 0.0;
	      if (iRegN >= 0 || iRegN > sizeof(afListN)/sizeof(float)) fDataN = afListN[iRegN];
         
         fprintf(stderr, "Regitser: %d-%d-%d, %d-%d-%.1f\n", 
                 iRegL, iRegM, iRegN, iDataL, iDataM, fDataN);
	      
	      fMem = 3.6864 * iDataL;
	      fRun = fMem * iDataM;
	      fTurbo = fRun * fDataN;
	      fPXBus = fRun / 2.0;
         fprintf(stderr, "Freqyence: Memory %.4f Run %.4f Turbo %.4f PXBus %.4f\n", 
                 fMem, fRun, fTurbo, fPXBus);
      }
   }   

   load_config_vol(sConfig);
   
   struct Config_Tag step_config[] = 
   {
      {"BatteryWarn",   Int_Tag,  &g_iBatteryWarn },
      {"overprotect",   Long_Tag, &g_lOverProtect },
      {"bSpeedStep",    Bool_Tag, &g_bSpeedStep },
      {"StepDelay",     Int_Tag,  &g_iStepDelay },
      {"acUsageHigher", Int_Tag,  &g_ac_step.iUsgHigher },
      {"acUsageLower",  Int_Tag,  &g_ac_step.iUsgLower },
      {"acMaxFreq",     Int_Tag,  &g_ac_step.iMaxFreq },
      {"acMinFreq",     Int_Tag,  &g_ac_step.iMinFreq },
      {"baUsageHigher", Int_Tag,  &g_ba_step.iUsgHigher },
      {"baUsageLower",  Int_Tag,  &g_ba_step.iUsgLower },
      {"baMaxFreq",     Int_Tag,  &g_ba_step.iMaxFreq },
      {"baMinFreq",     Int_Tag,  &g_ba_step.iMinFreq },
      {NULL,            Error_Tag,NULL }
   };

   if (get_config(sConfig, g_freq.sCPU, step_config) <= 0)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to config STEP.\n");
      exit(1);
   }
   
   g_lOverProtect = (g_lOverProtect * 2) + (g_lOverProtect / 2);
   DBG("OverProtect %lu\n", g_lOverProtect);
   load_disp(sConfig, "PaintCpu", &g_dispcpu);
   load_disp(sConfig, "PaintMem", &g_dispmem);
   load_disp(sConfig, "PaintFreq", &g_dispfreq);
   load_disp(sConfig, "PaintBattery", &g_dispba);
   load_ba_ext(sConfig, "PaintBattery");
   
   if (bTestOnly == True)
   {
      fprintf(stderr, "mb-applet-powerctrl: end of test.\n");
      exit(1);
   }

   return;
}

int volume_set(int iValue)
{
   int fd;
   char sDevMixer[] = "/dev/mixer";

   if ((fd = open(sDevMixer, O_RDWR)) < 0) 
   {
      fprintf(stderr, "mb-applet-powerctrl: Cant open mixer device %s.\n", sDevMixer);
      return -1;
   }
  
   if (iValue>100) iValue = 100;
   if (iValue<0)   iValue = 0;
  
   int new_vol;
   new_vol = iValue | (iValue << 8);
   if (ioctl(fd, SOUND_MIXER_WRITE_VOLUME, &new_vol) == -1)
   {
      fprintf(stderr, "mb-applet-powerctrl: unable to set volume\n");
      close(fd);
      return -1;
   }
   
   close(fd);
   g_vol.iVolume = iValue;
   return g_vol.iVolume;
}

void volume_menuset(MBMenuItem *item)
{
   int iVolume = atoi(item->info);
   DBG("menu set vol to %d\n", iVolume);
  
   volume_set(iVolume);
}

int volume_get(void)
{
   int fd;
   char sDevMixer[] = "/dev/mixer";

   if ((fd = open(sDevMixer, O_RDWR)) < 0) 
   {
      fprintf(stderr, "mb-applet-powerctrl: Cant open mixer device %s.\n", sDevMixer);
      return -1;
   }
  
   int orig_vol;

   if (ioctl(fd, SOUND_MIXER_READ_VOLUME, &orig_vol) == -1)
   {
      fprintf(stderr, "mb-applet-powerctrl: unable to read volume\n");
      close(fd);
      return -1;
   }

   close(fd);
   g_vol.iVolume = ( (orig_vol & 0xff) + ((orig_vol >> 8) & 0xff) ) / 2;
   return g_vol.iVolume;
}

int freq_get(void)
{
   if (g_freq.sPathCCCR[0] == 0)
      return -1;

   FILE *f;
   if ( (f = fopen(g_freq.sPathCCCR, "r")) == NULL)
      return -1;

   char sLine[255] = {0};
   if (fgets(sLine, sizeof(sLine), f) == NULL)
      return -1;
   fclose(f);

   // DBG("cur CCCR %s\n", sLine);
   int iVal;
   sscanf(sLine," %*2c%x", &iVal);
   DBG("CCCR val 0x%X\n", iVal);

   int iIdx = 0;
   for (iIdx = 0; iIdx < MAXFREQNUM; iIdx++)
   {
     if (g_freq.aiVal[iIdx][FREQ_NUM] <= 0)
        break;

     if (g_freq.aiVal[iIdx][FREQ_CCCR] == iVal)
     {
        // DBG("cur freq %d Mhz\n", g_freq.aiVal[iIdx][FREQ_NUM]);
        g_freq.iCurIdx = iIdx;
        break;
     }
   }
   return g_freq.iCurIdx;
}

int freq_set(int idx)
{
   if (idx < 0) return -1;
   if (idx > g_freq.iMaxIdx) return -1;
   if (g_freq.aiVal[idx][FREQ_NUM] <= 0 || g_freq.aiVal[idx][FREQ_CCCR] == 0) return -1;

   if (idx == g_freq.iCurIdx) return -1;

   DBG("set freq to %d\n", idx);
   FILE *f;
   if (g_freq.sPathVCORE[0] != 0 && g_freq.aiVal[idx][FREQ_VCORE] != 0)
   {
      if ( (f = fopen(g_freq.sPathVCORE, "w")) == NULL)
      {
         fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", g_freq.sPathVCORE);
         return -1;
      }

      fprintf(f, "%02x", g_freq.aiVal[idx][FREQ_VCORE]);
      fclose(f);
      DBG("write [%02x] to %s\n", g_freq.aiVal[idx][FREQ_VCORE], g_freq.sPathVCORE);
   }

   if (g_freq.sPathCCCR[0] != 0 && g_freq.aiVal[idx][FREQ_CCCR] != 0)
   {
      if ( (f = fopen(g_freq.sPathCCCR, "w")) == NULL)
      {
         fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", g_freq.sPathCCCR);
         return -1;
      }

      fprintf(f, "%x", g_freq.aiVal[idx][FREQ_CCCR]);
      fclose(f);
      DBG("write [%x] to %s\n", g_freq.aiVal[idx][FREQ_CCCR], g_freq.sPathCCCR);
   }
   g_freq.iCurIdx = idx;
   return 0;
}

int freq_stepset(int iFreqNum)
{
   if (iFreqNum < 0) return -1;
   if (iFreqNum > g_freq.iMaxIdx) return -1;

   int iIdx;
   if (iFreqNum > g_freq.iCurIdx)
   {  // step higher  
      for (iIdx = g_freq.iCurIdx + 1; iIdx <= iFreqNum; iIdx++)
      {
         if (freq_set(iIdx) < 0)
         {
            break;
         }
         usleep(50000L);
      }
   } else { 
      // step lower
      for (iIdx = g_freq.iCurIdx - 1; iIdx >= iFreqNum; iIdx--)
      {
         if (freq_set(iIdx) < 0)
         {
            break;
         }
         usleep(50000L);
      }
   }
   return 0;
}

void freq_menuset(MBMenuItem *item)
{
   if (item->info[0] == 'A')
   {
      if (g_bSpeedStep == True)
      {
         g_bSpeedStep = False;
         freq_stepset(g_freq.iDefaultIdx);
      } else {
         g_bSpeedStep = True;
      }
      return;
   }
   
   g_bSpeedStep = False;
   int iFreqNum = atoi(item->info);
   DBG("menu set freq to %d\n", iFreqNum);
  
   if (g_bProtected && (iFreqNum > g_freq.iDefaultIdx) )   iFreqNum = g_freq.iDefaultIdx;

   freq_stepset(iFreqNum);
}

/* returns current CPU load in percent, 0 to 100 */
int system_cpu(void)
{
   unsigned int cpuload;
   u_int64_t load, total, oload, ototal;
   u_int64_t ab, ac, ad, ae;
   u_int64_t kiload, kiu, kis;
   int i;
   FILE *stat;
 
   if ((stat = fopen(kistat, "r")) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to open %s. Exiting\n", kistat);
      kiu = kis = 0;
   } else {
      fscanf(stat, "%*d %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %Ld %Ld", &kiu, &kis);
      fclose(stat);
   }
   /* Find out the CPU load of the kapm-idled process */
   /* user + sys = load */
   kiload = kiu + kis;
 
   if ((stat = fopen("/proc/stat", "r")) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to open /proc/stat. Exiting\n");
      exit(1);
   }
 
   fscanf(stat, "%*s %Ld %Ld %Ld %Ld", &ab, &ac, &ad, &ae);
   fclose(stat);
 
   /* Find out the CPU load */
   /* user + sys - idle = load */
   /* total = total */
   load = ab + ac + ad - kiload;       /* cpu.user + cpu.sys; */
   total = ab + ac + ad + ae;  /* cpu.total; */
 
   /* "i" is an index into a load history */
   i = msd.loadIndex;
   oload = msd.load[i];
   ototal = msd.total[i];
 
   msd.load[i] = load;
   msd.total[i] = total;
   msd.loadIndex = (i + 1) % msd.samples;
 
   cpuload = 0;
   if (ototal != 0) cpuload = (100 * (load - oload)) / (total - ototal);
   
   return cpuload;
}

int memory_read(void)
{
   u_int64_t my_mem_used, my_mem_max;
   u_int64_t my_swap_used, my_swap_max;
 
   FILE *mem;
   static u_int64_t total, used, mfree, shared, buffers;
   static u_int64_t cached; //, cache_total, cache_used;
   static u_int64_t swap_total, swap_used;
 
   /* put this in permanent storage instead of stack */
   static char not_needed[2048];
 
   if ((mem = fopen("/proc/meminfo", "r")) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to open /proc/meminfo. Exiting.\n");
      exit(1);
   }
 
   fgets(not_needed, 2048, mem);
   /* total:    used:    free:  shared: buffers:  cached: */
   fscanf(mem, "%*s %Ld %Ld %Ld %Ld %Ld %Ld", &total, &used, &mfree,
          &shared, &buffers, &cached);
   //fscanf(mem, "%*s %Ld %Ld", &cache_total, &cache_used);
   fscanf(mem, "%*s %Ld %Ld", &swap_total, &swap_used);
   fclose(mem);
 
   /* calculate it */
   my_mem_max = total;
   my_swap_max = swap_total;
 
   //my_mem_used = cache_used + used - cached - buffers;
   my_mem_used = used;
   my_swap_used = swap_used;
 
   /* No swap on ipaq
   if (my_mem_used > my_mem_max)
   {
      my_swap_used = my_mem_used - my_mem_max;
      my_mem_used = my_mem_max;
   } else {
      my_swap_used = 0;
   }
   */
   msd.mem_used = my_mem_used;
   msd.mem_max = my_mem_max;
   msd.swap_used = my_swap_used;
   msd.swap_max = my_swap_max;
 
   msd.mem_percent = (100 * msd.mem_used) / msd.mem_max;
   
   if (msd.swap_max <= msd.swap_used)
      msd.swap_percent = 0;
   else
      msd.swap_percent = (100 * msd.swap_used) / msd.swap_max;
 
   /* memory info changed - update things */
   return 1;
}

int memory_read_delay(void)
{
   static int mem_delay = 0;
   if (mem_delay-- >  0)
      return 0;  /* nothing new */

   // update in 25 * 400ms = 10s
   mem_delay = 25;
   
   return memory_read();
}

/* catch possible segfualt
void catch_sigsegv(int sig)
{
   // signal(SIGSEGV, catch_sigsegv);
   // if (setjmp(Jbuf)) return 0;  
   DBG("ouch\n");
   signal(SIGSEGV, SIG_DFL);
   longjmp(Jbuf, 1);
}
*/

void cleanup_handler(int signo)
{
    DBG("Sign term\n");
    freq_stepset(g_freq.iDefaultIdx);
    exit(0);
}

void reap_children(int signum)
{
   pid_t pid;

   do
   {
     pid = waitpid(-1, NULL, WNOHANG);
   } while (pid > 0);
}

#ifdef USE_LIBSN

void sn_exec(char* name, char* bin_name, char *desc)
{
   SnLauncherContext *context;
   pid_t child_pid = 0;
  
   context = sn_launcher_context_new (g_app_data.sn_display,
                                      mb_tray_app_xscreen(g_app_data.tray_app));
  
   if (name)     sn_launcher_context_set_name (context, name);
   if (desc)     sn_launcher_context_set_description (context, desc);
   if (bin_name) sn_launcher_context_set_binary_name (context, bin_name);
  
   sn_launcher_context_initiate (context, "mbmenu launch", bin_name,
                                 CurrentTime);
  
   switch ((child_pid = fork ()))
   {
   case -1:
      fprintf (stderr, "Fork failed\n" );
      break;
   case 0:
      sn_launcher_context_setup_child_process (context);
      mb_exec(bin_name);
      fprintf (stderr, "mbmenu: Failed to exec %s \n", bin_name);
      _exit (1);
      break;
   }
}

#endif

void fork_exec(char *cmd)
{
   pid_t pid;
  
   switch (pid = fork())
   {
   case 0:
      mb_exec(cmd);
      fprintf(stderr, "mbmenu: exec failed, cleaning up child\n");
      exit(1);
   case -1:
      fprintf(stderr, "mbmenu: can't fork\n"); break;
   }
}

Bool read_apm()
{
   apm_info info;
   apm_read(&info);
    /*
   while (!read_apm())
     usleep(50000L);
    */
  
   g_apm_vals[TIME_LEFT] = info.battery_time;
   g_apm_vals[PERCENTAGE] = info.battery_percentage;
   g_apm_vals[AC_POWER] = info.ac_line_status;
  
   if (g_apm_vals[AC_POWER] != AC_LINE_STATUS_ON)
   {
      return True;
   }
   
   char sPath[] = "/proc/driver/battery/sharpsl_main_battery_voltage";
   FILE *f;
   // grab the battery voltage
   // sharpsl_main_battery_voltage measurement is volts*51
   f = fopen(sPath,"r");
   if (f == NULL)
   {
      DBG("Failed to open %s\n", sPath);
      return False;
   }

   char rawvoltage[10] = {0};
   if(fgets(rawvoltage, sizeof(rawvoltage), f) == NULL)
   {
      DBG("Unable to read from %s\n", sPath);
      fclose(f);
   }
   
   long voltage = strtol(rawvoltage, NULL, 0);
   // quick sanity check on voltage
   // ~190 min/~221 max on AC
   if (voltage > 0 && voltage < 250)
   {
      // simple linear approximation:
      // 100% defined at 221, 5% defined at 191
      g_apm_vals[PERCENTAGE] = (voltage*95 - 17995) / 30;
   }
   // when battery, just use apm's percentage
   // ~143 min/~212 max on battery
   fclose(f);
   return True;
}

Bool read_apm_delay()
{
   /* add stat function here ? */
   static int apm_delay = 0;
   if (apm_delay-- >  0)
      return True;  /* nothing new */
   
   // update in 5 * 400ms = 2s
   apm_delay = 25;
   return read_apm();
}

Bool is_ac_power()
{
   read_apm();
   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
      return True;
    else
      return False;
  
}

void lnp_config_read()
{
   FILE *config;
   char sPath[MAXPATHLEN];
   char line[255];
   float screensaver_mins, suspend_mins;
  
   strcpy(sPath, getenv("HOME"));
   strcat(sPath, "/Choices/lightnpower.cfg");
   if ( (config = fopen(sPath, "r")) == NULL )
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", sPath);
      return;
   }
   // DBG("%s opened\n", sPath);
  
   /* Skip the first three lines */
   fgets(line,sizeof(line),config);
   fgets(line,sizeof(line),config);
   fgets(line,sizeof(line),config);
  
   /* Skip the AC line if we're on battery power */
   if (fgets(line,sizeof(line),config) == NULL)
   {
      lnp.ac_backlight = 1;
      lnp.ac_screensaver = 0;
      lnp.ac_suspend = 0;
   } else {
      sscanf(line," %*25c%d%*c %*11c%f%*c %*9c%f",
             &lnp.ac_backlight, &screensaver_mins, &suspend_mins);
      lnp.ac_screensaver = screensaver_mins * 60;
      lnp.ac_suspend = suspend_mins * 60;
   }
   if (lnp.ac_backlight <= 0)  lnp.ac_backlight = 1;
   DBG("lnp AC Power %d %d %d\n", lnp.ac_backlight, lnp.ac_screensaver, lnp.ac_suspend);
  
   if (fgets(line,sizeof(line),config) == NULL)
   {
      lnp.ba_backlight = 1;
      lnp.ba_screensaver = 0;
      lnp.ba_suspend = 0;
   } else {
      sscanf(line," %*25c%d%*c %*11c%f%*c %*9c%f", &lnp.ba_backlight, &screensaver_mins, &suspend_mins);
      lnp.ba_screensaver = screensaver_mins * 60;
      lnp.ba_suspend = suspend_mins * 60;
   }
   if (lnp.ba_backlight <= 0)  lnp.ba_backlight = 1;
  
   fclose(config);
   DBG("lnp battery %d %d %d\n", lnp.ba_backlight, lnp.ba_screensaver, lnp.ba_suspend);

   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
   {
      if (lnp.ac_screensaver <= 0) g_bScreensaver = False;
      if (lnp.ac_suspend <= 0) g_bSuspend = False;
   } else {
      if (lnp.ba_screensaver <= 0) g_bScreensaver = False;
      if (lnp.ba_suspend <= 0) g_bSuspend = False;
   }
}

void lnp_config_write()
{
   FILE *config;
   char sPath[MAXPATHLEN];
  
   strcpy(sPath, getenv("HOME"));
   strcat(sPath, "/Choices/lightnpower.cfg");
   if ( (config = fopen(sPath, "w")) == NULL )
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", sPath);
      return;
   }
   DBG("write config to %s\n", sPath);
  
   fprintf(config, "<?xml version=\"1.0\" ?>\n");
   fprintf(config, "<!--pdaXrom settings file-->\n");
   fprintf(config, "<POWERSETTINGS>\n");
  
   float screensaver_mins, suspend_mins;
   screensaver_mins = lnp.ac_screensaver / 60;
   suspend_mins = lnp.ac_suspend / 60;
   fprintf(config,
           "\t<Powersetting backlight=\"%d\" screenoff=\"%.1f\" suspend=\"%.1f\" type=\"AC\"/>\n",
           lnp.ac_backlight, screensaver_mins, suspend_mins);
   screensaver_mins = lnp.ba_screensaver / 60;
   suspend_mins = lnp.ba_suspend / 60;
   fprintf(config,
           "\t<Powersetting backlight=\"%d\" screenoff=\"%.1f\" suspend=\"%.1f\" type=\"BATTERY\"/>\n",
           lnp.ba_backlight, screensaver_mins, suspend_mins);
   fprintf(config, "</POWERSETTINGS>\n");
  
   fclose(config);
}

void dpms_read()
{
   char sCmd[] = "/usr/X11R6/bin/xset q";
   FILE *info;
   if ( (info = popen(sCmd, "r")) == NULL )
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to exec %s.\n", sCmd);
      return;
   } 

   Bool bFind = False;
   char sLine[255];
   char sHdr[] = "DPMS";
   while (fgets(sLine, sizeof(sLine), info) != NULL)
   {
      trim(sLine);
      // DBG("Read %s", sLine);
      if ( memcmp( sLine, sHdr, sizeof(sHdr) - 1 ) == 0 )
      {
         bFind = True;
         break;
      }
   }

   if ( bFind != True ) 
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to find %s.\n", sHdr);
      fclose(info);
      return;
   }
   
   if (fgets(sLine, sizeof(sLine), info) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to get DPMS para.\n");
      fclose(info);
      return;
   }
   trim(sLine);
   //DBG("Read %s", sLine);
   
   int screen_timeout = 0;
   int suspend_timeout = 0;
   int off_timeout = 0;
   sscanf(sLine,"%*s %d %*s %d %*s %d",
          &screen_timeout, &suspend_timeout, &off_timeout);

   DBG("DMPS: Screen saver %d Suspend %d Off %d\n", 
       screen_timeout, suspend_timeout, off_timeout);

   if (fgets(sLine, sizeof(sLine), info) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to get DPMS status.\n");
      fclose(info);
      return;
   }
   trim(sLine);
   //DBG("Read %s", sLine);
   
   Bool bDPMS = True;
   if (strstr(sLine, "Disabled") != NULL)
   {
      bDPMS = False;
   }
   DBG("DPMS: %s\n", (bDPMS == True) ? "On" : "Off");

   pclose(info);
   
   g_bSuspend = bDPMS;
   g_bScreensaver = (screen_timeout > 0) ? True : False;
}

void dpms_set()
{
   int screen_timeout = 0;
   int suspend_timeout = 0;
  
   // Only read the config file if we are going to use a value from it
   if (g_bScreensaver || g_bSuspend)
   {
      lnp_config_read();
  
      if (is_ac_power() == True)
      {
         screen_timeout = lnp.ac_screensaver;
         suspend_timeout = lnp.ac_suspend;
      } else {
         screen_timeout = lnp.ba_screensaver;
         suspend_timeout = lnp.ba_suspend;
      }
   }
  
   if ( !g_bScreensaver )
   {
      screen_timeout = 0;
   }
   if ( !g_bSuspend )
   {
      suspend_timeout = 0;
   }
  
   char cmd[255];
   FILE *fp;
   fp = fopen("/usr/X11R6/bin/xset-wrapper","r");
   if (fp) 
   {
      fclose(fp);
      snprintf(cmd, sizeof(cmd), "/usr/X11R6/bin/xset-wrapper dpms %d 0 %d", screen_timeout, suspend_timeout);
      DBG("Exec [%s]\n", cmd);
      fork_exec(cmd);
      return;
   } 
   
   snprintf(cmd, sizeof(cmd), "/usr/X11R6/bin/xset dpms %d 0 %d", screen_timeout, suspend_timeout);
   DBG("Exec [%s]\n", cmd);
   fork_exec(cmd);
   
   // normally, xset will enable dpms, but it may have bug
   if (!g_bSuspend)
      snprintf(cmd, sizeof(cmd), "/usr/X11R6/bin/xset -dpms");
   else
      snprintf(cmd, sizeof(cmd), "/usr/X11R6/bin/xset +dpms");
   DBG("Exec [%s]\n", cmd);
   fork_exec(cmd);
}

void rect_expand(RECT *pDest, RECT *pSrc, int iExpand)
{
   pDest->x = pSrc->x - iExpand;
   pDest->y = pSrc->y - iExpand;
   pDest->w = pSrc->w + 2 * iExpand;
   pDest->h = pSrc->h + 2 * iExpand;
}

void resize_cb (MBTrayApp *app, int w, int h)
{
   if (g_img.pScaled) mb_pixbuf_img_free(g_app_data.pb, g_img.pScaled);
  
   g_img.pScaled = mb_pixbuf_img_scale(g_app_data.pb,
                                       g_img.pOrig,
                                       w, h);
  
   g_img.scaled_w = mb_pixbuf_img_get_width(g_img.pScaled);
   g_img.scaled_h = mb_pixbuf_img_get_height(g_img.pScaled);
  
   g_img.rcCpu.x = (g_img.scaled_w * g_dispcpu.rc.x) / 32;
   g_img.rcCpu.y = (g_img.scaled_h * g_dispcpu.rc.y) /32;
   g_img.rcCpu.w = (g_img.scaled_w * g_dispcpu.rc.w) / 32;
   g_img.rcCpu.h = (g_img.scaled_h * g_dispcpu.rc.h) /32;
   rect_expand(&g_img.rcCpuOut, &g_img.rcCpu, g_dispcpu.iOutline);
  
   g_img.rcMem.x = (g_img.scaled_w * g_dispmem.rc.x) / 32;
   g_img.rcMem.y = (g_img.scaled_h * g_dispmem.rc.y) /32;
   g_img.rcMem.w = (g_img.scaled_w * g_dispmem.rc.w) / 32;
   g_img.rcMem.h = (g_img.scaled_h * g_dispmem.rc.h) /32;
   rect_expand(&g_img.rcMemOut, &g_img.rcMem, g_dispmem.iOutline);
  
   g_img.rcBattery.x = (g_img.scaled_w * g_dispba.rc.x) / 32;
   g_img.rcBattery.y = (g_img.scaled_h * g_dispba.rc.y) /32;
   g_img.rcBattery.w = (g_img.scaled_w * g_dispba.rc.w) / 32;
   g_img.rcBattery.h = (g_img.scaled_h * g_dispba.rc.h) /32;
   rect_expand(&g_img.rcBatteryOut, &g_img.rcBattery, g_dispba.iOutline);

   g_img.rcFreq.x = (g_img.scaled_w * g_dispfreq.rc.x) / 32;
   g_img.rcFreq.y = (g_img.scaled_h * g_dispfreq.rc.y) /32;
   g_img.rcFreq.w = (g_img.scaled_w * g_dispfreq.rc.w) / 32;
   g_img.rcFreq.h = (g_img.scaled_h * g_dispfreq.rc.h) /32;
   g_img.freq_gap = (g_img.scaled_w * g_dispfreq.iOutline) /32;
   /* pDraw can not be init here, just release */
   if (g_img.pDraw) mb_pixbuf_img_free(g_app_data.pb, g_img.pDraw);
}

/*
static
void reload_icons()
{
  load_icons();
  resize_cb (g_app_data.tray_app, mb_tray_app_width(g_app_data.tray_app),
                   mb_tray_app_width(g_app_data.tray_app) );
  mb_tray_app_repaint (g_app_data.tray_app);
}
*/

void screensaver_toggle_cb(MBMenuItem *item)
{
   g_bScreensaver = g_bScreensaver ? False : True;
   dpms_set();
   //reload_icons();
}

void suspend_toggle_cb(MBMenuItem *item)
{
   g_bSuspend = g_bSuspend ? False : True;
   dpms_set();
   //reload_icons();
}

Bool backlight_set(int iBackLight)
{
   if (iBackLight <= 0 || iBackLight > MAXBLNUM) return False;
   if (iBackLight == backlight_now) return True;

   char cmd[255];
   snprintf(cmd, sizeof(cmd), "/sbin/setfl %d", iBackLight);
   fork_exec(cmd);

   backlight_now = iBackLight;
   return True;
}

void backlight_menuset(MBMenuItem *item)
{
   int iBackLight = atoi(item->info);

   if (backlight_set(iBackLight) != True)  return;

   if (is_ac_power() == True)
   {
      lnp.ac_backlight = iBackLight;
   } else {
      lnp.ba_backlight = iBackLight;
   }
   lnp_config_write();
}

int backlight_chk()
{
   FILE *bl;
   char sPath[MAXPATHLEN] = "/proc/driver/fl/corgi-bl";
   char sLine[255];
   int iLevel = 0;
  
   if ((bl = fopen(sPath, "r")) == NULL)
   {
      fprintf(stderr, "mb-applet-powerctrl: failed to open %s.\n", sPath);
      return iLevel;
   }
  
   if (fgets(sLine,sizeof(sLine),bl) == NULL)
   {
      fclose(bl);
      return iLevel;
   }
   fclose(bl);
   DBG("Backlight string %s\n", sLine);
  
   int iVal;
   sscanf(sLine," %*2c%x", &iVal);
   DBG("Backlight val 0x%X\n", iVal);
  
   int iIdx = 0;
   for (iIdx = 0; iIdx < MAXBLNUM; iIdx++)
   {
      if (g_backlight[iIdx] == iVal)
      {
         iLevel = iIdx + 1;
         break;
      }
   }
   return iLevel;
}

void backlight_get()
{
   lnp_config_read();
  
   int iLevel = backlight_chk();
   if (iLevel <= 0)
   {
      if (is_ac_power() == True)
         backlight_now = lnp.ac_backlight;
      else
         backlight_now = lnp.ba_backlight;
   } else {
      backlight_now = iLevel;
   }
   DBG("Backlight level %d\n", backlight_now);
}

void backlight_off()
{
   if (!g_bScreenBlank)
      backlight_get();

   fork_exec("/sbin/setfl 0");

   g_bScreenBlank = True;
}

void backlight_on()
{
   char cmd[255];
  
   lnp_config_read();
   if (backlight_now > 0)
   {
      snprintf(cmd, sizeof(cmd), "/sbin/setfl %d", backlight_now);
   } else {
      if (is_ac_power() == True)
      {
         snprintf(cmd, sizeof(cmd), "/sbin/setfl %d", lnp.ac_backlight);
      } else {
         snprintf(cmd, sizeof(cmd), "/sbin/setfl %d", lnp.ba_backlight);
      }
   }
  
   fork_exec(cmd);
  
   g_bScreenBlank = False;
}

void backlight_timeout_cb ( MBTrayApp *app )
{
   if(g_bScreenBlank)
      backlight_off();
}

void mute_cb(MBMenuItem *item)
{
   if(g_bMute==False)
   {
     volumeMuted=volume_get();
     volume_set(0);
     g_bMute=True;
   }
   else
   {
     volume_set(volumeMuted);
     g_bMute=False;
   }
}

void blanker_cb(MBMenuItem *item)
{
   backlight_off();
  
   // Hide the mouse cursor
   XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, 640, 480);
  
   backlight_timer = gtk_timeout_add (1000, // 1 second
                                 (GSourceFunc) backlight_timeout_cb,
                                 g_app_data.tray_app);
  
   gtk_widget_show_all (wnd_blanker);
   gdk_pointer_grab (wnd_blanker->window, True, GDK_BUTTON_PRESS_MASK, NULL, NULL, CurrentTime);
   gdk_keyboard_grab (wnd_blanker->window, True, CurrentTime);
   PopupIsMapped = True;
}

void swap_menuset(MBMenuItem *item)
{
   /*
   char cmd[255];
   if (msd.swap_max > 0l)
   {
      snprintf(cmd, sizeof(cmd), "/sbin/swapoff -a");
   } else {
      snprintf(cmd, sizeof(cmd), "/sbin/swapon -a");
   }   
   DBG("Exec [%s]\n", cmd);
   fork_exec(cmd);
   */
   int errno;
   if (msd.swap_max > 0l)
   {
      errno = system("sudo /sbin/swapoff -a");
   } else {
      errno = system("sudo /sbin/swapon -a");
   }
}

void card_check(void)
{
   FILE *fp;
   char sLine[256];

   g_cards.iCardSD = 0;
   g_cards.iUsbStorage = 0;
   
   fp = fopen("/etc/mtab", "r");
   while (fgets(sLine, sizeof(sLine), fp) != NULL)
   {
      // DBG("Read %s", sLine);
	   if (strncmp(sLine, "/dev/mmcd", 9) == 0) g_cards.iCardSD = 1;
	   if (strncmp(sLine, "/dev/mmcb", 9) == 0) g_cards.iCardSD = 1;
	   if (strncmp(sLine, "/dev/sd", 7) == 0) g_cards.iUsbStorage = 1;
   }
   fclose(fp);
   
   char *pToken;
   if (g_cards.iCardSD == 1)
   {
      strcpy(g_cards.sNameSD, "SD card");
      fp = fopen("/var/lib/sdcard/stab", "r");
      if(fp) 
      {
         fgets(sLine, sizeof(sLine), fp);
         fclose(fp);
         pToken = strtok(sLine, "\n\r");
         if (strlen(pToken) > 10)
         {
            strncpy(g_cards.sNameSD, &pToken[10], sizeof(g_cards.sNameSD));
            g_cards.sNameSD[sizeof(g_cards.sNameSD) - 1] = 0;
         }
      } 
   }

   g_cards.iCardCF = 1;
   fp = fopen("/var/lib/pcmcia/stab", "r");
   if(fp) 
   {
      fgets(sLine, sizeof(sLine), fp);
      fclose(fp);
      pToken = strtok(sLine, "\n\r");
      if (strlen(pToken) <= 10)
      {
         g_cards.iCardCF = 0;
      } else {
         if (strncmp(pToken, "Socket 0: empty", 15 ) == 0) 
         {
            g_cards.iCardCF = 0;
         } else {
            strncpy(g_cards.sNameCF, &pToken[10], sizeof(g_cards.sNameCF));
            g_cards.sNameCF[sizeof(g_cards.sNameCF) - 1] = 0;
         }
      }   
      return;
   }
   
   Bool kernelBug =False;
   Bool modelwMD = False;

   fp = fopen("/etc/hardware", "r");
   if(fp) 
   {
      fgets(sLine, sizeof(sLine), fp);
      fclose(fp);
      if ((strncmp(sLine, "Spitz",4 ) == 0) ||
          (strncmp(sLine, "Borzoi",5 ) == 0) ||
          (strncmp(sLine, "Terrier",6 ) == 0))
      {    
         modelwMD = True;
      }   
	}

   fp = fopen("/etc/issue", "r");
   fgets(sLine, sizeof(sLine), fp);
   fclose(fp);
   if (strncmp(sLine, "pdaXrom 1.1.0r1",14 ) == 0)
      kernelBug=True;
   
   if (kernelBug && modelwMD) 
      fp = fopen("/sys/bus/pcmcia/devices/1.0/prod_id1","r");
   else
      fp = fopen("/sys/bus/pcmcia/devices/0.0/prod_id1","r");
   if (fp) 
   {
	   fclose(fp);
   } else {
      g_cards.iCardCF = 0;
   }
}

void card_cf_cb( MBMenuItem *item )
{
   int err;
   FILE *fp;
   char sLine[256];
   Bool kernelBug = False;
   Bool modelwMD = False;

   fp = fopen("/etc/hardware","r");
   if (fp) 
   {
      fgets(sLine, sizeof(sLine), fp);
      fclose(fp);
      if ((strncmp(sLine, "Spitz",4 ) == 0) ||
          (strncmp(sLine, "Borzoi",5 ) == 0) ||
          (strncmp(sLine, "Terrier",6 ) == 0))
      {
         modelwMD=True;
      }
	}

   fp = fopen("/etc/issue", "r");
   fgets(sLine, sizeof(sLine), fp);
   fclose(fp);
   if (strncmp(sLine, "pdaXrom 1.1.0r1",14 ) == 0)
      kernelBug=True;
    
   if (g_cards.iCardCF == 1) 
   {  // eject card
	  fp = fopen("/sbin/cardctl","r");
	  if(fp) 
	  {
        fclose(fp);
        err = system("sudo cardctl eject 0");
	  } else {
	     if(kernelBug&&modelwMD)
           err = system("sudo pccardctl eject 1");
	     else
           err = system("sudo pccardctl eject 0");
	  }
	  if (err != 0) 
	  {
        mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                      _("CF/PCMCIA card eject failed!"),
                                      0);
	  } else {
        mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                      _("You can remove the CF card now."),
                                      0);
	  }
	  return;
   }
   
	// insert card
	fp = fopen("/sbin/cardctl","r");
	if (fp) 
	{
      fclose(fp);
      err = system("sudo cardctl insert 0");
	} else {
	   if (kernelBug&&modelwMD)
         err = system("sudo pccardctl insert 1");
	   else
         err = system("sudo pccardctl insert 0");
	}
	if (err != 0) 
	{
      mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                    _("CF/PCMCIA card insert failed!"),
                                    0);
   }
}

void card_sd_cb( MBMenuItem *item )
{
   FILE *fp;
   int err;
   Bool bUseControl = False;

   fp = fopen("/etc/sdcontrol","r");
   if (fp) 
   {
	   fclose(fp);
	   bUseControl=True;
   }

   if (g_cards.iCardSD == 1) 
   {
	   if (bUseControl)
	      err = system("sudo /etc/sdcontrol eject");
	   else
	      err = system("sudo umount /mnt/card");
	   if (err != 0) 
	   {
         mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                       _("SD/MMC card eject failed!"),
                                       0);
	   } else {
         mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                       _("You can remove the SD/MMC card now."),
                                       0);
	   }
	   return;
   }
   
   if (bUseControl)
	   err = system("sudo /etc/sdcontrol insert");
	else
	   err = system("sudo mount /mnt/card");
	if (err != 0) 
	{
      mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                    _("SD/MMC card insert failed!"),
                                    0);
   }
}

void card_usbstorage_cb( MBMenuItem *item )
{
   FILE *fp;
   int err;
   Bool bUseControl = False;

   fp = fopen("/etc/hotplug/usb/usb-storage.off","r");
   if (fp) 
   {
	   fclose(fp);
	   bUseControl=True;
   }
    
   if (g_cards.iUsbStorage == 1) 
   {
	   if (bUseControl)
         err = system("sudo /etc/hotplug/usb/usb-storage.off");
	   else
         err = system("sudo umount /mnt/usbstorage");
	   if (err != 0) 
	   {
         mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                       _("USB storage eject failed!"),
                                       0);
	   } else {
         mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                       _("You can remove the USB storage device now."),
                                       0);
	   }
	   return;
   }

	if (bUseControl)
	    err = system("sudo /etc/hotplug/usb/usb-storage");
	else
	    err = system("sudo mount /dev/sda1 /mnt/usbstorage");
	if (err != 0) 
	{
       mb_tray_app_tray_send_message(g_app_data.tray_app, 
                                     _("USB storage mount failed!"),
                                     0);
	}
}

void menu_build_volume(MBMenu *pMenu, MBMenuMenu *pmSubMenu)
{
   volume_get();

   char sImage[MAXPATHLEN];
   char sItem[64];
   char sNum[64];
   Bool bMatch = False;
   int iIdx;
   
   for (iIdx = MAXVOLNUM - 1; iIdx >= 0; iIdx--)
   {
      if (g_vol.aiPre[iIdx] < 0)
         continue;
         
      if (g_vol.aiPre[iIdx] == g_vol.iVolume)
      {
         bMatch = True;
         strcpy(sImage, IMG_YES);
      } else {
         strcpy(sImage, IMG_NUL);
      }
      sprintf(sItem, _("%d%%"), g_vol.aiPre[iIdx]);
      sprintf(sNum, "%d", g_vol.aiPre[iIdx]);
      mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                               sItem,
                               sImage,
                               sNum,
                               volume_menuset,
                               NULL, MBMENU_NO_SORT);
   }
   if (bMatch)
      return;
   
   mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmSubMenu, MBMENU_NO_SORT);
         
   sprintf(sItem, _("%d%%"), g_vol.iVolume);
   sprintf(sNum, "%d", g_vol.iVolume);
   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            sItem,
                            IMG_YES,
                            sNum,
                            volume_menuset,
                            NULL, MBMENU_NO_SORT);
}

void menu_build_cards(MBMenu *pMenu, MBMenuMenu *pmSubMenu)
{
   card_check();

   char sImage[MAXPATHLEN];
   if (g_cards.iCardCF == 1) 
      strcpy(sImage, IMG_PREFIX "cf_mount.png");
   else   
      strcpy(sImage, IMG_PREFIX "cf_unmount.png");

   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            g_cards.sNameCF,
                            sImage,
                            "",
                            card_cf_cb,
                            NULL, MBMENU_NO_SORT);

   if (g_cards.iCardSD == 1) 
      strcpy(sImage, IMG_PREFIX "sd_mount.png");
   else   
      strcpy(sImage, IMG_PREFIX "sd_unmount.png");

   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            g_cards.sNameSD,
                            sImage,
                            "",
                            card_sd_cb,
                            NULL, MBMENU_NO_SORT);

   if (g_cards.iUsbStorage == 1)
      strcpy(sImage, IMG_PREFIX "usb_mount.png");
   else   
      strcpy(sImage, IMG_PREFIX "usb_unmount.png");

   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            _("USB Storage"),
                            sImage,
                            "",
                            card_usbstorage_cb,
                            NULL, MBMENU_NO_SORT);
}

void menu_build_usbhost(MBMenu *pMenu, MBMenuMenu *pmSubMenu)
{
   FILE *fp;
   fp = fopen("/proc/bus/usb/devices", "r");
   if(fp == NULL) 
   {
      return;
   }

   char *fptr, *tok, *next;
   char sLine[1024];
   do 
   {
      fptr = trim(fgets(sLine, sizeof(sLine), fp));  /* get input line */
      if ( fptr == NULL ) 
         break;

      tok = trim(strtok(sLine, "=\n\r"));   /* get first token */
      if ( tok == NULL ) 
         continue;
      
      if ( strcasecmp( tok, "S:  Product" ) != 0 )  /* not got a match? */ 
         continue;

      next = trim(strtok(NULL, "\n\r"));  /* get actual config information */
      if (next == NULL) 
         continue;
      
      mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                               next,
                               IMG_PREFIX "usb_mount.png",
                               "",
                               NULL, NULL, MBMENU_NO_SORT);
   } while ( fptr != NULL );
   fclose(fp);
}

#define USB_SERIAL  0
#define USB_NET     1
#define USB_STORAGE 2
void usbfunct_cb(MBMenuItem *item)
{
   FILE *fp;

   char sType[32];
   int iErrorNum;
   if (strcmp(item->info, "net") == 0)
   {
      iErrorNum = system("sudo rm -f /etc/hotplug/usbdstorage.conf");
      strcpy(sType, "net");
	} else if (strcmp(item->info, "serial") == 0)
	{
      strcpy(sType, "serial");
   } else {
      // storage
      fp = fopen("/etc/hotplug/usbdstorage.conf", "w");
      if(fp != NULL) 
      {
         fprintf(fp, "%s\n", item->info);
         fclose(fp);
         strcpy(sType, "storage");
      }
   }

   fp = fopen("/etc/hotplug/usbd.ftype", "w");
   if(fp != NULL) 
   {
      fprintf(fp, "%s\n", sType);
      fclose(fp);
   }

   char sCmd[1024];
   sprintf(sCmd, "sudo /etc/usbcontrol %s", sType);
   DBG("Exec: %s\n", sCmd);
   iErrorNum = system( sCmd );
}

void menu_build_usbfunct(MBMenu *pMenu, MBMenuMenu *pmSubMenu)
{
   int iUsbType = USB_SERIAL;

   char sLine[1024];
   FILE *fp;
   fp = fopen("/etc/hotplug/usbd.ftype", "r");
   if(fp != NULL) 
   {
      if (fgets(sLine, sizeof(sLine), fp) != NULL)
      {
         if (strncmp(sLine, "net", 3) == 0)
            iUsbType = USB_NET;
         else if (strncmp(sLine, "storage", 7) == 0)
            iUsbType = USB_STORAGE;
         else;   
      }
      fclose(fp);
   }

   char sImage[MAXPATHLEN];
   if (iUsbType == USB_SERIAL) 
      strcpy(sImage, IMG_YES);
   else   
      strcpy(sImage, IMG_NUL);

   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            _("Serial"),
                            sImage,
                            "serial",
                            usbfunct_cb, 
                            NULL, MBMENU_NO_SORT);

   mb_menu_add_seperator_to_menu(pMenu, pmSubMenu, MBMENU_NO_SORT);

   if (iUsbType == USB_NET) 
      strcpy(sImage, IMG_YES);
   else   
      strcpy(sImage, IMG_NUL);
   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            _("Network"),
                            sImage,
                            "net",
                            usbfunct_cb, 
                            NULL, MBMENU_NO_SORT);
   
   mb_menu_add_seperator_to_menu(pMenu, pmSubMenu, MBMENU_NO_SORT);

   mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                            _("Storage"),
                            sImage,
                            "storage",
                            usbfunct_cb, 
                            NULL, MBMENU_NO_SORT);
   
   char sCurDev[MAXPATHLEN] = {0};
   fp = fopen("/etc/hotplug/usbdstorage.conf", "r");
   if(fp != NULL) 
   {
      fgets(sCurDev, sizeof(sCurDev), fp);
      fclose(fp);
   }

   char sItem[1024];
   char *pDev, *pMnt, *pType;
   fp = fopen("/etc/mtab", "r");
   if(fp == NULL) 
   {
      return;
   }
   while (!feof(fp)) 
   {
	   fgets(sLine, sizeof(sLine), fp);
	   if (strncmp(sLine, "/dev/", 5) != 0)
	      continue;
	   //if (strncmp(sLine, "/dev/root", 9) == 0) 
	   //   continue;
	   
      pDev = strtok(sLine, " \n\r");   /* get first token */
      if ( pDev == NULL ) 
         continue;
      
      pMnt = strtok(NULL, " \n\r");
      if (pMnt == NULL) 
         continue;

      pType = strtok(NULL, " \n\r");
      if (pType == NULL) 
         continue;
      if (strncmp(pType, "vfat", 4) != 0)
         continue;

      strcpy(sImage, IMG_NUL);
      if (iUsbType == USB_STORAGE && sCurDev[0] != 0)
      {
         if (strncmp(pDev, sCurDev, strlen(pDev)) == 0)
         {
            strcpy(sImage, IMG_YES);
         }   
      }
      sprintf(sItem, "%s [%s]", pMnt, pType);
      mb_menu_add_item_to_menu(pMenu, pmSubMenu,
                               sItem,
                               sImage,
                               pDev,
                               usbfunct_cb, 
                               NULL, MBMENU_NO_SORT);
   }
   fclose(fp);
}

void menu_build(void)
{
   MBMenuMenu *pmRoot = g_app_data.mbmenu->rootmenu;
   MBMenuMenu *pmFreq, *pmLight;
   MBMenuActivateCB screensaver_cb = screensaver_toggle_cb;
   MBMenuActivateCB suspend_cb = suspend_toggle_cb;
   char *pFolder = NULL, *pNoApp = NULL;
  
   char info_msg[255];
   int   iIdx;
   char sNum[16];
   char sImage[MAXPATHLEN];
  
   pFolder = mb_dot_desktop_icon_get_full_path (g_app_data.theme_name,
                                                 16,
                                                 "mbfolder.png" );
   pNoApp = mb_dot_desktop_icon_get_full_path (g_app_data.theme_name,
                                                 16,
                                                 "mbnoapp.png" );
   mb_menu_set_default_icons(g_app_data.mbmenu, pFolder, pNoApp);
  
   if (pFolder) free(pFolder);
   if (pNoApp) free(pNoApp);
  
   iIdx = g_freq.iDefaultIdx;
   if (iIdx >= 0 && iIdx < MAXFREQNUM &&  g_freq.aiVal[iIdx][FREQ_NUM] > 0)
   {
      sprintf(info_msg, _("Default CPU - %d MHz"), g_freq.aiVal[iIdx][FREQ_NUM]);
      sNum[0] = '0' + iIdx;
      sNum[1] = 0;
      
      if (g_bSpeedStep != True && g_freq.iCurIdx == iIdx)
         strcpy(sImage, IMG_YES);
      else   
         strcpy(sImage, IMG_PREFIX "powerctrl-cpu.png");

      mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                               info_msg,
                               sImage,
                               sNum,
                               freq_menuset,
                               NULL, MBMENU_NO_SORT);
   }
  
   sNum[0] = 'A';
   sNum[1] = 0;
   if (g_bSpeedStep) 
      strcpy(sImage, IMG_PREFIX "powerctrl-step.png");
   else   
      strcpy(sImage, IMG_PREFIX "powerctrl-no_step.png");

   mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                            _("CPU Speed Step"),
                            sImage,
                            sNum,
                            freq_menuset,
                            NULL, MBMENU_NO_SORT);
  
   // sprintf(info_msg, _("CPU Clock"), g_freq.sCPU);
   pmFreq = mb_menu_add_path(g_app_data.mbmenu,
                             _("CPU Clock Frequency"),
                             IMG_PREFIX "powerctrl-cpu.png",
                             MBMENU_NO_SORT);
  
   freq_get();
   for (iIdx = MAXFREQNUM - 1; iIdx >= 0; iIdx--)
   {
      if (g_freq.aiVal[iIdx][FREQ_NUM] <= 0 || g_freq.aiVal[iIdx][FREQ_CCCR] == 0)
         continue;
  
      if (g_freq.aiVal[iIdx][FREQ_STABLE] <= 0)
         continue;

      sprintf(info_msg, _("%d MHz"), g_freq.aiVal[iIdx][FREQ_NUM]);
      sNum[0] = '0' + iIdx;
      sNum[1] = 0;
      if (g_freq.iCurIdx == iIdx)
         strcpy(sImage, IMG_YES);
      else    
         strcpy(sImage, IMG_NUL);

      mb_menu_add_item_to_menu(g_app_data.mbmenu, pmFreq,
                               info_msg,
                               sImage,
                               sNum,
                               freq_menuset,
                               NULL, MBMENU_NO_SORT);
   }
   
   mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);

   lnp_config_read();
   dpms_read();
   char sBat[255];
   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
   {
      if (g_apm_vals[PERCENTAGE] > 0 && g_apm_vals[PERCENTAGE] < 100 )
         sprintf(sBat, _("Charging %.2i%%"), g_apm_vals[PERCENTAGE]);
      else
         strcpy(sBat, _("Charge Full"));
      mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                               sBat,
                               IMG_PREFIX "powerctrl-ac.png",
                               "",
                               NULL,
                               NULL, MBMENU_NO_SORT);

   } else {
      if (g_apm_vals[PERCENTAGE] >= 0 && g_apm_vals[PERCENTAGE] <= 100)
         sprintf(sBat, _("Battery %d%%"), g_apm_vals[PERCENTAGE]);
      else
         strcpy(sBat, _("Battery Err"));
      mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                               sBat,
                               IMG_PREFIX "powerctrl-battery.png",
                               "",
                               NULL,
                               NULL, MBMENU_NO_SORT);
   }
  
   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
      sprintf(info_msg, _("ScreenSaver - %d Min"), lnp.ac_screensaver / 60);
   else
      sprintf(info_msg, _("ScreenSaver - %d Min"), lnp.ba_screensaver / 60);

   if (g_bScreensaver)
      strcpy(sImage, IMG_PREFIX "powerctrl-screensaver.png");
   else    
      strcpy(sImage, IMG_PREFIX "powerctrl-no_screensaver.png");

   mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                            info_msg,
                            sImage,
                            "",
                            screensaver_cb,
                            NULL, MBMENU_NO_SORT);
  
   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
      sprintf(info_msg, _("AutoSuspend - %d Min"), lnp.ac_suspend / 60);
   else
      sprintf(info_msg, _("AutoSuspend - %d Min"), lnp.ba_suspend / 60);

   if (g_bSuspend)
      strcpy(sImage, IMG_PREFIX "powerctrl-suspend.png");
   else    
      strcpy(sImage, IMG_PREFIX "powerctrl-no_suspend.png");

   mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                            info_msg,
	                         sImage,
                            "",
                            suspend_cb,
                            NULL, MBMENU_NO_SORT);
  
   mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);
  
   mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                            _("Blank Screen"),
                            IMG_PREFIX "powerctrl-screen.png",
                            "",
                            blanker_cb,
                            NULL, MBMENU_NO_SORT);
   pmLight = mb_menu_add_path(g_app_data.mbmenu,
                            _("Screen Brightness"),
                            IMG_PREFIX "powerctrl-light.png",
                            MBMENU_NO_SORT);
  
   backlight_get();
   for (iIdx = MAXBLNUM - 1; iIdx >= 0; iIdx--)
   {
      if (g_backlight[iIdx] == 0)
         continue;
         
      sprintf(info_msg, _("Level %d"), iIdx + 1);
      sNum[0] = '0' + iIdx + 1;
      sNum[1] = 0;
      if (backlight_now == iIdx + 1)
         strcpy(sImage, IMG_YES);
      else    
         strcpy(sImage, IMG_NUL);

      mb_menu_add_item_to_menu(g_app_data.mbmenu, pmLight,
                               info_msg,
                               sImage,
                               sNum,
                               backlight_menuset,
                               NULL, MBMENU_NO_SORT);
   }
  
   if (g_vol.bEnable || g_cards.bEnable || g_bUsbFunction || g_bSwapCtrl)
      mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);

   // volume menu
   if (g_vol.bEnable)
   {
      mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                            _("Mute"),
                            IMG_PREFIX "powerctrl-mute.png",
                            "",
                            mute_cb,
                            NULL, MBMENU_NO_SORT);
      MBMenuMenu *pmVol;
      pmVol = mb_menu_add_path(g_app_data.mbmenu,
                            _("Volume Control"),
                            IMG_PREFIX "powerctrl-volume.png",
                            MBMENU_NO_SORT);

      menu_build_volume(g_app_data.mbmenu, pmVol);
      mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);
   }

   if (g_cards.bEnable)
   {
      MBMenuMenu *pmCards;
      pmCards = mb_menu_add_path(g_app_data.mbmenu,
                            _("External Storage"),
                            IMG_PREFIX "cards.png",
                            MBMENU_NO_SORT);
      
      menu_build_cards(g_app_data.mbmenu, pmCards);
      menu_build_usbhost(g_app_data.mbmenu, pmCards);
      mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);
   }

   if (g_bUsbFunction)
   {
      MBMenuMenu *pmUsbFunct;
      pmUsbFunct = mb_menu_add_path(g_app_data.mbmenu,
                            _("USB Client Function"),
                            IMG_PREFIX "usb.png",
                            MBMENU_NO_SORT);
      
      menu_build_usbfunct(g_app_data.mbmenu, pmUsbFunct);
      mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);
   }
   
   sprintf(info_msg, _("MEM %i%% SWAP %i%%"), msd.mem_percent, msd.swap_percent);
   mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                            info_msg,
                            IMG_PREFIX "powerctrl-memory.png",
                            "",
                            NULL,
                            NULL, MBMENU_NO_SORT);
   if (g_bSwapCtrl)
   {
      memory_read();
      if (msd.swap_max > 0l)
      {
         long lSize = (long)(msd.swap_max >> 20);
         sprintf(info_msg, _("Swap (%lu)Mb"), lSize);
         mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                                 info_msg,
                                 IMG_YES,
                                 "",
                                 swap_menuset,
                                 NULL, MBMENU_NO_SORT);
      } else {
         mb_menu_add_item_to_menu(g_app_data.mbmenu, pmRoot,
                                 "Swap",
                                 IMG_NUL,
                                 "",
                                 swap_menuset,
                                 NULL, MBMENU_NO_SORT);
      }
   }
   
   //mb_menu_add_seperator_to_menu(g_app_data.mbmenu, pmRoot, MBMENU_NO_SORT);
}

void menu_get_popup_pos (MBTrayApp *app, int *x, int *y)
{
   int abs_x, abs_y, menu_h, menu_w;
   mb_tray_app_get_absolute_coords (app, &abs_x, &abs_y);
   mb_menu_get_root_menu_size(g_app_data.mbmenu, &menu_w, &menu_h);
  
   if (mb_tray_app_tray_is_vertical (app))
   {
      /* XXX need to figure out menu size before its mapped
        so we can figure out offset for east panel
      */
      *y = abs_y + mb_tray_app_height(app);
  
      if (abs_x > (DisplayWidth(mb_tray_app_xdisplay(app), mb_tray_app_xscreen(app)) /2))
         *x = abs_x - menu_w - 2;
      else
         *x = abs_x + mb_tray_app_width(app) + 2;
   } else {
      *x = abs_x;
      if (abs_y > (DisplayHeight(mb_tray_app_xdisplay(app), mb_tray_app_xscreen(app)) /2))
         *y = abs_y - 2;
      else
         *y = abs_y + mb_tray_app_height(app) + menu_h;
   }
}

void button_cb (MBTrayApp *app, int x, int y, Bool is_released )
{
   int abs_x, abs_y;
   sigset_t block_sigset;
   static Bool next_cancels;
  
   sigemptyset(&block_sigset);
   sigaddset(&block_sigset, SIGRTMIN);
  
   if (is_released && !next_cancels)
   {
      DBG("Active menu\n");
      sigaddset(&block_sigset, SIGRTMIN);
      menu_get_popup_pos (app, &abs_x, &abs_y);
      mb_menu_activate (g_app_data.mbmenu, abs_x, abs_y);
   } else {
     if (mb_menu_is_active(g_app_data.mbmenu))
     {
        DBG("Next cancel true\n");
        next_cancels = True;
     } else {
        DBG("Free menu and build\n");
        next_cancels = False;
        mb_menu_free(g_app_data.mbmenu);
        menu_build();
     }
   }
   /* mb_tray_app_repaint (app); */
}

void speedstep_higher(int iMax)
{
   int iWantIdx = g_freq.iCurIdx + 1;
   if (iWantIdx > iMax)
      return;
   if (g_bProtected && (iWantIdx > g_freq.iDefaultIdx) ) return;

   if (g_freq.aiVal[iWantIdx][FREQ_STABLE] > 0
       && g_freq.aiVal[iWantIdx][FREQ_NUM] > 0 
       && g_freq.aiVal[iWantIdx][FREQ_CCCR] != 0)
   {
      freq_set(iWantIdx);
      return;
   }
   int iNextIdx = -1;
   int iIdx;
   for ( iIdx = iWantIdx; iIdx <= iMax; iIdx++)
   {
      if (g_freq.aiVal[iIdx][FREQ_STABLE] > 0
          && g_freq.aiVal[iIdx][FREQ_NUM] > 0 
          && g_freq.aiVal[iIdx][FREQ_CCCR] != 0)
      {
         iNextIdx = iIdx;
         break;
      }
   }
   if (iNextIdx >= 0)
   {
      freq_stepset(iNextIdx);
   }
}

void speedstep_lower(int iMin)
{
   int iWantIdx = g_freq.iCurIdx - 1;
   if (iWantIdx < iMin)
      return;
   if (g_freq.aiVal[iWantIdx][FREQ_STABLE] > 0
       && g_freq.aiVal[iWantIdx][FREQ_NUM] > 0 
       && g_freq.aiVal[iWantIdx][FREQ_CCCR] != 0)
   {
      freq_set(iWantIdx);
      return;
   }
   int iNextIdx = -1;
   int iIdx;
   for ( iIdx = iWantIdx; iIdx >= iMin; iIdx--)
   {
      if (g_freq.aiVal[iIdx][FREQ_STABLE] > 0
          && g_freq.aiVal[iIdx][FREQ_NUM] > 0 
          && g_freq.aiVal[iIdx][FREQ_CCCR] != 0)
      {
         iNextIdx = iIdx;
         break;
      }
   }
   if (iNextIdx >= 0)
   {
      freq_stepset(iNextIdx);
   }
}

void speedstep_acpower(int cpu_percent)
{
   if (g_freq.iCurIdx < g_ac_step.iMinFreq)
   {
      freq_stepset(g_ac_step.iMinFreq);
      return;
   }
   if (cpu_percent > g_ac_step.iUsgHigher)
   {
      speedstep_higher(g_ac_step.iMaxFreq);
      return;
   }
   if (cpu_percent < g_ac_step.iUsgLower)
   {
      speedstep_lower(g_ac_step.iMinFreq);
      return;
   }
}

void speedstep_battery(int cpu_percent)
{
   if (g_freq.iCurIdx < g_ba_step.iMinFreq)
   {
      freq_stepset(g_ba_step.iMinFreq);
      return;
   }
   if (cpu_percent > g_ba_step.iUsgHigher)
   {
      speedstep_higher(g_ba_step.iMaxFreq);
      return;
   }

   if (cpu_percent < g_ba_step.iUsgLower)
   {
      speedstep_lower(g_ba_step.iMinFreq);
      return;
   }
}

void speedstep_delay(int cpu_percent)
{
   static int speed_delay = 0;
   if (speed_delay-- >  0)
    return;  /* nothing new */

   // update in 3 * 400ms = 1.2s
   speed_delay = g_iStepDelay;
   
   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
   {
      speedstep_acpower(cpu_percent);
   } else {
      speedstep_battery(cpu_percent);
   }
}

void fill_rect(RECT *pRC, RGB *pRGB)
{
   int x, y;
   for ( y = pRC->y ; y < pRC->y + pRC->h; y++)
   {
      for ( x = pRC->x; x < pRC->x + pRC->w; x++)
      {
          mb_pixbuf_img_plot_pixel(g_app_data.pb, g_img.pDraw, x, y,
                                   pRGB->r, pRGB->g, pRGB->b);
      }
   }
}

void fill_rect_step(RECT *pRC, RGB *pRGB, RGB *pStep)
{
   int x, y;
   RGB rgb;
   memcpy(&rgb, pRGB, sizeof(rgb));
   rgb.r += pRC->y * pStep->r; 
   rgb.g += pRC->y * pStep->g; 
   rgb.b += pRC->y * pStep->b; 
   for ( y = pRC->y; y < pRC->y + pRC->h; y++)
   {
      for ( x = pRC->x; x < pRC->x + pRC->w; x++)
      {
          mb_pixbuf_img_plot_pixel(g_app_data.pb, g_img.pDraw, x, y,
                                   rgb.r, rgb.g, rgb.b);
      }
      rgb.r += pStep->r; rgb.g += pStep->g; rgb.b += pStep->b; 
   }
}

Bool paint_battery(MBTrayApp *app, Bool bClearBox, int ba_pc, int iAC_Power)
{
   static int prev_ba_pixels = 0, prev_ba_color = 0;
   int    ba_pixels, ba_color;

   ba_pixels = (ba_pc * ( g_img.rcBattery.h) )/ 100 ;
   if (ba_pixels > g_img.rcBattery.h)    ba_pixels = g_img.rcBattery.h;
   if (ba_pixels < 0)    ba_pixels = 0;
  
   // DBG("Battery %d%%", ba_pc);
   RGB *pBackRGB;
   pBackRGB = &g_dispba.rgbBack;
   if (iAC_Power)
   {
      pBackRGB = &g_dispba_ext.rgbBackAC;
   }
  
   RGB *pRGB;
   pRGB = &g_dispba.rgbBegin;
   RGB *pStep;
   pStep = &g_dispba.rgbStep;

   ba_color = 0;
   if ( !iAC_Power )
   {
      if (ba_pc <= g_dispba_ext.iUsg0)
      { 
         ba_color = 1; 
         pRGB = &g_dispba_ext.rgbUsg0;
         pStep = &g_dispba_ext.rgbStep0; 
      }
      else if (ba_pc <= g_dispba_ext.iUsg1)
      { 
         ba_color = 2; 
         pRGB = &g_dispba_ext.rgbUsg1;
         pStep = &g_dispba_ext.rgbStep1; 
      }
      else if (ba_pc >= g_dispba_ext.iUsg2)
      { 
         ba_color = 3; 
         pRGB = &g_dispba_ext.rgbUsg2;
         pStep = &g_dispba_ext.rgbStep2; 
      }
      else;
   }
   if (ba_color != prev_ba_color)
   {
      bClearBox = True;
      prev_ba_color = ba_color;
   }
  
   if (bClearBox == True)
   {
      if (g_dispba.iOutline)
      {
         fill_rect(&g_img.rcBatteryOut, &g_dispba.rgbOutline);
      }
      // clear boxes
      fill_rect(&g_img.rcBattery, pBackRGB);
      prev_ba_pixels = 0;
   }
  
   
   if (ba_pixels == prev_ba_pixels)
      return False;

   RECT rcUpdate;
   memcpy(&rcUpdate, &g_img.rcBattery, sizeof(rcUpdate));
   if (prev_ba_pixels > ba_pixels)
   { // prev is higher, clear.
      rcUpdate.y = g_img.rcBattery.y + g_img.rcBattery.h - prev_ba_pixels;
      rcUpdate.h = prev_ba_pixels - ba_pixels;
      fill_rect(&rcUpdate, pBackRGB);
   } else {
      // current is higher
      rcUpdate.y = g_img.rcBattery.y + g_img.rcBattery.h - ba_pixels;
      rcUpdate.h = ba_pixels - prev_ba_pixels;
      fill_rect_step(&rcUpdate, pRGB, pStep);
   }
   
   if (!iAC_Power && ba_pc > 0 && ba_pc < g_iBatteryWarn)
   {
      char tray_msg[256];
      sprintf(tray_msg, _("Battery power very low !\n\nJuice %d%%"),
              ba_pc);
      mb_tray_app_tray_send_message(app, tray_msg, 0);
   }

   prev_ba_pixels = ba_pixels;
   return True;
}

Bool paint_cpu(MBTrayApp *app, Bool bClearBox, int cpu_pc)
{
   static int prev_cpu_pixels = 0;
   int    cpu_pixels;

   cpu_pixels = (cpu_pc * g_img.rcCpu.h)/ 100 ;
   if (cpu_pixels > g_img.rcCpu.h)    cpu_pixels = g_img.rcCpu.h;
   if (cpu_pixels < 0)    cpu_pixels = 0;
   if (bClearBox == True)
   {
      if (g_dispcpu.iOutline)
      {
         fill_rect(&g_img.rcCpuOut, &g_dispcpu.rgbOutline);
      }
      // clear boxes
      fill_rect(&g_img.rcCpu, &g_dispcpu.rgbBack);
      prev_cpu_pixels = 0;
   }

   if (cpu_pixels == prev_cpu_pixels)
      return False;
      
   RECT rcUpdate;
   memcpy(&rcUpdate, &g_img.rcCpu, sizeof(rcUpdate));
   if (prev_cpu_pixels > cpu_pixels)
   { // prev is higher, clear.
      rcUpdate.y = g_img.rcCpu.y + g_img.rcCpu.h - prev_cpu_pixels;
      rcUpdate.h = prev_cpu_pixels - cpu_pixels;
      fill_rect(&rcUpdate, &g_dispcpu.rgbBack);
   } else {
      // current is higher
      rcUpdate.y = g_img.rcCpu.y + g_img.rcCpu.h - cpu_pixels;
      rcUpdate.h = cpu_pixels - prev_cpu_pixels;
      fill_rect_step(&rcUpdate, &g_dispcpu.rgbBegin, &g_dispcpu.rgbStep);
   }
   prev_cpu_pixels = cpu_pixels;
  
   return True;
}

Bool paint_memory(MBTrayApp *app, Bool bClearBox, int mem_pc)
{
   static int prev_mem_pixels = 0;
   int    mem_pixels;

   mem_pixels = (mem_pc * ( g_img.rcMem.h) )/ 100 ;
   if (mem_pixels > g_img.rcMem.h)    mem_pixels = g_img.rcMem.h;
   if (mem_pixels < 0)    mem_pixels = 0;
   // fprintf (stderr, "cpu %d, mem %d\n", cpu_pixels, mem_pixels);
  
   if (bClearBox == True)
   {
      if (g_dispmem.iOutline)
      {
         fill_rect(&g_img.rcMemOut, &g_dispmem.rgbOutline);
      }
      // clear boxes
      fill_rect(&g_img.rcMem, &g_dispmem.rgbBack);
      prev_mem_pixels = 0;
   }
   
   if (mem_pixels == prev_mem_pixels)
      return False;

   RECT rcUpdate;
   memcpy(&rcUpdate, &g_img.rcMem, sizeof(rcUpdate));
   if (prev_mem_pixels > mem_pixels)
   { // prev is higher, clear.
      rcUpdate.y = g_img.rcMem.y + g_img.rcMem.h - prev_mem_pixels;
      rcUpdate.h = prev_mem_pixels - mem_pixels;
      fill_rect(&rcUpdate, &g_dispmem.rgbBack);
   } else {
      // current is higher
      rcUpdate.y = g_img.rcMem.y + g_img.rcMem.h - mem_pixels;
      rcUpdate.h = mem_pixels - prev_mem_pixels;
      //fill_rect(&rcUpdate, &g_dispmem.rgbBegin);
      fill_rect_step(&rcUpdate, &g_dispmem.rgbBegin, &g_dispmem.rgbStep);
   }
   /* XXX Alert here for low memory  */
   prev_mem_pixels = mem_pixels;

   return True;
}

Bool paint_freq(MBTrayApp *app, Bool bClearBox, int iCurIdx)
{
   static int prev_freq_pixels = 0;
   int    freq_pixels;
   RGB    *pRGB;

   // draw freq line
   freq_pixels = iCurIdx;
   if (freq_pixels > g_freq.iMaxIdx)    freq_pixels = g_freq.iMaxIdx;
   if (freq_pixels < 0)    freq_pixels = 0;

   int iIdx;
   // when iCurIdx is 0, display one square
   RECT rcUpdate;
   memcpy(&rcUpdate, &g_img.rcFreq, sizeof(rcUpdate));
   if (bClearBox == True)
   {
      // clear boxes
      for (iIdx = 0; iIdx < MAXFREQNUM; iIdx++)
      {
         rcUpdate.x = g_img.rcFreq.x + iIdx * g_img.freq_gap;
         if (iIdx == 0)
            fill_rect(&rcUpdate, &g_dispfreq.rgbOutline);
         else
            fill_rect(&rcUpdate, &g_dispfreq.rgbBack);
      }
      prev_freq_pixels = 0;
   }

   if (freq_pixels == prev_freq_pixels)
      return False;
      
   if (prev_freq_pixels > freq_pixels)
   { // prev is higher, clear.
      pRGB = &g_dispfreq.rgbBack;
      for (iIdx = freq_pixels + 1; iIdx < prev_freq_pixels + 1; iIdx++)
      {
         rcUpdate.x = g_img.rcFreq.x + iIdx * g_img.freq_gap;
         fill_rect(&rcUpdate, &g_dispfreq.rgbBack);
      }
   } else {
      for (iIdx = prev_freq_pixels + 1; iIdx < freq_pixels + 1; iIdx++)
      {
         rcUpdate.x = g_img.rcFreq.x + iIdx * g_img.freq_gap;
         if (iIdx == g_freq.iDefaultIdx)
            fill_rect(&rcUpdate, &g_dispfreq.rgbBegin);
         else if (iIdx > g_freq.iDefaultIdx)
            fill_rect(&rcUpdate, &g_dispfreq.rgbStep);
         else 
            fill_rect(&rcUpdate, &g_dispfreq.rgbOutline);
      }
   }
   prev_freq_pixels = freq_pixels;
   return True;
}

void paint_cb (MBTrayApp *app, Drawable drw )
{
   Bool bClearBox = False;
   Bool bUpdate = False;
   static long lOverCount = 0;
   static int prev_ac_power = -1;
   int    iAC_Power;

   // no re-entrancy
   if (g_img.pDraw == NULL)
   {
      g_img.pDraw = mb_tray_app_get_background (app, g_app_data.pb);
      mb_pixbuf_img_copy_composite (g_app_data.pb, g_img.pDraw,
                                    g_img.pScaled,
                                    0, 0,
                                    g_img.scaled_w,
                                    g_img.scaled_h,
                                    0, 0 );
      bUpdate = True;
      bClearBox = True;
   }
   
   read_apm_delay();
   int ba_percent;
   ba_percent = g_apm_vals[PERCENTAGE];
   
   if (g_apm_vals[AC_POWER] == AC_LINE_STATUS_ON)
      iAC_Power = 1;
   else
      iAC_Power = 0;
  
   if (iAC_Power != prev_ac_power && prev_ac_power >= 0)
   {
      bClearBox = True;
      
      if (lnp.ac_backlight != lnp.ba_backlight)
      {
         if (iAC_Power)
            backlight_set(lnp.ac_backlight);
         else
            backlight_set(lnp.ba_backlight);
      }
      if ( lnp.ac_screensaver != lnp.ba_screensaver ||
           lnp.ac_suspend != lnp.ba_suspend )
      {     
         if (WEXITSTATUS(system("pidof -s mb-applet-battery > /dev/null")) > 0)
            dpms_set();
      }
   }
   prev_ac_power = iAC_Power;
   
   if (g_dispba.bDisplay)
   {
      if (paint_battery(app, bClearBox, ba_percent, iAC_Power)) bUpdate = True;
   }

   int cpu_usage;
   cpu_usage = system_cpu();
   if (g_dispcpu.bDisplay)
   {
      if (paint_cpu(app, bClearBox, cpu_usage)) bUpdate = True;
   }

   int mem_percent;
   memory_read_delay();              /* Update reading */
   mem_percent = msd.mem_percent;
   if (g_dispmem.bDisplay)
   {
      if (paint_memory(app, bClearBox, mem_percent)) bUpdate = True;
   }
   
   // cpu overclock protect
   if (g_freq.iCurIdx > g_freq.iDefaultIdx)
      lOverCount++;
   else
      lOverCount--;
   if (lOverCount < 0)
      lOverCount = 0l;
   
   if (g_bProtected)
   {
      if (lOverCount < g_lOverProtect/2)
      {
         DBG("CPU protect Off\n");
         g_bProtected = False;
      }
   } else {
      if (lOverCount > g_lOverProtect)
      {
         DBG("CPU protect On\n");
         g_bProtected = True;
      }
   }
   if (g_dispfreq.bDisplay)
   {
      if (paint_freq(app, bClearBox, g_freq.iCurIdx)) bUpdate = True;
   }
  
   
   if (bUpdate == True || bClearBox == True)
   {
      /* DBG("update tray" ); */
      mb_pixbuf_img_render_to_drawable(g_app_data.pb, g_img.pDraw, drw, 0, 0);
   }
   
   if (g_bProtected && g_freq.iCurIdx > g_freq.iDefaultIdx)
   {
      freq_stepset(g_freq.iDefaultIdx);
   }
      
   if (g_bSpeedStep == True)  speedstep_delay(cpu_usage);
}

void load_icons(void)
{
   char *icon_path = NULL;

   if (g_img.pOrig)
      mb_pixbuf_img_free(g_app_data.pb, g_img.pOrig);

   icon_path = mb_dot_desktop_icon_get_full_path (g_app_data.theme_name,
                                                  16,
                                                  TRAY_IMG);
   if (icon_path == NULL)
   {
      fprintf(stderr, "mbmenu: failed to load icon\n");
      exit(1);
   }
   if ( !(g_img.pOrig = mb_pixbuf_img_new_from_file(g_app_data.pb, icon_path)) )
   {
      free(icon_path);
      fprintf(stderr, "mbmenu: failed to load icon\n");
      exit(1);
   }
   free(icon_path);
}

void theme_callback (MBTrayApp *app, char *theme_name)
{
   if (!theme_name) return;
   if (g_app_data.theme_name) free(g_app_data.theme_name);
   g_app_data.theme_name = strdup(theme_name);

   if (g_app_data.mbmenu != NULL)
   {
      mb_menu_free(g_app_data.mbmenu);
      menu_build();
      load_icons();
      resize_cb (app, mb_tray_app_width(app), mb_tray_app_width(app) );
      mb_tray_app_repaint (g_app_data.tray_app);
   }
}

void xevent_callback (MBTrayApp *app, XEvent *ev)
{
   sigset_t block_sigset;

   mb_menu_handle_xevent (g_app_data.mbmenu, ev);

#ifdef USE_DNOTIFY
   if (!mb_menu_is_active(g_app_data.mbmenu))
   {                           /* Unblock any dnotify signals */
      sigemptyset(&block_sigset);
      sigaddset(&block_sigset, SIGRTMIN);
      sigprocmask(SIG_UNBLOCK, &block_sigset, NULL);
   }
#endif

#define MB_CMD_SHOW_EXT_MENU 6

   if (ev->type == ClientMessage)
   {
      if (ev->xclient.message_type == g_app_data.mbcommand_atom
          && ev->xclient.data.l[0] == MB_CMD_SHOW_EXT_MENU )
      {
         sigemptyset(&block_sigset);
         sigaddset(&block_sigset, SIGRTMIN);

         if (!mb_menu_is_active(g_app_data.mbmenu))
         {
            int abs_x, abs_y;
            sigprocmask(SIG_BLOCK, &block_sigset, NULL);
            menu_get_popup_pos (app, &abs_x, &abs_y);
            mb_menu_activate(g_app_data.mbmenu, abs_x, abs_y);
         } else {
            mb_menu_deactivate(g_app_data.mbmenu);
            sigprocmask(SIG_UNBLOCK, &block_sigset, NULL);
         }
      }
   }
}

void blanker_clicked (GtkWidget *w, GdkEventButton *ev)
{
   gdk_pointer_ungrab (ev->time);
   gdk_keyboard_ungrab (ev->time);

   gtk_widget_hide (wnd_blanker);

   gtk_timeout_remove(backlight_timer);

   backlight_on();
}

void popup_init(MBTrayApp *app)
{
   /*
   GtkWidget     *vbox;
   GtkWidget     *hbox;
   GtkWidget     *label;
   GtkWidget     *button_mute, *button_ok;
   GtkAdjustment *adj;
   */
   GtkStyle      *style = gtk_style_new();

   style->bg[GTK_STATE_NORMAL] = style->black;

   wnd_blanker = gtk_window_new (GTK_WINDOW_POPUP);

   gtk_widget_set_style(wnd_blanker, style);
   gtk_window_set_default_size ((GtkWindow *)wnd_blanker, 640, 480);

   g_signal_connect (G_OBJECT (wnd_blanker), "button-press-event", G_CALLBACK (blanker_clicked), NULL);

   gtk_widget_add_events (wnd_blanker, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);

   gtk_widget_realize (wnd_blanker);
}

GdkFilterReturn event_filter (GdkXEvent *xev, GdkEvent *gev, gpointer data)
{
   XEvent    *ev  = (XEvent *)xev;
   MBTrayApp *app = (MBTrayApp*)data;

   /* Display *dpy = ev->xany.display; */

   mb_tray_handle_xevent (app, ev);

   return GDK_FILTER_CONTINUE;
}

void check_timeout_cb ( MBTrayApp *app )
{
   //if(!mb_menu_is_active(g_app_data.mbmenu))
   //   dpms_set();
   
   // check unexpected
   // lnp_config_read();
   freq_get();
}

void tray_timeout_cb ( MBTrayApp *app )
{
   mb_tray_app_repaint (g_app_data.tray_app);
}

int main( int argc, char *argv[])
{
   g_cards.bEnable = True;
   g_cards.iCardSD = -1;
   g_cards.iCardCF = -1;
   g_cards.iUsbStorage = -1;
   load_config( argc, argv );
   
   MBTrayApp *app = NULL;
   struct sigaction act;
  
   u_int64_t load = 0, total = 0;
  
   FILE *pidof_fp = NULL;
   int kipid = 1;
  
   
   if ((pidof_fp = popen("pidof kapm-idled", "r")) == NULL)
   {
      fprintf(stderr, "Error executing pidof\n");
      exit(1);
   }
   fscanf(pidof_fp, "%d", &kipid);
   pclose(pidof_fp);
   if (kipid == -1)
   {
      fprintf(stderr, "Could not execute pidof or no pid found for kapm-idled\n");
      exit(1);
   }
  
   sprintf(kistat, "/proc/%d/stat", kipid);
  
   gtk_init (&argc, &argv);
  
#ifdef USE_DNOTIFY
   int fd;
#endif

#if ENABLE_NLS
   setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, "/usr/share/locale");
   bind_textdomain_codeset (PACKAGE, "UTF-8");
   textdomain (PACKAGE);
#endif

   memset(&g_app_data, 0, sizeof(g_app_data));
   memset(&g_img, 0, sizeof(g_img));

   g_pDisplay = GDK_DISPLAY();
   if (g_pDisplay == NULL) 
   {
      fprintf(stderr, "unable to open display [%s]\n", XDisplayName (NULL));
      exit(1);
   }
   
   app = mb_tray_app_new_with_display ("Power Control",
                                       resize_cb,
                                       paint_cb,
                                       &argc,
                                       &argv,
                                       g_pDisplay);
   msd.samples = 16;

   if (msd.load)
   {
      load = msd.load[msd.loadIndex];
      free(msd.load);
   }

   if (msd.total)
   {
      total = msd.total[msd.loadIndex];
      free(msd.total);
   }

   msd.loadIndex = 0;
   msd.load = malloc(msd.samples * sizeof(u_int64_t));
   msd.total = malloc(msd.samples * sizeof(u_int64_t));
   int i;
   for (i = 0; i < msd.samples; i++)
   {
      msd.load[i] = load;
      msd.total[i] = total;
   }

   g_app_data.tray_app = app;

   g_app_data.mbtheme_atom
      = XInternAtom(mb_tray_app_xdisplay(app), "_MB_THEME", False);
   g_app_data.mbcommand_atom
      = XInternAtom(mb_tray_app_xdisplay(app), "_MB_COMMAND", False);

   g_app_data.mbmenu = mb_menu_new(mb_tray_app_xdisplay(app),
                                  mb_tray_app_xscreen(app));

   mb_menu_set_font(g_app_data.mbmenu, g_sMenuFont);

#ifdef USE_LIBSN
   g_app_data.sn_display = sn_display_new (mb_tray_app_xdisplay(app),
                                           NULL, NULL);
#endif

   g_app_data.pb = mb_pixbuf_new(mb_tray_app_xdisplay(app),
                                 mb_tray_app_xscreen(app));

   XSelectInput (mb_tray_app_xdisplay(app), mb_tray_app_xrootwin(app),
                 PropertyChangeMask|SubstructureNotifyMask);

   mb_tray_app_set_button_callback (app, button_cb );

   mb_tray_app_set_xevent_callback (app, xevent_callback );

   mb_tray_app_set_theme_change_callback (app, theme_callback );

   gtk_timeout_add (400, // 400 ms
                    (GSourceFunc) tray_timeout_cb,
                    app);

   gtk_timeout_add (30000, // 30 seconds
                    (GSourceFunc) check_timeout_cb,
                    app);

   lnp_config_read();
   dpms_read();
   freq_get();
   load_icons();

   if (backlight_chk() == 0)
   {
      // reset backlight
      backlight_on();
   }
   if ( !g_bSpeedStep && (g_freq.iCurIdx != g_freq.iDefaultIdx) )
   {
      freq_stepset(g_freq.iDefaultIdx);
   }

   /* Set up signals */
   act.sa_flags = 0;
   sigemptyset(&act.sa_mask);
   act.sa_handler = reap_children;
   sigaction(SIGCHLD, &act, NULL);

   act.sa_handler = cleanup_handler;
   sigaction(SIGTERM, &act, NULL);
   sigaction(SIGINT, &act, NULL);
   sigaction(SIGQUIT, &act, NULL);

   /* Initialise popup window (starts off hidden) and key event handling */
   popup_init(app);

   mb_tray_app_set_icon(app, g_app_data.pb, g_img.pOrig);

   mb_tray_app_main_init (app);
   gdk_window_add_filter (NULL, event_filter, (gpointer)app );

   gtk_main ();
   
   DBG("Quit Main\n");
   return 1;
}

