[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH] Remaining battery/charging time for Gnome battstat-applet



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 <bisente@bisente.com> | 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,

Reply to: