wmacpi: Bump to version 2.2rc3.
[dockapps.git] / wmacpi / wmacpi.c
blob815cfb1760e56b5cc9f404715288340ad54d7706
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 <dockapp.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <unistd.h>
28 #include <time.h>
30 #include <X11/X.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/extensions/shape.h>
34 #include <X11/xpm.h>
36 #include "libacpi.h"
37 #include "wmacpi.h"
39 #define WMACPI_VER "2.2rc3"
41 /* main pixmap */
42 #ifdef LOW_COLOR
43 #include "master_low.xpm"
44 static char **master_xpm = master_low_xpm;
45 #else
46 #include "master.xpm"
47 #endif
49 struct dockapp {
50 Display *display; /* display */
51 Window win; /* main window */
52 Pixmap pixmap; /* main pixmap */
53 Pixmap mask; /* mask pixmap */
54 Pixmap text; /* pixmap for text scroller */
55 unsigned short width; /* width of pixmap */
56 unsigned short height; /* height of pixmap */
57 int screen; /* current screen */
58 int tw; /* text width inside text pixmap */
59 int update; /* need to redraw? */
60 int blink; /* should we blink the LED? (critical battery) */
61 int bell; /* bell on critical low, or not? */
62 int scroll; /* scroll message text? */
63 int scroll_reset; /* reset the scrolling text */
64 int percent;
67 /* globals */
68 struct dockapp *dockapp;
69 /* global_t *globals; */
71 /* this gives us a variable scroll rate, depending on the importance of the
72 * message . . . */
73 #define DEFAULT_SCROLL_RESET 150;
74 int scroll_reset = DEFAULT_SCROLL_RESET;
76 /* copy a chunk of pixmap around the app */
77 static void copy_xpm_area(int x, int y, int w, int h, int dx, int dy)
79 XCopyArea(DADisplay, dockapp->pixmap, dockapp->pixmap,
80 DAGC, x, y, w, h, dx, dy);
81 dockapp->update = 1;
84 /* display AC power symbol */
85 static void display_power_glyph(void)
87 copy_xpm_area(67, 38, 12, 7, 6, 17);
90 /* get rid of AC power symbol */
91 static void kill_power_glyph(void)
93 copy_xpm_area(67, 48, 12, 7, 6, 17);
96 /* display battery symbol */
97 static void display_battery_glyph(void)
99 copy_xpm_area(82, 38, 12, 7, 20, 17);
102 /* get rid of battery symbol */
103 static void kill_battery_glyph(void)
105 copy_xpm_area(82, 48, 12, 7, 20, 17);
108 /* clear the time display */
109 static void clear_time_display(void)
111 copy_xpm_area(114, 76, 31, 11, 7, 32);
114 /* set time display to -- -- */
115 static void invalid_time_display(void)
117 copy_xpm_area(122, 14, 31, 11, 7, 32);
120 static void reset_scroll(void) {
121 dockapp->scroll_reset = 1;
124 static void clear_text_area(void) {
125 copy_xpm_area(66, 9, 52, 7, 6, 50);
128 static void redraw_window(void)
130 if (dockapp->update) {
131 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->win,
132 DAGC, 0, 0, 64, 64, 0, 0);
133 dockapp->update = 0;
137 static void new_window(char *display, char *name, int argc, char **argv)
139 XSizeHints *hints;
141 /* Initialise the dockapp window and appicon */
142 DAOpenDisplay(display, argc, argv);
143 DACreateIcon(name, 64, 64, argc, argv);
144 dockapp->display = DADisplay;
145 dockapp->win = DAWindow;
147 XSelectInput(dockapp->display, dockapp->win,
148 ExposureMask | ButtonPressMask | ButtonReleaseMask |
149 StructureNotifyMask);
151 /* create the main pixmap . . . */
152 DAMakePixmapFromData(master_xpm, &dockapp->pixmap, &dockapp->mask,
153 &dockapp->width, &dockapp->height);
154 DASetPixmap(dockapp->pixmap);
155 DASetShape(dockapp->mask);
157 /* text area is 318x7, or 53 characters long */
158 dockapp->text = XCreatePixmap(dockapp->display, dockapp->win, 318, 7,
159 DefaultDepth(dockapp->display,
160 dockapp->screen));
161 if (!dockapp->text) {
162 pfatal("FATAL: Cannot create text scroll pixmap!\n");
163 exit(1);
166 /* force the window to stay this size - otherwise the user could
167 * resize us and see our panties^Wmaster pixmap . . . */
168 hints = XAllocSizeHints();
169 if(hints) {
170 hints->flags |= PMinSize | PMaxSize;
171 hints->min_width = 64;
172 hints->max_width = 64;
173 hints->min_height = 64;
174 hints->max_height = 64;
175 XSetWMNormalHints(dockapp->display, dockapp->win, hints);
176 XFree(hints);
179 DAShow();
182 static void copy_to_text_buffer(int sx, int sy, int w, int h, int dx, int dy)
184 XCopyArea(dockapp->display, dockapp->pixmap, dockapp->text,
185 DAGC, sx, sy, w, h, dx, dy);
188 static void copy_to_text_area(int sx, int sy, int w, int h, int dx, int dy)
190 XCopyArea(dockapp->display, dockapp->text, dockapp->pixmap,
191 DAGC, sx, sy, w, h, dx, dy);
194 static void scroll_text(void)
196 static int start, end, stop;
197 int x = 6; /* x coord of the start of the text area */
198 int y = 50; /* y coord */
199 int width = 51; /* width of the text area */
200 int height = 7; /* height of the text area */
201 int tw = dockapp->tw; /* width of the rendered text */
202 int sx, dx, w;
204 if (!dockapp->scroll)
205 return;
208 * Conceptually this is viewing the text through a scrolling
209 * window - the window starts out with the end immediately before
210 * the text, and stops when the start of the window is immediately
211 * after the end of the text.
213 * We begin with the start of the window at pixel (0 - width) and
214 * as we scroll we render only the portion of the window above
215 * pixel 0. The destination of the copy during this period starts
216 * out at the end of the text area and works left as more of the
217 * text is being copied, until a full window is being copied.
219 * As the end of the window moves out past the end of the text, we
220 * want to keep the destination at the beginning of the text area,
221 * but copy a smaller and smaller chunk of the text. Eventually the
222 * start of the window will scroll past the end of the text, at
223 * which point we stop doing any work and wait to be reset.
226 if (dockapp->scroll_reset) {
227 start = 0 - width;
228 end = 0;
229 stop = 0;
230 clear_text_area();
231 dockapp->scroll_reset = 0;
234 if (stop)
235 return;
237 w = 52;
238 if (end < 52)
239 w = end;
240 else if (end > tw)
241 w = 52 - (end - tw);
243 dx = x + 52 - w;
244 if (end > tw)
245 dx = x;
247 sx = start;
248 if (start < 0)
249 sx = 0;
251 if (start > tw)
252 stop = 1;
254 clear_text_area();
255 copy_to_text_area(sx, 0, w, height, dx, y);
256 start += 2;
257 end += 2;
259 dockapp->update = 1;
262 static void render_text(char *string)
264 int i, c, k;
266 /* drop out immediately if scrolling is disabled - we don't render
267 * any text at all, since there's not much else we could do
268 * sensibly without scrolling. */
269 if (!dockapp->scroll)
270 return;
272 if (strlen(string) > 53)
273 return;
275 /* prepare the text area by clearing it */
276 for (i = 0; i < 54; i++) {
277 copy_to_text_buffer(133, 57, 6, 8, i * 6, 0);
279 k = 0;
281 for (i = 0; string[i]; i++) {
282 c = toupper(string[i]);
283 if (c >= 'A' && c <= 'Z') { /* letter */
284 c = c - 'A';
285 copy_to_text_buffer(c * 6, 67, 6, 7, k, 0);
286 } else if (c >= '0' && c <= '9') { /* number */
287 c = c - '0';
288 copy_to_text_buffer(c * 6 + 66, 58, 6, 7, k, 0);
289 } else if (c == '.') {
290 copy_to_text_buffer(140, 58, 6, 7, k, 0);
291 } else if (c == '-') {
292 copy_to_text_buffer(126, 58, 6, 7, k, 0);
294 k += 6;
296 dockapp->tw = k; /* length of text segment */
297 /* re-scroll the message */
298 reset_scroll();
299 scroll_text();
302 static void clear_percentage(void)
304 /* clear the number */
305 copy_xpm_area(95, 47, 21, 9, 37, 16);
306 /* clear the bar */
307 copy_xpm_area(66, 18, 54, 8, 5, 5);
309 dockapp->percent = -1;
312 static void display_percentage(int percent)
314 unsigned int bar;
315 int width = 54; /* width of the bar */
316 float ratio = 100.0/width; /* ratio between the current percentage
317 * and the number of pixels in the bar */
319 if (percent == -1)
320 percent = 0;
322 if (dockapp->percent == percent)
323 return;
325 if (percent < 0)
326 percent = 0;
327 if (percent > 100)
328 percent = 100;
330 if (dockapp->percent == -1)
331 copy_xpm_area(127, 28, 5, 7, 52, 17);
333 if (percent < 100) { /* 0 - 99 */
334 copy_xpm_area(95, 48, 8, 7, 37, 17);
335 if (percent >= 10)
336 copy_xpm_area((percent / 10) * 6 + 67, 28, 5, 7, 40, 17);
337 copy_xpm_area((percent % 10) * 6 + 67, 28, 5, 7, 46, 17);
338 } else
339 copy_xpm_area(95, 37, 21, 9, 37, 16); /* 100% */
340 dockapp->percent = percent;
342 bar = (int)((float)percent / ratio);
344 copy_xpm_area(66, 0, bar, 8, 5, 5);
345 if (bar < 54)
346 copy_xpm_area(66 + bar, 18, 54 - bar, 8, bar + 5, 5);
349 static void display_time(int minutes)
351 static int ohour = -1, omin = -1;
352 int hour, min, tmp;
354 if (minutes <= 0) { /* error - clear the display */
355 invalid_time_display();
356 ohour = omin = -1;
357 return;
360 /* render time on the display */
361 hour = minutes / 60;
362 /* our display area only fits %2d:%2d, so we need to make sure
363 * what we're displaying will fit in those constraints. I don't
364 * think we're likely to see any batteries that do more than
365 * 100 hours any time soon, so it's fairly safe. */
366 if (hour >= 100) {
367 hour = 99;
368 min = 59;
369 } else
370 min = minutes % 60;
372 if (hour == ohour && min == omin)
373 return;
375 tmp = hour / 10;
376 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 7, 32);
377 tmp = hour % 10;
378 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 14, 32);
379 tmp = min / 10;
380 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 25, 32);
381 tmp = min % 10;
382 copy_xpm_area(tmp * 7 + 1, 76, 6, 11, 32, 32);
383 copy_xpm_area(71, 76, 3, 11, 21, 32);
384 ohour = hour;
385 omin = min;
389 * The reworked state handling stuff.
392 /* set the current state of the power panel */
393 enum panel_states {
394 PS_AC,
395 PS_BATT,
396 PS_NULL,
399 static void really_blink_power_glyph(void)
401 static int counter = 0;
403 if (counter == 10)
404 display_power_glyph();
405 else if (counter == 20)
406 kill_power_glyph();
407 else if (counter > 30)
408 counter = 0;
409 counter++;
412 static void blink_power_glyph(void)
414 if (dockapp->blink)
415 really_blink_power_glyph();
418 static void really_blink_battery_glyph(void)
420 static int counter = 0;
422 if (counter == 10)
423 display_battery_glyph();
424 else if (counter == 20)
425 kill_battery_glyph();
426 else if (counter > 30)
427 counter = 0;
428 counter++;
431 static void blink_battery_glyph(void)
433 if (dockapp->blink)
434 really_blink_battery_glyph();
437 static void set_power_panel(global_t *globals)
439 static enum panel_states power = PS_NULL;
440 battery_t *binfo = globals->binfo;
441 adapter_t *ap = &globals->adapter;
443 if (ap->power == AC) {
444 if (power != PS_AC) {
445 power = PS_AC;
446 kill_battery_glyph();
447 display_power_glyph();
449 } else if (ap->power == BATT) {
450 if (power != PS_BATT) {
451 power = PS_BATT;
452 kill_power_glyph();
453 display_battery_glyph();
457 if (globals->battery_count > 0) {
458 if (binfo->charge_state == CHARGE)
459 blink_power_glyph();
461 if ((binfo->state == CRIT) && (ap->power == BATT))
462 blink_battery_glyph();
466 void scroll_faster(double factor) {
467 scroll_reset = scroll_reset * factor;
470 void scroll_slower(double factor) {
471 scroll_reset = scroll_reset * factor;
474 void reset_scroll_speed(void) {
475 scroll_reset = DEFAULT_SCROLL_RESET;
479 * The message that needs to be displayed needs to be decided
480 * according to a heirarchy: a message like not present needs to take
481 * precedence over a global thing like the current power status, and
482 * something like a low battery warning should take precedence over
483 * the "on battery" message. Likewise, a battery charging message
484 * needs to take precedence over the on ac power message. The other
485 * question is how much of a precedence local messages should take
486 * over global ones . . .
488 * So, there are three possible sets of messages: not present, on-line
489 * and off-line messages. We need to decide which of those sets is
490 * appropriate right now, and then decide within them.
492 enum messages {
493 M_NB, /* no batteries */
494 M_NP, /* not present */
495 M_AC, /* on ac power */
496 M_CH, /* battery charging */
497 M_BATT, /* on battery */
498 M_LB, /* low battery */
499 M_CB, /* critical low battery */
500 M_HCB, /* battery reported critical capacity state */
501 M_NULL, /* empty starting state */
504 static void set_message(global_t *globals)
506 static enum messages state = M_NULL;
507 battery_t *binfo = globals->binfo;
508 adapter_t *ap = &globals->adapter;
510 if (globals->battery_count == 0) {
511 if (state != M_NB) {
512 state = M_NB;
513 reset_scroll_speed();
514 render_text("no batteries");
517 return;
520 /* battery not present case */
521 if (!binfo->present) {
522 if (state != M_NP) {
523 state = M_NP;
524 reset_scroll_speed();
525 render_text("not present");
527 } else if (ap->power == AC) {
528 if (binfo->charge_state == CHARGE) {
529 if (state != M_CH) {
530 state = M_CH;
531 reset_scroll_speed();
532 render_text("battery charging");
534 } else {
535 if (state != M_AC) {
536 state = M_AC;
537 reset_scroll_speed();
538 render_text("on ac power");
541 } else {
542 if (binfo->state == CRIT) {
543 if (state != M_CB) {
544 state = M_CB;
545 scroll_faster(0.75);
546 render_text("critical low battery");
548 } else if (binfo->state == LOW) {
549 if (state != M_LB) {
550 state = M_LB;
551 scroll_faster(0.85);
552 render_text("low battery");
554 } else {
555 if (state != M_BATT) {
556 state = M_BATT;
557 reset_scroll_speed();
558 render_text("on battery");
564 void set_time_display(global_t *globals)
566 if (globals->battery_count == 0) {
567 invalid_time_display();
568 return;
571 if (globals->binfo->charge_state == CHARGE)
572 display_time(globals->binfo->charge_time);
573 else if (globals->binfo->charge_state == DISCHARGE)
574 display_time(globals->rtime);
575 else
576 invalid_time_display();
579 void clear_batt_id_area(void)
581 copy_xpm_area(125, 40, 7, 11, 51, 32);
584 void set_batt_id_area(int bno)
586 int w = 7; /* Width of the number */
587 int h = 11; /* Height of the number */
588 int dx = 50; /* x coord of the target area */
589 int dy = 32; /* y coord of the target area */
590 int sx = (bno + 1) * 7; /* source x coord */
591 int sy = 76; /* source y coord */
593 copy_xpm_area(sx, sy, w, h, dx, dy);
596 #define VERSION "wmacpi version " WMACPI_VER "\nUsing libacpi version " LIBACPI_VER
598 void cli_wmacpi(global_t *globals, int samples)
600 int i, j, sleep_time = 0;
601 battery_t *binfo;
602 adapter_t *ap;
604 pdebug("samples: %d\n", samples);
605 if(samples > 1)
606 sleep_time = 1000000/samples;
608 /* we want to acquire samples over some period of time, so . . . */
609 for(i = 0; i < samples + 2; i++) {
610 for(j = 0; j < globals->battery_count; j++)
611 acquire_batt_info(globals, j);
612 acquire_global_info(globals);
613 usleep(sleep_time);
616 ap = &globals->adapter;
617 if(ap->power == AC) {
618 printf("On AC Power");
619 for(i = 0; i < globals->battery_count; i++) {
620 binfo = &batteries[i];
621 if(binfo->present && (binfo->charge_state == CHARGE)) {
622 printf("; Battery %s charging", binfo->name);
623 printf(", currently at %2d%%", binfo->percentage);
624 if(binfo->charge_time >= 0)
625 printf(", %2d:%02d remaining",
626 binfo->charge_time/60,
627 binfo->charge_time%60);
630 printf("\n");
631 } else if(ap->power == BATT) {
632 printf("On Battery");
633 for(i = 0; i < globals->battery_count; i++) {
634 binfo = &batteries[i];
635 if(binfo->present && (binfo->percentage >= 0))
636 printf(", Battery %s at %d%%", binfo->name,
637 binfo->percentage);
639 if(globals->rtime >= 0)
640 printf("; %d:%02d remaining", globals->rtime/60,
641 globals->rtime%60);
642 printf("\n");
644 return;
647 battery_t *switch_battery(global_t *globals, int battno)
649 globals->binfo = &batteries[battno];
650 pinfo("changing to monitor battery %s\n", globals->binfo->name);
651 set_batt_id_area(battno);
652 dockapp->update = 1;
654 return globals->binfo;
657 int main(int argc, char **argv)
659 char *display = NULL;
660 int sample_count = 0;
661 int batt_reinit, ac_reinit;
662 int batt_count = 0;
663 int ac_count = 0;
664 int cli = 0, samples = 1, critical = 10;
665 int samplerate = 20;
666 int sleep_rate = 10;
667 int sleep_time = 1000000/sleep_rate;
668 int scroll_count = 0;
669 enum rtime_mode rt_mode = RT_RATE;
670 int rt_forced = 0;
671 battery_t *binfo = NULL;
672 global_t *globals;
674 DAProgramOption options[] = {
675 {"-r", "--no-scroll", "disable scrolling message", DONone, False, {NULL}},
676 {"-n", "--no-blink", "disable blinking of various UI elements", DONone, False, {NULL}},
677 {"-x", "--cmdline", "run in command line mode", DONone, False, {NULL}},
678 {"-f", "--force-capacity-mode", "force the use of capacity mode for calculating time remaining", DONone, False, {NULL}},
679 {"-d", "--display", "display or remote display", DOString, False, {&display}},
680 {"-c", "--critical", "set critical low alarm at <number> percent\n (default: 10 percent)", DONatural, False, {&critical}},
681 {"-m", "--battery", "battery number to monitor", DONatural, False, {&battery_no}},
682 {"-s", "--sample-rate", "number of times per minute to sample battery information\n default 20 (once every three seconds)", DONatural, False, {&samplerate}},
683 {"-V", "--verbosity", "Set verbosity", DONatural, False, {&verbosity}},
684 {"-a", "--samples", "number of samples to average over (cli mode only)", DONatural, False, {&samples}},
687 dockapp = calloc(1, sizeof(struct dockapp));
688 globals = calloc(1, sizeof(global_t));
690 dockapp->blink = 1;
691 dockapp->bell = 0;
692 dockapp->scroll = 1;
693 dockapp->scroll_reset = 0;
694 globals->crit_level = 10;
695 battery_no = 1;
697 /* after this many samples, we reinit the battery and AC adapter
698 * information.
699 * XXX: make these configurable . . . */
700 batt_reinit = 100;
701 ac_reinit = 1000;
703 /* this needs to be up here because we need to know what batteries
704 * are available /before/ we can decide if the battery we want to
705 * monitor is available. */
706 /* parse command-line options */
707 DAParseArguments(argc, argv, options, 10,
708 "A battery monitor dockapp for ACPI based systems",
709 VERSION);
711 if (options[0].used)
712 dockapp->scroll = 0;
713 if (options[1].used)
714 dockapp->blink = 0;
715 if (options[2].used)
716 cli = 1;
717 if (options[3].used) {
718 rt_mode = RT_CAP;
719 rt_forced = 1;
722 if (samplerate == 0) samplerate = 1;
723 if (samplerate > 600) samplerate = 600;
725 if (critical > 100) {
726 fprintf(stderr, "Please use values between 0 and 100%%\n");
727 fprintf(stderr, "Using default value of 10%%\n");
728 critical = 10;
730 globals->crit_level = critical;
732 if (battery_no >= MAXBATT) {
733 fprintf(stderr, "Please specify a battery number below %d\n", MAXBATT);
734 return 1;
736 pinfo("Monitoring battery %d\n", battery_no);
738 if (power_init(globals))
739 /* power_init functions handle printing error messages */
740 exit(1);
742 globals->rt_mode = rt_mode;
743 globals->rt_forced = rt_forced;
745 if (battery_no > globals->battery_count) {
746 pinfo("Battery %d not available for monitoring.\n", battery_no);
749 /* check for cli mode */
750 if (cli) {
751 cli_wmacpi(globals, samples);
752 exit(0);
754 /* check to see if we've got a valid DISPLAY env variable, as a simple check to see if
755 * we're running under X */
756 if (!getenv("DISPLAY")) {
757 pdebug("Not running under X - using cli mode\n");
758 cli_wmacpi(globals, samples);
759 exit(0);
762 battery_no--;
764 /* make new dockapp window */
765 /* Don't even /think/ of asking me why, but if I set the window name to
766 * "acpi", the app refuses to dock properly - it's just plain /weird/.
767 * So, wmacpi it is . . . */
768 new_window(display, "wmacpi", argc, argv);
770 /* get initial statistics */
771 acquire_all_info(globals);
773 if (globals->battery_count > 0) {
774 binfo = &batteries[battery_no];
775 globals->binfo = binfo;
776 set_batt_id_area(battery_no);
777 pinfo("monitoring battery %s\n", binfo->name);
780 clear_time_display();
781 set_power_panel(globals);
782 set_message(globals);
784 /* main loop */
785 while (1) {
786 Atom atom;
787 Atom wmdelwin;
788 XEvent event;
789 while (XPending(dockapp->display)) {
790 XNextEvent(dockapp->display, &event);
791 switch (event.type) {
792 case Expose:
793 /* update */
794 dockapp->update = 1;
795 while (XCheckTypedEvent(dockapp->display, Expose, &event));
796 redraw_window();
797 break;
798 case DestroyNotify:
799 XCloseDisplay(dockapp->display);
800 exit(0);
801 break;
802 case ButtonPress:
803 break;
804 case ButtonRelease:
805 if (globals->battery_count == 0)
806 break;
808 /* cycle through the known batteries. */
809 battery_no++;
810 battery_no = battery_no % globals->battery_count;
812 binfo = switch_battery(globals, battery_no);
813 break;
814 case ClientMessage:
815 /* what /is/ this crap?
816 * Turns out that libdockapp adds the WM_DELETE_WINDOW atom to
817 * the WM_PROTOCOLS property for the window, which means that
818 * rather than get a simple DestroyNotify message, we get a
819 * nice little message from the WM saying "hey, can you delete
820 * yourself, pretty please?". So, when running as a window
821 * rather than an icon, we're impossible to kill in a friendly
822 * manner, because we're expecting to die from a DestroyNotify
823 * and thus blithely ignoring the WM knocking on our window
824 * border . . .
826 * This simply checks for that scenario - it may fail oddly if
827 * something else comes to us via a WM_PROTOCOLS ClientMessage
828 * event, but I suspect it's not going to be an issue. */
829 wmdelwin = XInternAtom(dockapp->display, "WM_DELETE_WINDOW", 1);
830 atom = event.xclient.data.l[0];
831 if (atom == wmdelwin) {
832 XCloseDisplay(dockapp->display);
833 exit(0);
835 break;
839 /* XXX: some laptops have problems with sampling the battery
840 * regularly - apparently, the BIOS disables interrupts while
841 * reading from the battery, which is generally on a slow bus
842 * and is a slow device, so you get significant periods without
843 * interrupts. This causes interactivity to suffer . . .
845 * My proposed workaround is to allow the user to set the sample
846 * rate - it defaults to ten, but can be set lower (or higher).
848 * The only problem with this is that we need to sample less
849 * frequently, while still allowing the app to update normally.
850 * That means calling redraw_window() and all the set_*() functions
851 * normally, but only calling acquire_all_info() every so often.
852 * As it stands, we only call acquire_all_info() once every three
853 * seconds (once every thirty updates) . . . I'm not entirely sure
854 * /how/ this could cause interactivity problems, but hey . . .
856 * So, given the base rate of once every three seconds, we want to
857 * change this test to . . . */
858 /* Okay, this needs /fixing/ - it's ridiculous. We should be giving
859 * the user the option of saying how many times per minute the
860 * battery should be sampled, defaulting to 20 times.
862 * We sleep for one tenth of a second at a time, so 60 seconds
863 * translates to 600 sleeps. So, we change the default sample
864 * rate to 20, and the calculation below becomes . . .*/
865 if (sample_count++ == ((sleep_rate*60)/samplerate)) {
866 if (globals->battery_count == 0) {
867 batt_count = 0;
869 reinit_batteries(globals);
871 /* battery appeared */
872 if (globals->battery_count > 0) {
873 if (battery_no > globals->battery_count)
874 battery_no = 0;
876 binfo = switch_battery(globals, battery_no);
880 acquire_all_info(globals);
882 /* we need to be able to reinitialise batteries and adapters, because
883 * they change - you can hotplug batteries on most laptops these days
884 * and who knows what kind of shit will be happening soon . . . */
885 if (batt_count++ >= batt_reinit) {
886 if(reinit_batteries(globals))
887 pfatal("Oh my god, the batteries are gone!\n");
888 batt_count = 0;
891 if (ac_count++ >= ac_reinit) {
892 if(reinit_ac_adapters(globals))
893 pfatal("What happened to our AC adapters?!?\n");
894 ac_count = 0;
896 sample_count = 0;
899 if (scroll_count++ >= scroll_reset) {
900 reset_scroll();
901 scroll_count = 0;
904 /* The old code had some kind of weird crap with timers and the like.
905 * As far as I can tell, it's meaningless - the time we want to display
906 * is the time calculated from the remaining capacity, as per the
907 * ACPI spec. The only thing I'd change is the handling of a charging
908 * state: my best guess, based on the behaviour I'm seeing with my
909 * Lifebook, is that the present rate value when charging is the rate
910 * at which the batteries are being charged, which would mean I'd just
911 * need to reverse the rtime calculation to be able to work out how
912 * much time remained until the batteries were fully charged . . .
913 * That would be rather useful, though given it would vary rather a lot
914 * it seems likely that it'd be little more than a rough guesstimate. */
915 set_time_display(globals);
916 set_power_panel(globals);
917 set_message(globals);
919 if (globals->battery_count == 0) {
920 clear_percentage();
921 clear_batt_id_area();
922 } else
923 display_percentage(binfo->percentage);
925 scroll_text();
927 /* redraw_window, if anything changed - determined inside
928 * redraw_window. */
929 redraw_window();
931 usleep(sleep_time);
933 return 0;