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