Hi all. Happy holidays and new year for everyone. :) I've added to the Gnome battstat applet the ability to calculate the (estimate) remaining battery/charging time. When the system is running on batteries or recharging them, the remaining time will be shown on the applet's tooltip.
The math has been taken from Grahame Bowland's ACPI command line utility. The patch attached to this mail also includes the previous /proc/acpi parsing patch. I've got two concerns about the code: - All the math is done in acpi-linux.c and then the value in seconds is passed to the applet's core in battstat_applet.c using the battery_time component of the apm_info structure of apmlib/apm.h. I suppose this battery_time var was intended to be used for something like this, but I don't know it for sure and I don't know if the value should be seconds, minutes, or what. Anyway that var is not being used anywhere else on the applet so using it that way I'm not breaking anything, but you know... - The time estimates are a little bit jerky over time... I mean, in consecutive reads over a few seconds the estimates differ sometimes in more than half an hour, sometimes even a full hour. This is due to the values read from the /proc files: if there's a 100% peak in CPU usage the battery discharging rate will be higher (faster) in that lapse of time, and then if the next second the CPU goes near 0% the discharging rate will be (s)lower... Maybe we should keep the last, say, three values read and then use the average of those three values to do the math, instead of using only the last usage/charging rate. What do you think? Well, test the patch and tell me what you think. I've been using it for a couple of days and it seems to work right. -- Vicente Aguilar <[EMAIL PROTECTED]> | http://www.bisente.com
diff -urN orig/gnome-applets-2.0.4/battstat/acpi-linux.c gnome-applets-2.0.4/battstat/acpi-linux.c --- orig/gnome-applets-2.0.4/battstat/acpi-linux.c 2002-05-14 00:26:17.000000000 +0200 +++ gnome-applets-2.0.4/battstat/acpi-linux.c 2002-12-25 22:30:59.000000000 +0100 @@ -39,6 +39,7 @@ #include <fcntl.h> #include <errno.h> #include <unistd.h> +#include <dirent.h> #include "acpi-linux.h" static GHashTable * @@ -141,12 +142,15 @@ */ gboolean acpi_linux_read(struct apm_info *apminfo) { - guint32 max_capacity, low_capacity, critical_capacity, remain; + guint32 max_capacity, low_capacity, critical_capacity, remain, rate, seconds; gboolean charging, ac_online; gulong acpi_ver; char buf[BUFSIZ]; GHashTable *hash; - const char *batt_info, *batt_state, *ac_state, *ac_state_state, *charging_state; + const char *ac_state_state, *charging_state; + char batt_info[60], batt_state[60], ac_state[60]; + DIR * procdir; + struct dirent * procdirentry; /* * apminfo.ac_line_status must be one when on ac power @@ -160,6 +164,11 @@ max_capacity = 0; low_capacity = 0; critical_capacity = 0; + charging = FALSE; + remain = 0; + rate = 0; + seconds = 0; + ac_online = FALSE; hash = read_file ("/proc/acpi/info", buf, sizeof (buf)); if (!hash) @@ -169,59 +178,84 @@ g_hash_table_destroy (hash); if (acpi_ver < (gulong)20020208) { - batt_info = "/proc/acpi/battery/1/info"; - batt_state = "/proc/acpi/battery/1/status"; - ac_state = "/proc/acpi/ac_adapter/0/status"; ac_state_state = "status"; charging_state = "state"; } else { - batt_info = "/proc/acpi/battery/BAT1/info"; - batt_state = "/proc/acpi/battery/BAT1/state"; - ac_state = "/proc/acpi/ac_adapter/ACAD/state"; ac_state_state = "state"; charging_state = "charging state"; } - hash = read_file (batt_info, buf, sizeof (buf)); - if (hash) - { - max_capacity = read_long (hash, "design capacity"); - low_capacity = read_long (hash, "design capacity warning"); - critical_capacity = read_long (hash, "design capacity low"); - g_hash_table_destroy (hash); - } - + procdir=opendir("/proc/acpi/battery/"); + while ((procdirentry=readdir(procdir))) + { + if (procdirentry->d_name[0]!='.') + { + strcpy(batt_info,"/proc/acpi/battery/"); + strcat(batt_info,procdirentry->d_name); + strcat(batt_info,"/info"); + hash = read_file (batt_info, buf, sizeof (buf)); + if (hash) + { + max_capacity += read_long (hash, "last full capacity"); + low_capacity += read_long (hash, "design capacity warning"); + critical_capacity += read_long (hash, "design capacity low"); + g_hash_table_destroy (hash); + } + strcpy(batt_state,"/proc/acpi/battery/"); + strcat(batt_state,procdirentry->d_name); + strcat(batt_state,"/state"); + hash = read_file (batt_state, buf, sizeof (buf)); + if (hash) + { + const char *s; + if (!charging) + { + s = read_string (hash, charging_state); + charging = s ? (strcmp (s, "charging") == 0) : 0; + } + remain += read_long (hash, "remaining capacity"); + rate += read_long (hash, "present rate"); + g_hash_table_destroy (hash); + } + } + } + closedir(procdir); + if (!max_capacity) return FALSE; - - charging = FALSE; - remain = 0; - - hash = read_file (batt_state, buf, sizeof (buf)); - if (hash) - { - const char *s; - s = read_string (hash, charging_state); - charging = s ? (strcmp (s, "charging") == 0) : 0; - remain = read_long (hash, "remaining capacity"); - g_hash_table_destroy (hash); - } - ac_online = FALSE; + procdir=opendir("/proc/acpi/ac_adapter/"); + while ((procdirentry=readdir(procdir))) + { + if (procdirentry->d_name[0]!='.') + { + strcpy(ac_state,"/proc/acpi/ac_adapter/"); + strcat(ac_state,procdirentry->d_name); + strcat(ac_state,"/"); + strcat(ac_state,ac_state_state); + hash = read_file (ac_state, buf, sizeof (buf)); + if (hash && !ac_online) + { + const char *s; + s = read_string (hash, ac_state_state); + ac_online = s ? (strcmp (s, "on-line") == 0) : 0; + g_hash_table_destroy (hash); + } + } + } + closedir(procdir); + + if (charging && rate > 0.01) { + seconds = 3600 * (double)(max_capacity - remain) / rate; + } else if (!ac_online) { + seconds = 3600 * (float)remain / rate; + } - hash = read_file (ac_state, buf, sizeof (buf)); - if (hash) - { - const char *s; - s = read_string (hash, ac_state_state); - ac_online = s ? (strcmp (s, "on-line") == 0) : 0; - g_hash_table_destroy (hash); - } - apminfo->ac_line_status = ac_online ? 1 : 0; apminfo->battery_status = remain < low_capacity ? 1 : remain < critical_capacity ? 2 : 0; apminfo->battery_percentage = (int) (remain/(float)max_capacity*100); apminfo->battery_flags = charging ? 0x8 : 0; + apminfo->battery_time = seconds; return TRUE; } diff -urN orig/gnome-applets-2.0.4/battstat/battstat_applet.c gnome-applets-2.0.4/battstat/battstat_applet.c --- orig/gnome-applets-2.0.4/battstat/battstat_applet.c 2002-10-26 00:08:15.000000000 +0200 +++ gnome-applets-2.0.4/battstat/battstat_applet.c 2002-12-25 22:12:25.000000000 +0100 @@ -305,9 +305,11 @@ guint progress_value; guint pixmap_index; guint charging; + guint seconds; gint i, x; gboolean batterypresent; gchar new_label[80]; + gchar time_label[80]; gchar new_string[80]; gchar *status[]={ /* The following four messages will be displayed as tooltips over @@ -357,6 +359,7 @@ batt_state = apminfo.battery_status; batt_life = (guint) apminfo.battery_percentage; charging = (apminfo.battery_flags & 0x8) ? TRUE : FALSE; + seconds = apminfo.battery_time; #else acline_status = 1; batt_state = 0; @@ -460,6 +463,30 @@ } } + /* Re-calculate the remaining battery/charging time, and set the label */ + if (seconds>0 && (charging || !acline_status)) { + int hours, minutes; + + hours = seconds / 3600; + seconds -= 3600 * hours; + minutes = seconds / 60; + seconds -= 60 * minutes; + + if(charging) { + snprintf(time_label, sizeof(time_label), + "\n%s: %02d:%02d:%02d", + _("Time until fully charged"), + hours, minutes, seconds); + } else { + snprintf(time_label, sizeof(time_label), + "\n%s: %02d:%02d:%02d", + _("Remaining battery time"), + hours, minutes, seconds); + } + + strncat(new_label, time_label, sizeof(new_label)); + } + gtk_tooltips_set_tip (battery->ac_tip, battery->eventstatus, new_label, @@ -637,6 +664,10 @@ (_("Battery: Not present"))); } } + + if (seconds>0 && (charging || !acline_status)) { + strncat(new_string, time_label, sizeof(new_string)); + } gtk_tooltips_set_tip(battery->progress_tip, battery->eventbattery,