Hi, I fixed the sysfs-patch, at least for my system (lenny i386 with standard kernel on a samsung x20), but it should be so generic that it works on any system. There were several problems and I'll describe shortly my way to fix them: - The AC adapter is not always listed in /sys/class/power_supply/AC/. Now it checks in any directory in /sys/class/power_supply if the file "type" exists and contains "Mains". I hope that AC adapters always have the type "Mains", otherwise the other possibilities should also be considered. Only one AC adapter (are there systems with more than one?) is used. - In 2.6.24 the charge status is in /sys/class/power_supply/$battery/charge_full, charge_full_design and charge_now. It seems like it was energy_* before. now both possibilities are taken care of. - The monitor showed random values for the remaining capacity - probably because the struct that saves acpi status was not initialised (because of charge_* vs energy_*) and only allocated with malloc. using calloc should give more deterministic values.
I also changed some code that didn't make sense to me. I noticed that after unplugging the power cord the monitor show the new status quite quick, but it takes about 30 seconds until the displayed time left makes sense. I don't know if this is normal or connected to my patch. Cheers, - Daniel
diff -ru xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.c xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.c --- xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.c 2008-04-06 02:56:10.502679698 +0200 +++ xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.c 2008-04-06 03:15:11.306678469 +0200 @@ -54,6 +54,10 @@ static char batteries[MAXBATT][128]; static char battinfo[MAXBATT][128]; +/* path to AC adapter because not all AC adapter are listed +in /sys/class/power_supply/AC/ this obviously only supports one AC adapter. */ +static char sysfsacdir[128]; + #ifndef __linux__ #if HAVE_SYSCTL static int @@ -181,9 +185,76 @@ #endif #endif +int check_acpi_sysfs(void) +{ + DIR *sysfs; + struct dirent *batt; + char *name; + FILE *typefile; + char typepath[128]; + char tmptype[8]; + + sysfs = opendir("/sys/class/power_supply"); + if (sysfs == 0) + { + #ifdef DEBUG + printf("DBG:No acpi support for sysfs. Trying procfs...\n"); + #endif + return 2; + } + + while ((batt = readdir(sysfs))) + { + name = batt->d_name; + if (!strncmp(".", name, 1)) continue; + /* check whether /sys/class/power_supply/$name/type exists and + contains "Battery" or "Mains" */ + sprintf(typepath, "/sys/class/power_supply/%s/type",name); + if(!(typefile = fopen(typepath, "r"))) continue; + fgets(tmptype, 8, typefile); + fclose(typefile); + if(strncmp("Battery", tmptype, 7)==0) + { + sprintf(batteries[batt_count], "/sys/class/power_supply/%s", name); + #ifdef DEBUG + printf("DBG:battery number %d at:\n",batt_count); + printf("DBG:sysfs dir->%s\n",batteries[batt_count]); + printf("DBG:------------------------\n"); + #endif + batt_count++; + } + /* I guess that the type of the AC adapter is always "Mains" (?) */ + else if(strncmp("Mains", tmptype, 5)==0){ + sprintf(sysfsacdir, "/sys/class/power_supply/%s", name); + #ifdef DEBUG + printf("DBG:sysfs AC dir->%s\n",sysfsacdir); + printf("DBG:------------------------\n"); + #endif + } + } + closedir(sysfs); + if ( batt_count == 0 ) + { +#ifdef DEBUG + printf("DBG:No acpi support for sysfs. Trying procfs...\n"); +#endif + acpi_sysfs = 0; + return 2; + } + else + { + acpi_sysfs = 1; + return 0; + } +} + /* see if we have ACPI support */ int check_acpi(void) { +#ifdef __linux__ + if ( check_acpi_sysfs() == 0 ) + return 0; +#endif DIR *battdir; struct dirent *batt; char *name; @@ -262,9 +333,82 @@ #endif } +int read_sysfs_int(char* filename) +{ + FILE* f; + f = fopen(filename,"r"); + if ( !f ) + { +#ifdef DEBUG + printf("DBG:Could not open %s\n",filename); +#endif + return 0; + } + int out; + fscanf(f,"%d",&out); + fclose(f); + return out; +} + +char* read_sysfs_string(char* filename) +{ + FILE* f; + f = fopen(filename,"r"); + if ( !f ) + { +#ifdef DEBUG + printf("DBG:Could not open %s\n",filename); +#endif + return NULL; + } + fscanf(f,"%s",buf2); + fclose(f); + return buf2; +} + +int read_acad_state_sysfs(void) +{ + DIR *sysfs; + struct dirent *propety; + char *name; + char onlinefilepath[128]; + + sysfs = opendir(sysfsacdir); + if (sysfs == 0) + { + #ifdef DEBUG + printf("DBG:Can't open %s",sysfsacdir); + #endif + return 0; + } + closedir(sysfs); + + if (!acadstate) acadstate=(ACADstate *)malloc(sizeof(ACADstate)); + /* this code doesn't make much sense.. why look at the whole directory?! + while ((propety = readdir(sysfs))) + { + name = propety->d_name; + if (!strncmp(".", name, 1) || !strncmp("..", name, 2)) continue; + + if (strcmp(name,"online") == 0) + { + acadstate->state = ( read_sysfs_int("/sys/class/power_supply/AC/online") == 1 ) ; + } + } + */ + sprintf(onlinefilepath, "%s/online", sysfsacdir); + /* if onlinefilepath doesn't exist read_sysfs_int() will return 0 + so acadstate->state will be 0, that should be ok */ + acadstate->state = ( read_sysfs_int(onlinefilepath) == 1 ); + + return acadstate->state; +} + int read_acad_state(void) { #ifdef __linux__ + if (acpi_sysfs) + return read_acad_state_sysfs(); FILE *acpi; char *ptr; char stat; @@ -354,20 +498,83 @@ #endif } +int read_acpi_info_sysfs(int battery) +{ + DIR *sysfs; + struct dirent *propety; + char *name; + + sysfs = opendir(batteries[battery]); + if (sysfs == 0) + { + #ifdef DEBUG + printf("DBG:Can't open %s!\n", batteries[battery]); + #endif + return 0; + } + /* malloc.. might explain the random battery level values on 2.6.24 + systems (energe_full is called charge_full so the value isn't initialised + and some random data from the heap is displayed..) + if (!acpiinfo) acpiinfo=(ACPIinfo *)malloc(sizeof(ACPIinfo)); + */ + if (!acpiinfo) acpiinfo=(ACPIinfo *)calloc(1, sizeof(ACPIinfo)); + + while ((propety = readdir(sysfs))) + { + name = propety->d_name; + if (!strncmp(".", name, 1) || !strncmp("..", name, 2)) continue; + /* at least on my system this is called charge_full */ + if ((strcmp(name,"energy_full") == 0) || (strcmp(name,"charge_full") == 0)) + { + sprintf(buf,"%s/%s",batteries[battery], name); + acpiinfo->last_full_capacity = read_sysfs_int(buf); + } + if ((strcmp(name,"energy_full_design") == 0) || (strcmp(name,"charge_full_design") == 0)) + { + sprintf(buf,"%s/%s",batteries[battery], name); + acpiinfo->design_capacity = read_sysfs_int(buf); + } + if (strcmp(name,"technology") == 0) + { + char *tmp; + sprintf(buf,"%s/%s",batteries[battery], name); + tmp = read_sysfs_string(buf); + if (tmp != NULL) + { + if (strcmp(tmp,"Li-ion") == 0) + acpiinfo->battery_technology = 1; + else + acpiinfo->battery_technology = 0; + } + } + if (strcmp(name,"present") == 0) + { + sprintf(buf,"%s/%s",batteries[battery], name); + acpiinfo->present = read_sysfs_int(buf); + } + } + closedir(sysfs); + return acpiinfo->present; +} + int read_acpi_info(int battery) { #ifdef __linux__ - FILE *acpi; - char *ptr; - char stat; - int temp; - if (battery > MAXBATT) { #ifdef DEBUG printf("DBG: error, battery > MAXBATT (%d)\n",MAXBATT); #endif return 0; } + + if (acpi_sysfs) + return read_acpi_info_sysfs(battery); + + FILE *acpi; + char *ptr; + char stat; + int temp; + if (!(acpi = fopen (battinfo[battery], "r"))) { #ifdef DEBUG printf("DBG:cannot open %s for read!\n",battinfo[battery]); @@ -514,9 +721,80 @@ } +int read_acpi_state_sysfs(int battery) +{ + DIR *sysfs; + struct dirent *propety; + char *name; + + sysfs = opendir(batteries[battery]); + if (sysfs == 0) + { + #ifdef DEBUG + printf("DBG:Can't open %s!\n", batteries[battery]); + #endif + return 0; + } + + /* again it might be better to use calloc + if (!acpistate) acpistate=(ACPIstate *)malloc(sizeof(ACPIstate)); + */ + if (!acpistate) acpistate=(ACPIstate *)calloc(1, sizeof(ACPIstate)); + + while ((propety = readdir(sysfs))) + { + name = propety->d_name; + if (!strncmp(".", name, 1) || !strncmp("..", name, 2)) continue; + + if (strcmp(name,"status") == 0) + { + char *tmp; + sprintf(buf,"%s/%s",batteries[battery], name); + tmp = read_sysfs_string(buf); + if ( tmp != NULL ) + { + if (strcmp(tmp,"Charging") == 0) + acpistate->state = CHARGING; + else if (strcmp(tmp,"Discharging") == 0) + acpistate->state = DISCHARGING; + else if (strcmp(tmp,"Full") == 0) + acpistate->state = POWER; + else + acpistate->state = UNKNOW; + } + } + /* on my system this is called charge_now */ + if ((strcmp(name,"energy_now") == 0) || (strcmp(name,"charge_now") == 0)) + { + sprintf(buf,"%s/%s",batteries[battery], name); + acpistate->rcapacity = read_sysfs_int(buf); + acpistate->percentage = (((float) acpistate->rcapacity)/acpiinfo->last_full_capacity) * 100; + } + if (strcmp(name,"current_now") == 0) + { + sprintf(buf,"%s/%s",batteries[battery], name); + acpistate->prate = read_sysfs_int(buf); + if ( acpistate->prate < 0 ) + acpistate->prate = 0; + if ( acpistate->prate > 0 ) + acpistate->rtime = (((float) acpistate->rcapacity) / acpistate->prate) * 60; + } + if (strcmp(name,"voltage_now") == 0) + { + sprintf(buf,"%s/%s",batteries[battery], name); + acpistate->pvoltage = read_sysfs_int(buf); + } + } + closedir(sysfs); + return acpiinfo->present; +} + int read_acpi_state(int battery) { #ifdef __linux__ + if (acpi_sysfs) + return read_acpi_state_sysfs(battery); + FILE *acpi; char *ptr; char stat; diff -ru xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.h xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.h --- xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.h 2007-01-17 18:56:51.000000000 +0100 +++ xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.h 2008-04-05 22:13:55.522679792 +0200 @@ -80,6 +80,8 @@ int batt_count; /* temp buffer */ char buf[512]; +char buf2[512]; +int acpi_sysfs; #else extern int batt_count; extern ACPIstate *acpistate;