Subject: Re: measuring each battery's charge
To: Steve Bellovin <smb@research.att.com>
From: Simon Burge <simonb@wasabisystems.com>
List: netbsd-users
Date: 06/17/2003 00:11:08
Steve Bellovin wrote:

> Here's a hack to apm.c to let you specify which battery's charge you 
> want to ask about.

On a related note, here's a patch I did for xbattbar (in pkgsrc) a
couple of years ago (and had since forgotten about).  It adds a -m
option to show multiple batteries.

Standard Disclaimer: I am not an X programmer.

--
Simon Burge                            <simonb@wasabisystems.com>
NetBSD Support and Service:         http://www.wasabisystems.com/


Index: distinfo
===================================================================
RCS file: /cvsroot/pkgsrc/sysutils/xbattbar/distinfo,v
retrieving revision 1.3
diff -d -p -u -r1.3 distinfo
--- distinfo	2001/06/18 10:57:03	1.3
+++ distinfo	2001/09/10 11:00:33
@@ -3,3 +3,5 @@ $NetBSD: distinfo,v 1.3 2001/06/18 10:57
 SHA1 (xbattbar_1.4.2.tar.gz) = 14e9aafd62919e4a625d3f84df3b074b9eef8279
 Size (xbattbar_1.4.2.tar.gz) = 14079 bytes
 SHA1 (patch-aa) = 3bd4b688ccaeebe766335245c1a8c5dde52768c2
+SHA1 (patch-ab) = 7268d6051f2d0653698d76df19a5cce7f1bcbffa
+SHA1 (patch-ac) = c33f3f010dc4e70de0e945a77447fa75bcc57bbd
Index: patches/patch-ab
===================================================================
RCS file: patch-ab
diff -N patch-ab
--- /dev/null	Mon Sep 10 03:15:07 2001
+++ patch-ab	Mon Sep 10 14:00:33 2001
@@ -0,0 +1,395 @@
+$NetBSD$
+
+--- xbattbar.c.orig	Fri Feb  2 16:25:29 2001
++++ xbattbar.c	Mon Sep 10 20:52:15 2001
+@@ -49,12 +49,14 @@
+ #define DiagXMergin 20
+ #define DiagYMergin 5
+ 
++#define	MAX_BATTERIES	10
++
+ /*
+  * Global variables
+  */
+ 
+-int ac_line = -1;               /* AC line status */
+-int battery_level = -1;         /* battery level */
++int ac_line = -1;				/* AC line status */
++int battery_level[MAX_BATTERIES] = { -1 };	/* battery level */
+ 
+ unsigned long onin, onout;      /* indicator colors for AC online */
+ unsigned long offin, offout;    /* indicator colors for AC offline */
+@@ -68,6 +70,8 @@
+ char *OFFOUT_C = "red";
+ 
+ int alwaysontop = False;
++int multibattery = False;
++int num_batteries;
+ 
+ struct itimerval IntervalTimer;     /* APM polling interval timer */
+ 
+@@ -93,8 +97,7 @@
+ void InitDisplay(void);
+ Status AllocColor(char *, unsigned long *);
+ void battery_check(void);
+-void plug_proc(int);
+-void battery_proc(int);
++void show_proc(int *, unsigned long, unsigned long);
+ void redraw(void);
+ void showdiagbox(void);
+ void disposediagbox(void);
+@@ -102,6 +105,12 @@
+ void about_this_program(void);
+ void estimate_remain(void);
+ 
++#ifdef __NetBSD__
++int nbatteries(void);
++#else
++#define	nbatteries()	1
++#endif
++
+ /*
+  * usage of this command
+  */
+@@ -124,6 +133,7 @@
+     "-v, -h: show this message.\n"
+     "-t:     bar (indicator) thickness. [def: 3 pixels]\n"
+     "-p:     polling interval. [def: 10 sec.]\n"
++    "-m:     show multiple batteries (if supported).\n"
+     "-I, -O: bar colors in AC on-line. [def: \"green\" & \"olive drab\"]\n"
+     "-i, -o: bar colors in AC off-line. [def: \"blue\" and \"red\"]\n"
+     "top, bottom, left, right: bar localtion. [def: \"bottom\"]\n",
+@@ -180,26 +190,26 @@
+   switch (bi_direction) {
+   case BI_Top: /* (0,0) - (width, bi_thick) */
+     bi_width = width;
+-    bi_height = bi_thick;
++    bi_height = bi_thick * num_batteries;
+     bi_x = 0;
+     bi_y = 0;
+     break;
+   case BI_Bottom:
+     bi_width = width;
+-    bi_height = bi_thick;
++    bi_height = bi_thick * num_batteries;
+     bi_x = 0;
+-    bi_y = height - bi_thick;
++    bi_y = height - bi_thick * num_batteries;
+     break;
+   case BI_Left:
+-    bi_width = bi_thick;
++    bi_width = bi_thick * num_batteries;
+     bi_height = height;
+     bi_x = 0;
+     bi_y = 0;
+     break;
+   case BI_Right:
+-    bi_width = bi_thick;
++    bi_width = bi_thick * num_batteries;
+     bi_height = height;
+-    bi_x = width - bi_thick;
++    bi_x = width - bi_thick * num_batteries;
+     bi_y = 0;
+   }
+ 
+@@ -223,7 +233,7 @@
+   int ch;
+ 
+   about_this_program();
+-  while ((ch = getopt(argc, argv, "at:f:hI:i:O:o:p:v")) != -1)
++  while ((ch = getopt(argc, argv, "at:f:hI:i:mO:o:p:v")) != -1)
+     switch (ch) {
+     case 'a':
+       alwaysontop = True;
+@@ -247,6 +257,10 @@
+       OFFOUT_C = optarg;
+       break;
+ 
++    case 'm':
++      multibattery = True;
++      break;
++
+     case 'p':
+       bi_interval = atoi(optarg);
+       break;
+@@ -282,6 +296,11 @@
+     exit(1);
+   }
+ 
++  if (multibattery)
++    num_batteries = nbatteries() + 1;	/* 1 "bar" for overall status */
++  else
++    num_batteries = 1;
++
+   /*
+    * X Window main loop
+    */
+@@ -323,9 +342,9 @@
+ void redraw(void)
+ {
+   if (ac_line) {
+-    plug_proc(battery_level);
++    show_proc(battery_level, onin, onout);
+   } else {
+-    battery_proc(battery_level);
++    show_proc(battery_level, offin, offout);
+   }
+   estimate_remain();
+ }
+@@ -343,7 +362,7 @@
+   /* compose diag message and calculate its size in pixels */
+   sprintf(diagmsg,
+          "AC %s-line: battery level is %d%%",
+-         ac_line ? "on" : "off", battery_level);
++         ac_line ? "on" : "off", battery_level[0]);
+   fontp = XLoadQueryFont(disp, DefaultFont);
+   pixw = XTextWidth(fontp, diagmsg, strlen(diagmsg));
+   pixh = fontp->ascent + fontp->descent;
+@@ -377,46 +396,34 @@
+   }
+ }
+ 
+-void battery_proc(int left)
+-{
+-  int pos;
+-  if (BI_Horizontal) {
+-    pos = width * left / 100;
+-    XSetForeground(disp, gcbar, offin);
+-    XFillRectangle(disp, winbar, gcbar, 0, 0, pos, bi_thick);
+-    XSetForeground(disp, gcbar, offout);
+-    XFillRectangle(disp, winbar, gcbar, pos, 0, width, bi_thick);
+-  } else {
+-    pos = height * left / 100;
+-    XSetForeground(disp, gcbar, offin);
+-    XFillRectangle(disp, winbar, gcbar, 0, height-pos, bi_thick, height);
+-    XSetForeground(disp, gcbar, offout);
+-    XFillRectangle(disp, winbar, gcbar, 0, 0, bi_thick, height-pos);
+-  }
+-  XFlush(disp);
+-}
+-
+-void plug_proc(int left)
++void show_proc(int *left, unsigned long in, unsigned long out)
+ {
+-  int pos;
++  int i, pos;
+ 
+   if (BI_Horizontal) {
+-    pos = width * left / 100;
+-    XSetForeground(disp, gcbar, onin);
+-    XFillRectangle(disp, winbar, gcbar, 0, 0, pos, bi_thick);
+-    XSetForeground(disp, gcbar, onout);
+-    XFillRectangle(disp, winbar, gcbar, pos+1, 0, width, bi_thick);
++    for (i = 0; i < num_batteries; i++) {
++      pos = width * left[i] / 100;
++      XSetForeground(disp, gcbar, in);
++      XFillRectangle(disp, winbar, gcbar,
++	  0, i * bi_thick, pos, bi_thick);
++      XSetForeground(disp, gcbar, out);
++      XFillRectangle(disp, winbar, gcbar,
++	  pos, i * bi_thick, width, bi_thick);
++    }
+   } else {
+-    pos = height * left / 100;
+-    XSetForeground(disp, gcbar, onin);
+-    XFillRectangle(disp, winbar, gcbar, 0, height-pos, bi_thick, height);
+-    XSetForeground(disp, gcbar, onout);
+-    XFillRectangle(disp, winbar, gcbar, 0, 0, bi_thick, height-pos);
++    for (i = 0; i < num_batteries; i++) {
++      pos = height * left[i] / 100;
++      XSetForeground(disp, gcbar, in);
++      XFillRectangle(disp, winbar, gcbar,
++	  i * bi_thick, height-pos, bi_thick, height);
++      XSetForeground(disp, gcbar, out);
++      XFillRectangle(disp, winbar, gcbar,
++	  i * bi_thick, 0, bi_thick, height-pos);
++    }
+   }
+   XFlush(disp);
+ }
+ 
+-
+ /*
+  * estimating time for battery remaining / charging 
+  */
+@@ -431,33 +438,33 @@
+ 
+   /* static value initialize */
+   if (battery_base == -1) {
+-    battery_base = battery_level;
++    battery_base = battery_level[0];
+     return;
+   }
+ 
+-  diff = battery_base - battery_level;
++  diff = battery_base - battery_level[0];
+ 
+   if (diff == 0) return;
+ 
+   /* estimated time for battery remains */
+   if (diff > 0) {
+-    remain = elapsed_time * (battery_level - CriticalLevel) / diff ;
++    remain = elapsed_time * (battery_level[0] - CriticalLevel) / diff ;
+     remain = remain * bi_interval;  /* in sec */
+     if (remain < 0 ) remain = 0;
+     printf("battery remain: %2d hr. %2d min. %2d sec.\n",
+ 	   remain / 3600, (remain % 3600) / 60, remain % 60);
+     elapsed_time = 0;
+-    battery_base = battery_level;
++    battery_base = battery_level[0];
+     return;
+   }
+ 
+   /* estimated time of battery charging */
+-  remain = elapsed_time * (battery_level - 100) / diff;
++  remain = elapsed_time * (battery_level[0] - 100) / diff;
+   remain = remain * bi_interval;  /* in sec */
+   printf("charging remain: %2d hr. %2d min. %2d sec.\n",
+ 	 remain / 3600, (remain % 3600) / 60, remain % 60);
+   elapsed_time = 0;
+-  battery_base = battery_level;
++  battery_base = battery_level[0];
+ }
+ 
+ 
+@@ -488,10 +495,10 @@
+   close (fd);
+ 
+   if (first || ac_line != ((ar.bret >> 8) & 0xff)
+-      || battery_level != (ar.cret&0xff)) {
++      || battery_level[0] != (ar.cret&0xff)) {
+     first = 0;
+     ac_line = (ar.bret >> 8) & 0xff;
+-    battery_level = ar.cret&0xff;
++    battery_level[0] = ar.cret&0xff;
+     redraw();
+   }
+   signal(SIGALRM, (void *)(battery_check));
+@@ -564,10 +571,10 @@
+     p = APM_STAT_LINE_OFF;
+   }
+ 
+-  if (first || ac_line != p || battery_level != r) {
++  if (first || ac_line != p || battery_level[0] != r) {
+     first = 0;
+     ac_line = p;
+-    battery_level = r;
++    battery_level[0] = r;
+     redraw();
+   }
+   signal(SIGALRM, (void *)(battery_check));
+@@ -583,10 +590,10 @@
+ #define _PATH_APM_CTLDEV       "/dev/apmctl"
+ #define _PATH_APM_NORMAL       "/dev/apm"
+ 
+-int first = 1;
+-void battery_check(void)
++int
++nbatteries(void)
+ {
+-       int fd, r, p;
++       int fd, i, p, r[MAX_BATTERIES];
+        struct apm_power_info info;
+ 
+        if ((fd = open(_PATH_APM_NORMAL, O_RDONLY)) == -1) {
+@@ -594,23 +601,66 @@
+                exit(1);
+        }
+ 
++       memset(&info, 0, sizeof(info));
++
+        if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
+                fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n");
+                exit(1);
+        }
+ 
+-       close(fd);
++       return (info.nbattery);
++}
+ 
+-       ++elapsed_time;
++int first = 1;
++void battery_check(void)
++{
++       int fd, i, p, r[MAX_BATTERIES];
++       struct apm_power_info info;
+ 
+-       /* get current remoain */
++       if ((fd = open(_PATH_APM_NORMAL, O_RDONLY)) == -1) {
++               fprintf(stderr, "xbattbar: cannot open apm device\n");
++               exit(1);
++       }
++
++       memset(&info, 0, sizeof(info));
++
++       if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
++               fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n");
++               exit(1);
++       }
++
++       /* get current remaining */
+        if (info.battery_life > 100) {
+                /* some APM BIOSes return values slightly > 100 */
+-               r = 100;
++               r[0] = 100;
+        } else {
+-               r = info.battery_life;
++               r[0] = info.battery_life;
++       }
++
++       if (info.nbattery > 1) {
++	       for (i = 1; i <= info.nbattery; i++) {
++		       if (i == MAX_BATTERIES)
++			       break;
++		       info.batteryid = i;
++		       if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
++			       fprintf(stderr,
++				   "xbattbar: ioctl APM_IOC_GETPOWER (%d)failed\n", i);
++			       exit(1);
++		       }
++		       /* get current remaining */
++		       if (info.battery_life > 100) {
++			       /* some APM BIOSes return values slightly >100 */
++			       r[i] = 100;
++		       } else {
++			       r[i] = info.battery_life;
++		       }
++	       }
+        }
+ 
++       close(fd);
++
++       ++elapsed_time;
++
+        /* get AC-line status */
+        if (info.ac_state == APM_AC_ON) {
+                p = APM_AC_ON;
+@@ -618,10 +668,13 @@
+                p = APM_AC_OFF;
+        }
+ 
+-       if (first || ac_line != p || battery_level != r) {
++       if (first || ac_line != p || 
++	   /* compare all battery levels at once */
++           memcmp(battery_level, r, sizeof(int) * num_batteries)) {
+                first = 0;
+                ac_line = p;
+-               battery_level = r;
++	       for (i = 0; i < num_batteries; i++)
++                 battery_level[i] = r[i];
+                redraw();
+        }
+ }
+@@ -699,10 +752,10 @@
+      p = APM_STAT_LINE_OFF;
+    }
+ 
+-  if (first || ac_line != p || battery_level != r) {
++  if (first || ac_line != p || battery_level[0] != r) {
+     first = 0;
+     ac_line = p;
+-    battery_level = r;
++    battery_level[0] = r;
+     redraw();
+   }
+   signal(SIGALRM, (void *)(battery_check));
Index: patches/patch-ac
===================================================================
RCS file: patch-ac
diff -N patch-ac
--- /dev/null	Mon Sep 10 03:15:07 2001
+++ patch-ac	Mon Sep 10 14:00:34 2001
@@ -0,0 +1,57 @@
+$NetBSD$
+
+--- xbattbar.man.orig	Fri Feb  2 16:25:29 2001
++++ xbattbar.man	Mon Sep 10 20:56:09 2001
+@@ -25,7 +25,7 @@
+ .Nd show battery status in X Window
+ .Sh SYNOPSIS
+ .Nm xbattbar
+-.Op Fl a 
++.Op Fl am
+ .Op Fl t Ar thickness
+ .Op Fl p Ar interval
+ .Op Fl I Ar color
+@@ -42,10 +42,17 @@
+ .Pp
+ .Nm xbattbar
+ shows its battery status in a simple bar indicator.
++The
+ .Nm -a
+ option makes the indicator window keep always on top of your screen.
++The
++.Nm -m
++option uses separate lines for each battery, with the left-most
++or top-most line being a summary of all installed batteries.
++Note that not all operating systems support this option.
+ The thickness of the indicator is 3 pixels in default and
+ you can set the thickness as a parameter of 
++the
+ .Nm -t
+ option.
+ This indicator is appeared in the bottom of the display, as its default.
+@@ -65,7 +72,7 @@
+ its color of the bar indicator consists of "green" and "olive drab"
+ portions.
+ The "green" portion shows its battery charging level.
+-These colors can be changed by
++These colors can be changed by the
+ .Nm -I
+ and
+ .Nm -O
+@@ -73,7 +80,7 @@
+ In the other case (AC line is off-line),
+ the "blue" portion of the bar indicator shows the percentage of
+ its remaining battery level.
+-The color conbination for the AC off-line case can be changed by
++The color conbination for the AC off-line case can be changed by the
+ .Nm -i
+ and
+ .Nm -o
+@@ -82,6 +89,7 @@
+ .Nm xbattbar
+ trys to know its battery status in every 10 seconds in default.
+ This is achived by APM polling.
++The
+ .Nm -p
+ option sets the polling interval in second.
+ .Pp