wmacpi: Bump to version 1.99r4.
[dockapps.git] / wmacpi / wmacpi.c
blobc9b384d168be01675fa633b224d76a815d60d15f
1 /* apm/acpi dockapp - phear it 1.34
2 * Copyright (C) 2000, 2001, 2002 timecop@japan.co.jp
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <getopt.h>
26 #include <unistd.h>
27 #include <time.h>
29 #include <X11/X.h>
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <X11/extensions/shape.h>
33 #include <X11/xpm.h>
35 #include "libacpi.h"
36 #include "wmacpi.h"
38 #define WMACPI_VER "1.99r2"
40 /* main pixmap */
41 #ifdef LOW_COLOR
42 #include "master_low.xpm"
43 #else
44 #include "master.xpm"
45 #endif
47 typedef struct {
48 Display *display; /* X11 display struct */
49 int screen; /* current screen */
50 Window root; /* root window */
51 Window win; /* one window */
52 Window iconwin; /* another one */
53 Pixmap pixmap; /* UI pixmap, window pixmap */
54 Pixmap mask; /* mask pixmap for shape */
55 GC gc; /* main drawing GC */
56 Pixmap text; /* pixmap for text scroller */
57 int tw; /* text width inside text pixmap */
58 int update; /* need to redraw? */
59 int blink; /* should we blink the LED? (critical battery) */
60 int bell; /* bell on critical low, or not? */
61 int scroll; /* scroll message text? */
62 } Dockapp;
64 /* globals */
65 Dockapp *dockapp;
66 global_t *globals;
67 int count = 0; /* global timer variable */
68 /* extern int verbosity; */
70 /* Time for scroll updates */
71 #define DEFAULT_UPDATE 150
72 static int update_timeout = DEFAULT_UPDATE;
74 /* proto for local stuff */
75 static void new_window(char *name);
76 static int open_display(char *display);
77 static void redraw_window(void);
78 static void render_text(char *string);
79 static void scroll_text(int x, int y, int width, int tw, int reset);
80 static void display_percentage(int percent);
81 static void display_time(int minutes);
83 #define copy_xpm_area(x, y, w, h, dx, dy) \
84 { \
85 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->pixmap, \
86 dockapp->gc, x, y, w, h, dx, dy); \
87 dockapp->update = 1; \
90 /* display AC power symbol */
91 static void display_power_glyph(void)
93 copy_xpm_area(67, 38, 12, 7, 6, 17);
96 /* get rid of AC power symbol */
97 static void kill_power_glyph(void)
99 copy_xpm_area(67, 48, 12, 7, 6, 17);
102 /* display battery symbol */
103 static void display_battery_glyph(void)
105 copy_xpm_area(82, 38, 12, 7, 20, 17);
108 /* get rid of battery symbol */
109 static void kill_battery_glyph(void)
111 copy_xpm_area(82, 48, 12, 7, 20, 17);
114 /* clear the time display */
115 static void clear_time_display(void)
117 copy_xpm_area(114, 76, 31, 11, 7, 32);
120 /* set time display to -- -- */
121 static void invalid_time_display(void)
123 copy_xpm_area(122, 13, 31, 11, 7, 32);
126 static void redraw_window(void)
128 if (dockapp->update) {
129 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->iconwin,
130 dockapp->gc, 0, 0, 64, 64, 0, 0);
131 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->win,
132 dockapp->gc, 0, 0, 64, 64, 0, 0);
133 dockapp->update = 0;
137 static void new_window(char *name)
139 XpmAttributes attr;
140 Pixel fg, bg;
141 XGCValues gcval;
142 XSizeHints sizehints;
143 XClassHint classhint;
144 XWMHints wmhints;
146 dockapp->screen = DefaultScreen(dockapp->display);
147 dockapp->root = DefaultRootWindow(dockapp->display);
149 sizehints.flags = USSize | USPosition;
150 sizehints.width = 64;
151 sizehints.height = 64;
153 fg = BlackPixel(dockapp->display, dockapp->screen);
154 bg = WhitePixel(dockapp->display, dockapp->screen);
156 dockapp->win = XCreateSimpleWindow(dockapp->display, dockapp->root,
157 0, 0, sizehints.width,
158 sizehints.height, 1, fg, bg);
159 dockapp->iconwin =
160 XCreateSimpleWindow(dockapp->display, dockapp->win, 0, 0,
161 sizehints.width, sizehints.height, 1, fg, bg);
163 XSetWMNormalHints(dockapp->display, dockapp->win, &sizehints);
164 classhint.res_name = name;
165 classhint.res_class = name;
166 XSetClassHint(dockapp->display, dockapp->win, &classhint);
168 XSelectInput(dockapp->display, dockapp->win,
169 ExposureMask | ButtonPressMask | ButtonReleaseMask |
170 StructureNotifyMask);
171 XSelectInput(dockapp->display, dockapp->iconwin,
172 ExposureMask | ButtonPressMask | ButtonReleaseMask |
173 StructureNotifyMask);
175 XStoreName(dockapp->display, dockapp->win, name);
176 XSetIconName(dockapp->display, dockapp->win, name);
178 gcval.foreground = fg;
179 gcval.background = bg;
180 gcval.graphics_exposures = False;
182 dockapp->gc =
183 XCreateGC(dockapp->display, dockapp->win,
184 GCForeground | GCBackground | GCGraphicsExposures,
185 &gcval);
187 attr.exactColors = 0;
188 attr.alloc_close_colors = 1;
189 attr.closeness = 1L << 15;
190 attr.valuemask = XpmExactColors | XpmAllocCloseColors | XpmCloseness;
191 if (XpmCreatePixmapFromData(dockapp->display, dockapp->win,
192 master_xpm, &dockapp->pixmap,
193 &dockapp->mask, &attr) != XpmSuccess) {
194 pfatal("FATAL: Not enough colors for main pixmap!\n");
195 exit(1);
198 /* text area is 318x7, or 53 characters long */
199 dockapp->text = XCreatePixmap(dockapp->display, dockapp->win, 318, 7,
200 DefaultDepth(dockapp->display,
201 dockapp->screen));
202 if (!dockapp->text) {
203 pfatal("FATAL: Cannot create text scroll pixmap!\n");
204 exit(1);
207 XShapeCombineMask(dockapp->display, dockapp->win, ShapeBounding, 0, 0,
208 dockapp->mask, ShapeSet);
209 XShapeCombineMask(dockapp->display, dockapp->iconwin, ShapeBounding, 0,
210 0, dockapp->mask, ShapeSet);
212 wmhints.initial_state = WithdrawnState;
213 wmhints.flags = StateHint;
214 wmhints.icon_window = dockapp->iconwin;
215 wmhints.icon_x = sizehints.x;
216 wmhints.icon_y = sizehints.y;
217 wmhints.window_group = dockapp->win;
218 wmhints.flags =
219 StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
220 XSetWMHints(dockapp->display, dockapp->win, &wmhints);
222 XMapWindow(dockapp->display, dockapp->win);
225 static void render_text(char *string)
227 int i, c, k;
229 /* drop out immediately if scrolling is disabled - we don't render
230 * any text at all, since there's not much else we could do
231 * sensibly without scrolling. */
232 if (!dockapp->scroll)
233 return;
235 if (strlen(string) > 53)
236 return;
238 /* prepare the text area by clearing it */
239 for (i = 0; i < 54; i++) {
240 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
241 dockapp->gc, 133, 57, 6, 8, i * 6, 0);
243 k = 0;
245 for (i = 0; string[i]; i++) {
246 c = toupper(string[i]);
247 if (c >= 'A' && c <= 'Z') { /* letter */
248 c = c - 'A';
249 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
250 dockapp->gc, c * 6, 67, 6, 7, k, 0);
251 } else if (c >= '0' && c <= '9') { /* number */
252 c = c - '0';
253 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
254 dockapp->gc, c * 6 + 66, 58, 6, 7, k, 0);
255 } else if (c == '.') {
256 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
257 dockapp->gc, 140, 58, 6, 7, k, 0);
258 } else if (c == '-') {
259 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
260 dockapp->gc, 126, 58, 6, 7, k, 0);
262 k += 6;
264 dockapp->tw = k; /* length of text segment */
265 /* re-scroll the message */
266 scroll_text(6, 50, 52, dockapp->tw, 1);
267 /* reset the scroll repeat counter */
268 count = 0;
271 static int open_display(char *display)
273 dockapp->display = XOpenDisplay(display);
274 if (!dockapp->display) {
275 perr("Unable to open display '%s'\n", display);
276 return 1;
278 return 0;
281 static void scroll_text(int x, int y, int width, int tw, int reset)
283 static int pos, first, stop;
285 if (!dockapp->scroll)
286 return;
288 if (reset) {
289 pos = 0;
290 first = 0;
291 stop = 0;
292 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
293 dockapp->gc, 0, 0, width, 7, x, y);
296 if (stop) {
297 return;
300 if ((first == 0) && pos == 0) {
301 pos = width;
302 first = 1;
305 if (pos == (0 - tw - 2)) {
306 first = 1;
307 pos = width;
308 stop = 1;
309 return;
311 pos -= 2;
313 if (pos > 0) {
314 copy_xpm_area(66, 9, pos, 7, x, y); /* clear */
315 XCopyArea(dockapp->display, dockapp->text, dockapp->pixmap,
316 dockapp->gc, 0, 0, width - pos, 7, x + pos, y);
317 } else { /* don't need to clear, already in text */
318 XCopyArea(dockapp->display, dockapp->text, dockapp->pixmap,
319 dockapp->gc, abs(pos), 0, width, 7, x, y);
321 dockapp->update = 1;
324 static void display_percentage(int percent)
326 static int op = -1;
327 static unsigned int obar;
328 unsigned int bar;
330 if (percent == -1)
331 percent = 0;
333 if (op == percent)
334 return;
336 if (percent < 0)
337 percent = 0;
338 if (percent > 100)
339 percent = 100;
341 if (percent < 100) { /* 0 - 99 */
342 copy_xpm_area(95, 48, 8, 7, 37, 17);
343 if (percent >= 10)
344 copy_xpm_area((percent / 10) * 6 + 67, 28, 5, 7, 40, 17);
345 copy_xpm_area((percent % 10) * 6 + 67, 28, 5, 7, 46, 17);
346 } else
347 copy_xpm_area(95, 37, 21, 9, 37, 16); /* 100% */
348 op = percent;
350 bar = percent / 1.8518;
352 if (bar == obar)
353 return;
355 copy_xpm_area(66, 0, bar, 8, 5, 5);
356 if (bar < 54)
357 copy_xpm_area(66 + bar, 18, 54 - bar, 8, bar + 5, 5);
358 obar = bar;
361 static void display_time(int minutes)
363 static int ohour = -1, omin = -1;
364 int hour, min, tmp;
366 if (minutes <= 0) { /* error - clear the display */
367 invalid_time_display();
368 ohour = omin = -1;
369 return;
372 /* render time on the display */
373 hour = minutes / 60;
374 /* our display area only fits %2d:%2d, so we need to make sure
375 * what we're displaying will fit in those constraints. I don't
376 * think we're likely to see any batteries that do more than
377 * 100 hours any time soon, so it's fairly safe. */
378 if (hour >= 100) {
379 hour = 99;
380 min = 59;
381 } else
382 min = minutes % 60;
384 if (hour == ohour && min == omin)
385 return;
387 tmp = hour / 10;
388 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 7, 32);
389 tmp = hour % 10;
390 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 14, 32);
391 tmp = min / 10;
392 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 25, 32);
393 tmp = min % 10;
394 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 32, 32);
395 copy_xpm_area(71, 76, 3, 11, 21, 32);
396 ohour = hour;
397 omin = min;
401 * The reworked state handling stuff.
404 /* set the current state of the power panel */
405 enum panel_states {
406 PS_AC,
407 PS_BATT,
408 PS_NULL,
411 static void really_blink_power_glyph(void)
413 static int counter = 0;
415 if (counter == 10)
416 display_power_glyph();
417 else if (counter == 20)
418 kill_power_glyph();
419 else if (counter > 30)
420 counter = 0;
421 counter++;
424 static void blink_power_glyph(void)
426 if (dockapp->blink)
427 really_blink_power_glyph();
430 static void really_blink_battery_glyph(void)
432 static int counter = 0;
434 if (counter == 10)
435 display_battery_glyph();
436 else if (counter == 20)
437 kill_battery_glyph();
438 else if (counter > 30)
439 counter = 0;
440 counter++;
443 static void blink_battery_glyph(void)
445 if (dockapp->blink)
446 really_blink_battery_glyph();
449 static void set_power_panel(void)
451 enum panel_states power = PS_NULL;
452 battery_t *binfo = globals->binfo;
453 adapter_t *ap = &globals->adapter;
455 if (ap->power == AC) {
456 if (power != PS_AC) {
457 power = PS_AC;
458 kill_battery_glyph();
459 display_power_glyph();
461 } else if (ap->power == BATT) {
462 if (power != PS_BATT) {
463 power = PS_BATT;
464 kill_power_glyph();
465 display_battery_glyph();
469 if (binfo->charge_state == CHARGE)
470 blink_power_glyph();
472 if (binfo->state == CRIT)
473 blink_battery_glyph();
475 if (binfo->state == HARD_CRIT) {
476 really_blink_battery_glyph();
477 /* we only do this here because it'd be obnoxious to
478 * do it anywhere else. */
479 if (dockapp->bell) {
480 XBell(dockapp->display, 100);
486 * The message that needs to be displayed needs to be decided
487 * according to a heirarchy: a message like not present needs to take
488 * precedence over a global thing like the current power status, and
489 * something like a low battery warning should take precedence over
490 * the "on battery" message. Likewise, a battery charging message
491 * needs to take precedence over the on ac power message. The other
492 * question is how much of a precedence local messages should take
493 * over global ones . . .
495 * So, there are three possible sets of messages: not present, on-line
496 * and off-line messages. We need to decide which of those sets is
497 * appropriate right now, and then decide within them.
499 enum messages {
500 M_NP, /* not present */
501 M_AC, /* on ac power */
502 M_CH, /* battery charging */
503 M_BATT, /* on battery */
504 M_LB, /* low battery */
505 M_CB, /* critical low battery */
506 M_HCB, /* battery reported critical capacity state */
507 M_NULL, /* empty starting state */
510 static void set_message(void)
512 static enum messages state = M_NULL;
513 battery_t *binfo = globals->binfo;
514 adapter_t *ap = &globals->adapter;
516 /* battery not present case */
517 if (!binfo->present) {
518 if (state != M_NP) {
519 state = M_NP;
520 render_text("not present");
522 } else if (ap->power == AC) {
523 if (binfo->charge_state == CHARGE) {
524 if (state != M_CH) {
525 state = M_CH;
526 update_timeout = DEFAULT_UPDATE;
527 render_text("battery charging");
529 } else {
530 if (state != M_AC) {
531 state = M_AC;
532 update_timeout = DEFAULT_UPDATE;
533 render_text("on ac power");
536 } else {
537 if (binfo->state == CRIT) {
538 if (state != M_CB) {
539 state = M_CB;
540 update_timeout = 80;
541 render_text("critical low battery");
543 } else if (binfo->state == HARD_CRIT) {
544 if (state != M_HCB) {
545 state = M_HCB;
546 update_timeout = 60;
547 render_text("hard critical low battery");
549 } else if (binfo->state == LOW) {
550 if (state != M_LB) {
551 state = M_LB;
552 update_timeout = 100;
553 render_text("low battery");
555 } else {
556 if (state != M_BATT) {
557 state = M_BATT;
558 update_timeout = DEFAULT_UPDATE;
559 render_text("on battery");
565 void set_time_display(void)
567 battery_t *binfo = &batteries[battery_no];
569 if (binfo->charge_state == CHARGE)
570 display_time(binfo->charge_time);
571 else if (binfo->charge_state == DISCHARGE)
572 display_time(globals->rtime);
573 else
574 invalid_time_display();
578 * This should really be fixed so that it can handle more than two batteries.
581 void set_id_1(void)
583 copy_xpm_area(118, 38, 15, 15, 44, 30);
586 void set_id_2(void)
588 copy_xpm_area(136, 38, 15, 15, 44, 30);
591 void set_batt_id_area(int bno)
593 switch(bno) {
594 case 0:
595 set_id_1();
596 break;
597 case 1:
598 set_id_2();
599 break;
603 void usage(char *name)
605 printf("%s - help\t\t[simon@dreamcraft.com.au]\n\n"
606 "-d display\t\tdisplay on remote display <display>\n"
607 "-b\t\t\tmake noise when battery is critical low (beep)\n"
608 "-r\t\t\tdisable scrolling message\n"
609 "-c value\t\tset critical low alarm at <value> percent\n"
610 "\t\t\t(default: 10 percent)\n"
611 "-m <battery number>\tbattery number to monitor\n"
612 "-s <sample rate>\trate at which to sample battery status\n"
613 "\t\t\tdefault 100 (once every three seconds)\n"
614 "-n\t\t\tdo not blink\n"
615 "-w\t\t\trun in command line mode\n"
616 "-a <samples>\t\tsamples to average over (cli mode only)\n"
617 "-v\t\t\tincrease verbosity\n"
618 "\t\t\tcan be used multiple times to increase verbosity further\n"
619 "-h\t\t\tdisplay this help\n",
620 name);
623 void print_version(void)
625 printf("wmacpi version %s\n", WMACPI_VER);
626 printf(" Using libacpi version %s\n", LIBACPI_VER);
629 void cli_wmacpi(int samples)
631 int i, j, sleep_time = 0;
632 battery_t *binfo;
633 adapter_t *ap;
635 printf("%d\n", samples);
636 if(samples > 1)
637 sleep_time = 1000000/samples;
639 /* we want to acquire samples over some period of time, so . . . */
640 for(i = 0; i < samples + 2; i++) {
641 for(j = 0; j < batt_count; j++)
642 acquire_batt_info(j);
643 acquire_global_info();
644 usleep(sleep_time);
647 ap = &globals->adapter;
648 if(ap->power == AC) {
649 printf("On AC Power");
650 for(i = 0; i < batt_count; i++) {
651 binfo = &batteries[i];
652 if(binfo->present && (binfo->charge_state == CHARGE)) {
653 printf("; Battery %s charging", binfo->name);
654 printf(", currently at %2d%%", binfo->percentage);
655 if(binfo->charge_time >= 0)
656 printf(", %2d:%02d remaining",
657 binfo->charge_time/60,
658 binfo->charge_time%60);
661 printf("\n");
662 } else if(ap->power == BATT) {
663 printf("On Battery");
664 for(i = 0; i < batt_count; i++) {
665 binfo = &batteries[i];
666 if(binfo->present && (binfo->percentage >= 0))
667 printf(", Battery %s at %d%%", binfo->name,
668 binfo->percentage);
670 if(globals->rtime >= 0)
671 printf("; %d:%02d remaining", globals->rtime/60,
672 globals->rtime%60);
673 printf("\n");
675 return;
678 int main(int argc, char **argv)
680 char *display = NULL;
681 int ch;
682 int update = 0;
683 int cli = 0, samples = 1;
684 int samplerate = 100;
685 battery_t *binfo;
687 dockapp = calloc(1, sizeof(Dockapp));
688 globals = calloc(1, sizeof(global_t));
690 dockapp->blink = 1;
691 dockapp->bell = 0;
692 dockapp->scroll = 1;
693 globals->crit_level = 10;
694 battery_no = 1;
696 /* parse command-line options */
697 while ((ch = getopt(argc, argv, "d:c:m:s:a:hnwbrvV")) != EOF) {
698 switch (ch) {
699 case 'c':
700 if (optarg) {
701 globals->crit_level = atoi(optarg);
702 if ((globals->crit_level < 0) || (globals->crit_level > 100)) {
703 fprintf(stderr, "Please use values between 0 and 100%%\n");
704 globals->crit_level = 10;
705 fprintf(stderr, "Using default value of 10%%\n");
708 break;
709 case 'd':
710 if (optarg)
711 display = strdup(optarg);
712 break;
713 case 'm':
714 if (optarg) {
715 battery_no = atoi(optarg);
716 if (battery_no >= MAXBATT) {
717 fprintf(stderr, "Please specify a battery number below %d\n",
718 MAXBATT);
719 return 1;
721 if (battery_no > batt_count) {
722 fprintf(stderr, "Battery %d does not appear to be installed\n",
723 battery_no);
724 return 1;
726 fprintf(stderr, "Monitoring battery %d\n", battery_no);
728 break;
729 case 's':
730 if (optarg) {
731 samplerate = atoi(optarg);
732 if (samplerate == 0) samplerate = 1;
733 if (samplerate > 3000) samplerate = 3000;
734 } else {
735 usage(argv[0]);
736 exit(1);
738 break;
739 case 'h':
740 usage(argv[0]);
741 return 0;
742 case 'v':
743 verbosity++;
744 break;
745 case 'V':
746 print_version();
747 return 0;
748 case 'n':
749 dockapp->blink = 0;
750 break;
751 case 'w':
752 cli = 1;
753 break;
754 case 'a':
755 if(optarg != NULL) {
756 samples = atoi(optarg);
757 if(samples > 1000 || samples <= 0) {
758 fprintf(stderr, "Please specify a reasonable number of samples\n");
759 exit(1);
762 break;
763 case 'b':
764 dockapp->blink = 1;
765 break;
766 case 'r':
767 printf("disabling scroll\n");
768 dockapp->scroll = 0;
769 break;
770 default:
771 usage(argv[0]);
772 return 1;
777 /* see if whatever we want to use is supported */
778 if (power_init())
779 /* power_init functions handle printing error messages */
780 exit(1);
782 /* check for cli mode */
783 if (cli) {
784 cli_wmacpi(samples);
785 exit(0);
788 battery_no--;
790 /* open local or command-line specified display */
791 if (open_display(display))
792 exit(1);
794 /* make new dockapp window */
795 /* Don't even /think/ of asking me why, but if I set the window name to
796 * "acpi", the app refuses to dock properly - it's just plain /weird/.
797 * So, wmacpi it is . . . */
798 new_window("wmacpi");
800 /* get initial statistics */
801 acquire_all_info();
802 binfo = &batteries[battery_no];
803 globals->binfo = binfo;
804 pinfo("monitoring battery %s\n", binfo->name);
805 clear_time_display();
806 set_power_panel();
807 set_message();
808 set_batt_id_area(battery_no);
810 /* main loop */
811 while (1) {
812 XEvent event;
813 while (XPending(dockapp->display)) {
814 XNextEvent(dockapp->display, &event);
815 switch (event.type) {
816 case Expose:
817 /* update */
818 dockapp->update = 1;
819 while (XCheckTypedEvent(dockapp->display, Expose, &event));
820 redraw_window();
821 break;
822 case DestroyNotify:
823 XCloseDisplay(dockapp->display);
824 exit(0);
825 break;
826 case ButtonPress:
827 break;
828 case ButtonRelease:
829 /* cycle through the known batteries. */
830 battery_no++;
831 battery_no = battery_no % batt_count;
832 globals->binfo = &batteries[battery_no];
833 binfo = globals->binfo;
834 pinfo("changing to monitor battery %d\n", battery_no + 1);
835 set_batt_id_area(battery_no);
836 break;
840 /* XXX: some laptops have problems with sampling the battery
841 * regularly - apparently, the BIOS disables interrupts while
842 * reading from the battery, which is generally on a slow bus
843 * and is a slow device, so you get significant periods without
844 * interrupts. This causes interactivity to suffer . . .
846 * My proposed workaround is to allow the user to set the sample
847 * rate - it defaults to ten, but can be set lower (or higher).
849 * The only problem with this is that we need to sample less
850 * frequently, while still allowing the app to update normally.
851 * That means calling redraw_window() and all the set_*() functions
852 * normally, but only calling acquire_all_info() every so often.
853 * As it stands, we only call acquire_all_info() once every three
854 * seconds (once every thirty updates) . . . I'm not entirely sure
855 * /how/ this could cause interactivity problems, but hey . . .
857 * So, given the base rate of once every three seconds, we want to
858 * change this test to . . . */
859 if (update++ == (3000/samplerate)) {
860 acquire_all_info();
861 update = 0;
864 if (count++ == update_timeout) {
865 scroll_text(6, 50, 52, dockapp->tw, 1);
866 count = 0;
869 /* the old code had some kind of weird crap with timers and the like.
870 * As far as I can tell, it's meaningless - the time we want to display
871 * is the time calculated from the remaining capacity, as per the
872 * ACPI spec. The only thing I'd change is the handling of a charging
873 * state: my best guess, based on the behaviour I'm seeing with my
874 * Lifebook, is that the present rate value when charging is the rate
875 * at which the batteries are being charged, which would mean I'd just
876 * need to reverse the rtime calculation to be able to work out how
877 * much time remained until the batteries were fully charged . . .
878 * That would be rather useful, though given it would vary rather a lot
879 * it seems likely that it'd be little more than a rough guesstimate. */
880 set_time_display();
881 set_power_panel();
882 set_message();
883 display_percentage(binfo->percentage);
884 scroll_text(6, 50, 52, dockapp->tw, 0);
886 /* redraw_window, if anything changed - determined inside
887 * redraw_window. */
888 redraw_window();
889 usleep(100000);
891 return 0;