wmclockmon: include styles in dist tar-ball and install in `$(pkgdatadir)`
[dockapps.git] / wmbattery / wmbattery.c
blob7c1a9d466ac9cb289bfeedfb36186fa2498e6cbd
1 /* wmbattery - display laptop battery info, dockable in WindowMaker
2 * Copyright (C) 1998-2014 Joey Hess <joey@kitenet.net>
3 * Copyright (C) 2014 Window Maker Developers Team
4 * <wmaker-dev@googlegroups.com>
6 * Portions of code derived from:
7 * wmapm - Copyright (C) 1998-1999 Chris D. Faulhaber <jedgar@fxp.org>
8 * wmmon - Copyright (C) 1998 Martijn Pieterse <pieterse@xs4all.nl>
9 * Copyright (C) 1998 Antoine Nulle <warp@xs4all.nl>
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU General Public License
13 * as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <X11/xpm.h>
28 #include <X11/extensions/shape.h>
29 #include <stdarg.h>
30 #include <signal.h>
31 #include <time.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include "wmbattery.h"
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #endif
42 #include "mask.xbm"
43 #include "mask_nodial.xbm"
44 #include "sonypi.h"
45 #include "acpi.h"
46 #ifdef HAL
47 #include "simplehal.h"
48 #endif
49 #ifdef UPOWER
50 #include "upower.h"
51 #endif
53 Pixmap images[NUM_IMAGES];
54 Window root, iconwin, win;
55 int screen;
56 XpmIcon icon;
57 Display *display;
58 GC NormalGC;
59 char *user_geom = NULL;
61 #ifdef HAVE__DEV_APM
62 #define APM_STATUS_FILE "/dev/apm"
63 #else
64 #define APM_STATUS_FILE "/proc/apm"
65 #endif
67 char *apm_status_file = APM_STATUS_FILE;
69 char *crit_audio_fn = NULL;
70 char *crit_audio;
71 int crit_audio_size;
72 char *crit_command = NULL;
74 int battnum = 1;
75 #ifdef HAL
76 int use_simplehal = 0;
77 #endif
78 #ifdef UPOWER
79 int use_upower = 0;
80 #endif
81 int use_sonypi = 0;
82 int use_acpi = 0;
83 int delay = 0;
84 int always_estimate_remaining = 0;
85 int granularity_estimate_remaining = 1;
86 int initial_state = WithdrawnState;
87 int use_dial = 1;
89 signed int low_pct = -1;
90 signed int critical_pct = -1;
92 void error(const char *fmt, ...)
94 va_list arglist;
96 va_start(arglist, fmt);
97 fprintf(stderr, "Error: ");
98 vfprintf(stderr, fmt, arglist);
99 fprintf(stderr, "\n");
100 va_end(arglist);
102 exit(1);
105 #if defined (HAVE_MACHINE_APM_BIOS_H) || defined (HAVE_I386_APMVAR_H) /* BSD */
106 int apm_read(apm_info *i)
108 int fd;
109 #ifdef HAVE_MACHINE_APM_BIOS_H /* FreeBSD */
110 unsigned long request = APMIO_GETINFO;
111 struct apm_info info;
112 #else /* NetBSD or OpenBSD */
113 unsigned long request= APM_IOC_GETPOWER;
114 struct apm_power_info info;
115 #endif
117 if ((fd = open(apm_status_file, O_RDONLY)) == -1) {
118 return 0;
120 if (ioctl(fd, request, &info) == -1) {
121 return 0;
123 close(fd);
125 #ifdef HAVE_MACHINE_APM_BIOS_H /* FreeBSD */
126 i->ac_line_status = info.ai_acline;
127 i->battery_status = info.ai_batt_stat;
128 i->battery_flags = (info.ai_batt_stat == 3) ? 8: 0;
129 i->battery_percentage = info.ai_batt_life;
130 i->battery_time = info.ai_batt_time;
131 i->using_minutes = 0;
132 #else /* NetBSD or OpenBSD */
133 i->ac_line_status = info.ac_state;
134 i->battery_status = info.battery_state;
135 i->battery_flags = (info.battery_state == 3) ? 8: 0;
136 i->battery_percentage = info.battery_life;
137 i->battery_time = info.minutes_left;
138 i->using_minutes = 1;
139 #endif
141 return 1;
144 int apm_exists(void)
146 apm_info i;
148 if (access(apm_status_file, R_OK))
149 return 0;
150 return apm_read(&i);
152 #elif !defined(HAVE_LIBAPM)
153 int apm_read(apm_info *i)
155 return -1;
157 int apm_exists(void)
159 return -1;
161 #endif
163 int apm_change(apm_info *cur)
165 static int ac_line_status = 0, battery_status = 0, battery_flags = 0,
166 battery_percentage = 0, battery_time = 0, using_minutes = 0;
168 int i = cur->ac_line_status == ac_line_status &&
169 cur->battery_status == battery_status &&
170 cur->battery_flags == battery_flags &&
171 cur->battery_percentage == battery_percentage &&
172 cur->battery_time == battery_time &&
173 cur->using_minutes == using_minutes;
175 ac_line_status = cur->ac_line_status;
176 battery_status = cur->battery_status;
177 battery_flags = cur->battery_flags;
178 battery_percentage = cur->battery_percentage;
179 battery_time = cur->battery_time;
180 using_minutes = cur->using_minutes;
182 return i;
185 /* Calculate battery estimate */
186 void estimate_timeleft(apm_info *cur_info)
188 /* Time of the last estimate */
189 static time_t estimate_time;
190 /* Estimated time left */
191 static time_t estimate;
192 /* Time when we last noticed a battery level change */
193 static time_t battery_change_time;
194 /* The previous estimation we had before the battery level changed */
195 static time_t prev_estimate;
196 /* Percentage at the last estimate */
197 static short percent;
198 /* Where we charging or discharging the last time we were called? */
199 static short was_charging = 1;
200 /* Have we made a guess lately? */
201 static short guessed_lately;
203 time_t t;
204 int interval;
205 short is_charging = cur_info->battery_flags & BATTERY_FLAGS_CHARGING;
207 errno = 0;
208 if (time(&t) == ((time_t)-1) && errno != 0)
209 goto estim_values;
211 if ((
212 /* AC is on and battery is not charging anymore or ... */
213 (cur_info->ac_line_status == AC_LINE_STATUS_ON) && !is_charging
214 ) ||
216 /* ... the charging state has changed */
217 is_charging ^ was_charging
218 )) {
219 /* Reset counters */
220 battery_change_time = t;
221 estimate = -1;
222 guessed_lately = 0;
223 estimate_time = t;
224 prev_estimate = 0;
225 goto estim_values;
228 /* No change: decrease estimate */
229 if ((percent - cur_info->battery_percentage)
230 / granularity_estimate_remaining == 0) {
231 estimate -= t - estimate_time;
232 estimate_time = t;
233 if (guessed_lately && estimate < 0)
234 estimate = 0;
235 goto estim_values;
238 /* The battery level changed: calculate estimate based
239 * on change speed and previous estimate */
240 guessed_lately = 1;
241 estimate_time = t;
242 interval = estimate_time - battery_change_time;
243 prev_estimate = estimate;
244 battery_change_time = estimate_time;
245 estimate = (is_charging
246 ? (cur_info->battery_percentage - 100)
247 : cur_info->battery_percentage)
248 * interval / (percent - cur_info->battery_percentage);
249 if (prev_estimate > 0)
250 estimate = (estimate * 2 + prev_estimate) / 3;
252 estim_values:
253 percent = cur_info->battery_percentage;
254 was_charging = is_charging;
255 cur_info->battery_time = estimate;
256 if (estimate < 0)
257 estimate = 0;
258 cur_info->using_minutes = 0;
261 /* Load up the images this program uses. */
262 void load_images(void)
264 int x;
265 char fn[128]; /* enough? */
267 for (x = 0; x < NUM_IMAGES; x++) {
268 sprintf(fn, "%s/%s.xpm", ICONDIR, image_info[x].filename);
269 if (XpmReadFileToPixmap(display, root, fn, &images[x], NULL, NULL)) {
270 /* Check in current direcotry for fallback. */
271 sprintf(fn, "%s.xpm", image_info[x].filename);
272 if (XpmReadFileToPixmap(display, root, fn, &images[x], NULL, NULL))
273 error("Failed to load %s\n", fn);
278 void load_audio(void)
280 int fd;
281 struct stat s;
283 crit_audio = NULL;
284 if (crit_audio_fn == NULL)
285 return;
286 fd = open(crit_audio_fn, 0);
287 if (fd == -1)
288 error("unable to open audio file");
289 if (fstat(fd, &s) == 0) {
290 crit_audio_size = s.st_size;
291 crit_audio = malloc(crit_audio_size);
292 /* XXX: make this more robust? (loop?) */
293 if (read(fd, crit_audio, crit_audio_size) != crit_audio_size) {
294 free(crit_audio);
295 crit_audio = NULL;
296 error("unable to read audio file");
299 close(fd);
302 /* string replacement function by Laird Shaw, in public domain
303 * http://creativeandcritical.net/str-replace-c */
304 char *replace_str(const char *str, const char *old, const char *new)
306 char *ret, *r;
307 const char *p, *q;
308 size_t oldlen = strlen(old);
309 size_t count, retlen, newlen = strlen(new);
311 if (oldlen != newlen) {
312 for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen)
313 count++;
314 /* this is undefined if p - str > PTRDIFF_MAX */
315 retlen = p - str + strlen(p) + count * (newlen - oldlen);
316 } else
317 retlen = strlen(str);
319 ret = malloc(retlen + 1);
320 if (!ret)
321 return NULL;
323 for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) {
324 /* this is undefined if q - p > PTRDIFF_MAX */
325 ptrdiff_t l = q - p;
326 memcpy(r, p, l);
327 r += l;
328 memcpy(r, new, newlen);
329 r += newlen;
331 strcpy(r, p);
333 return ret;
336 void cmd_crit(const char *cmd, int percentage, int time)
338 char prc_str[255] = "";
339 char min_str[255] = "";
340 char sec_str[255] = "";
341 char *tmp_a = NULL;
342 char *tmp_b = NULL;
343 char *command = NULL;
344 int ret;
346 if (!cmd)
347 return;
348 if (percentage > 100 || percentage < 0)
349 return;
350 if (time > 65535 || time < 0)
351 return;
353 sprintf(prc_str, "%i", percentage);
354 sprintf(min_str, "%i", time / 60);
355 sprintf(sec_str, "%i", time % 60);
357 tmp_a = replace_str(cmd, STR_SUB_PERCENT, prc_str);
358 if (!tmp_a)
359 return;
360 tmp_b = replace_str(tmp_a, STR_SUB_MINUTES, min_str);
361 if (!tmp_b)
362 goto free_tmp_a;
363 command = replace_str(tmp_b, STR_SUB_SECONDS, sec_str);
364 if (!command)
365 goto free_tmp_b;
367 ret = system(command);
368 if (ret == -1)
369 error("unable to run command: %s", command);
371 free(command);
372 free_tmp_b:
373 free(tmp_b);
374 free_tmp_a:
375 free(tmp_a);
378 /* Returns the display to run on (or NULL for default). */
379 char *parse_commandline(int argc, char *argv[])
381 int c = 0;
382 char *ret = NULL;
384 while (c != -1) {
385 c = getopt(argc, argv, "hd:g:if:b:w:c:l:es:a:x:vn");
386 switch (c) {
387 case 'h':
388 printf("Usage: wmbattery [options]\n");
389 printf("\t-d <display>\tselects target display\n");
390 printf("\t-h\t\tdisplay this help\n");
391 printf("\t-g {+-}x{+-}y\tposition of the window\n");
392 printf("\t-i\t\tdisplay as icon\n");
393 printf("\t-b num\t\tnumber of battery to display\n");
394 printf("\t-w secs\t\tseconds between updates\n");
395 printf("\t-l percent\tlow percentage\n");
396 printf("\t-c percent\tcritical percentage\n");
397 printf("\t-e\t\tuse own time estimates\n");
398 printf("\t-s granularity\tignore fluctuations less than granularity%% (implies -e)\n");
399 printf("\t-a file\t\twhen critical send file to /dev/audio\n");
400 printf("\t-x command\twhen critical execute this command\n");
401 printf("\t-n\t\tdisable dial graphic\n");
402 printf("\t-v\t\tdisplay version number\n");
403 exit(0);
404 break;
405 case 'd':
406 ret = strdup(optarg);
407 break;
408 case 'g':
409 user_geom = strdup(optarg);
410 break;
411 case 'i':
412 initial_state = IconicState;
413 break;
414 case 'b':
415 battnum = atoi(optarg);
416 break;
417 case 'w':
418 delay = atoi(optarg);
419 break;
420 case 'l':
421 low_pct = atoi(optarg);
422 break;
423 case 'c':
424 critical_pct = atoi(optarg);
425 break;
426 case 'e':
427 always_estimate_remaining = 1;
428 break;
429 case 's':
430 always_estimate_remaining = 1;
431 granularity_estimate_remaining = atoi(optarg);
432 break;
433 case 'a':
434 crit_audio_fn = strdup(optarg);
435 break;
436 case 'x':
437 crit_command = strdup(optarg);
438 break;
439 case 'v':
440 printf("wmbattery "PACKAGE_VERSION"\n");
441 exit(0);
442 break;
443 case 'n':
444 use_dial = 0;
445 break;
449 return ret;
452 /* Sets up the window and icon and all the nasty X stuff. */
453 void make_window(char *display_name, int argc, char *argv[])
455 XClassHint classhint;
456 char *wname = argv[0];
457 XTextProperty name;
458 XGCValues gcv;
459 int dummy = 0, borderwidth = 1;
460 XSizeHints sizehints;
461 XWMHints wmhints;
462 Pixel back_pix, fore_pix;
463 Pixmap pixmask;
465 display = XOpenDisplay(display_name);
466 if (!display)
467 error("can't open display %s", XDisplayName(display_name));
469 screen = DefaultScreen(display);
470 root = RootWindow(display, screen);
472 /* Create window. */
473 sizehints.flags = USSize | USPosition;
474 sizehints.x = 0;
475 sizehints.y = 0;
476 XWMGeometry(display, screen, user_geom, NULL, borderwidth,
477 &sizehints, &sizehints.x, &sizehints.y,
478 &sizehints.width, &sizehints.height, &dummy);
480 sizehints.width = 64;
481 sizehints.height = 64;
482 back_pix = WhitePixel(display, screen);
483 fore_pix = BlackPixel(display, screen);
484 win = XCreateSimpleWindow(display, root, sizehints.x, sizehints.y,
485 sizehints.width, sizehints.height,
486 borderwidth, fore_pix, back_pix);
487 iconwin = XCreateSimpleWindow(display, win, sizehints.x,
488 sizehints.y, sizehints.width,
489 sizehints.height, borderwidth,
490 fore_pix, back_pix);
492 /* Activate hints */
493 XSetWMNormalHints(display, win, &sizehints);
494 classhint.res_name = wname;
495 classhint.res_class = wname;
496 XSetClassHint(display, win, &classhint);
498 if (!XStringListToTextProperty(&wname, 1, &name))
499 error("Can't allocate window name.");
501 XSetWMName(display, win, &name);
503 /* Create GC for drawing */
504 gcv.foreground = fore_pix;
505 gcv.background = back_pix;
506 gcv.graphics_exposures = 0;
507 NormalGC = XCreateGC(display, root,
508 GCForeground | GCBackground | GCGraphicsExposures,
509 &gcv);
511 if (use_dial)
512 pixmask = XCreateBitmapFromData(display, win, mask_bits,
513 mask_width, mask_height);
514 else
515 pixmask = XCreateBitmapFromData(display, win, mask_nodial_bits,
516 mask_nodial_width, mask_nodial_height);
517 XShapeCombineMask(display, win, ShapeBounding, 0, 0,
518 pixmask, ShapeSet);
519 XShapeCombineMask(display, iconwin, ShapeBounding, 0, 0,
520 pixmask, ShapeSet);
522 wmhints.initial_state = initial_state;
523 wmhints.icon_window = iconwin;
524 wmhints.icon_x = sizehints.x;
525 wmhints.icon_y = sizehints.y;
526 wmhints.window_group = win;
527 wmhints.flags = StateHint | IconWindowHint |
528 IconPositionHint | WindowGroupHint;
530 XSetWMHints(display, win, &wmhints);
531 XSetCommand(display, win, argv, argc);
533 XSelectInput(display, iconwin, ExposureMask);
534 XSelectInput(display, win, ExposureMask);
536 XMapWindow(display, win);
539 void flush_expose(Window w)
541 XEvent dummy;
543 while (XCheckTypedWindowEvent(display, w, Expose, &dummy))
547 void redraw_window(void)
549 XCopyArea(display, images[FACE], iconwin, NormalGC, 0, 0,
550 image_info[FACE].width, image_info[FACE].height, 0, 0);
551 flush_expose(iconwin);
552 XCopyArea(display, images[FACE], win, NormalGC, 0, 0,
553 image_info[FACE].width, image_info[FACE].height, 0, 0);
554 flush_expose(win);
558 * Display an image, using XCopyArea. Can display only part of an image,
559 * located anywhere.
561 void copy_image(int image, int xoffset, int yoffset,
562 int width, int height, int x, int y)
564 XCopyArea(display, images[image], images[FACE], NormalGC,
565 xoffset, yoffset, width, height, x, y);
569 * Display a letter in one of two fonts, at the specified x position.
570 * Note that 10 is passed for special characters `:' or `1' at the
571 * end of the font.
573 void draw_letter(int letter, int font, int x)
575 copy_image(font, image_info[font].charwidth * letter, 0,
576 image_info[font].charwidth, image_info[font].height,
577 x, image_info[font].y);
580 /* Display an image at its normal location. */
581 void draw_image(int image)
583 copy_image(image, 0, 0,
584 image_info[image].width, image_info[image].height,
585 image_info[image].x, image_info[image].y);
588 void recalc_window(apm_info cur_info)
590 int time_left, hour_left, min_left, digit, x;
591 static int blinked;
593 /* Display if it's plugged in. */
594 switch (cur_info.ac_line_status) {
595 case AC_LINE_STATUS_ON:
596 draw_image(PLUGGED);
597 break;
598 default:
599 draw_image(UNPLUGGED);
602 /* Display the appropriate color battery. */
603 switch (cur_info.battery_status) {
604 case BATTERY_STATUS_HIGH:
605 case BATTERY_STATUS_CHARGING:
606 draw_image(BATTERY_HIGH);
607 break;
608 case BATTERY_STATUS_LOW:
609 draw_image(BATTERY_LOW);
610 break;
611 case BATTERY_STATUS_CRITICAL: /* blinking red battery */
612 if (blinked)
613 draw_image(BATTERY_CRITICAL);
614 else
615 draw_image(BATTERY_BLINK);
616 blinked = !blinked;
617 break;
618 default:
619 draw_image(BATTERY_NONE);
622 /* Show if the battery is charging. */
623 if (cur_info.battery_flags & BATTERY_FLAGS_CHARGING)
624 draw_image(CHARGING);
625 else
626 draw_image(NOCHARGING);
629 * Display the percent left dial. This has the side effect of
630 * clearing the time left field.
632 x = DIAL_MULTIPLIER * cur_info.battery_percentage;
633 if (x >= 0) {
634 /* Start by displaying bright on the dial. */
635 copy_image(DIAL_BRIGHT, 0, 0,
636 x, image_info[DIAL_BRIGHT].height,
637 image_info[DIAL_BRIGHT].x,
638 image_info[DIAL_BRIGHT].y);
640 /* Now display dim on the remainder of the dial. */
641 copy_image(DIAL_DIM, x, 0,
642 image_info[DIAL_DIM].width - x,
643 image_info[DIAL_DIM].height,
644 image_info[DIAL_DIM].x + x,
645 image_info[DIAL_DIM].y);
647 /* Show percent remaining */
648 if (cur_info.battery_percentage >= 0) {
649 digit = cur_info.battery_percentage / 10;
650 if (digit == 10) {
651 /* 11 is the `1' for the hundreds place. */
652 draw_letter(11, SMALLFONT, HUNDREDS_OFFSET);
653 digit = 0;
655 draw_letter(digit, SMALLFONT, TENS_OFFSET);
656 digit = cur_info.battery_percentage % 10;
657 draw_letter(digit, SMALLFONT, ONES_OFFSET);
658 } else {
659 /* There is no battery, so we need to dim out the
660 * percent sign that is normally bright. */
661 draw_letter(10, SMALLFONT, PERCENT_OFFSET);
664 /* Show time left */
666 /* A negative number means that it is unknown. Dim the field. */
667 if (cur_info.battery_time < 0) {
668 draw_letter(10, BIGFONT, COLON_OFFSET);
669 redraw_window();
670 return;
673 if (cur_info.using_minutes)
674 time_left = cur_info.battery_time;
675 else
676 time_left = cur_info.battery_time / 60;
677 hour_left = time_left / 60;
678 min_left = time_left % 60;
679 digit = hour_left / 10;
680 draw_letter(digit, BIGFONT, HOURS_TENS_OFFSET);
681 digit = hour_left % 10;
682 draw_letter(digit, BIGFONT, HOURS_ONES_OFFSET);
683 digit = min_left / 10;
684 draw_letter(digit, BIGFONT, MINUTES_TENS_OFFSET);
685 digit = min_left % 10;
686 draw_letter(digit, BIGFONT, MINUTES_ONES_OFFSET);
688 redraw_window();
691 void snd_crit(void)
693 int audio, n;
695 if (crit_audio) {
696 audio = open("/dev/audio", O_WRONLY);
697 if (audio >= 0) {
698 n = write(audio, crit_audio, crit_audio_size);
699 if (n != crit_audio_size)
700 fprintf(stderr, "write failed (%d/%d bytes)\n", n, crit_audio_size);
701 close(audio);
706 void alarmhandler(int sig)
708 apm_info cur_info;
709 int old_status;
711 #ifdef UPOWER
712 if (use_upower) {
713 if (upower_read(1, &cur_info) != 0)
714 error("Cannot read upower information.");
715 } else if (use_acpi) {
716 #else
717 if (use_acpi) {
718 #endif
719 if (acpi_read(battnum, &cur_info) != 0)
720 error("Cannot read ACPI information.");
722 #ifdef HAL
723 else if (use_simplehal) {
724 if (simplehal_read(battnum, &cur_info) != 0)
725 error("Cannot read HAL information.");
727 #endif
728 else if (!use_sonypi) {
729 if (apm_read(&cur_info) != 0)
730 error("Cannot read APM information.");
731 } else {
732 if (sonypi_read(&cur_info) != 0)
733 error("Cannot read sonypi information.");
736 old_status = cur_info.battery_status;
738 /* Always calculate remaining lifetime? apm and acpi both use a
739 * negative number here to indicate error, missing battery, or
740 * cannot determine time. */
741 if (always_estimate_remaining || cur_info.battery_time < 0)
742 estimate_timeleft(&cur_info);
744 /* Override the battery status? */
745 if ((low_pct > -1 || critical_pct > -1) &&
746 cur_info.ac_line_status != AC_LINE_STATUS_ON) {
747 if (cur_info.battery_percentage <= critical_pct)
748 cur_info.battery_status = BATTERY_STATUS_CRITICAL;
749 else if (cur_info.battery_percentage <= low_pct)
750 cur_info.battery_status = BATTERY_STATUS_LOW;
751 else
752 cur_info.battery_status = BATTERY_STATUS_HIGH;
755 /* If APM data changes redraw and wait for next update */
756 /* Always redraw if the status is critical, to make it blink. */
757 if (!apm_change(&cur_info) || cur_info.battery_status == BATTERY_STATUS_CRITICAL)
758 recalc_window(cur_info);
760 if ((old_status == BATTERY_STATUS_HIGH) &&
761 (cur_info.battery_status == BATTERY_STATUS_LOW)) {
762 snd_crit();
763 } else if (cur_info.battery_status == BATTERY_STATUS_CRITICAL) {
764 snd_crit();
765 cmd_crit(crit_command, cur_info.battery_percentage,
766 cur_info.battery_time);
769 alarm(delay);
772 void check_battery_num(int real, int requested)
774 if (requested > real || requested < 1) {
775 error("There %s only %i batter%s, and you asked for number %i.",
776 real == 1 ? "is" : "are",
777 real,
778 real == 1 ? "y" : "ies",
779 requested);
783 int main(int argc, char *argv[])
785 make_window(parse_commandline(argc, argv), argc, argv);
787 /* Check for APM support (returns 0 on success). */
788 if (apm_exists() == 0) {
789 if (!delay)
790 delay = 1;
792 #ifdef HAL
793 /* Check for hal support. */
794 else if (simplehal_supported()) {
795 use_simplehal = 1;
796 if (!delay)
797 delay = 2;
799 #endif
800 #ifdef UPOWER
801 else if (upower_supported()) {
802 use_upower = 1;
803 delay = 2;
805 #endif
806 /* Check for ACPI support. */
807 else if (acpi_supported() && acpi_batt_count > 0) {
808 check_battery_num(acpi_batt_count, battnum);
809 use_acpi = 1;
810 if (!delay)
811 delay = 3; /* slow interface! */
812 } else if (sonypi_supported()) {
813 use_sonypi = 1;
814 low_pct = 10;
815 critical_pct = 5;
816 if (!delay)
817 delay = 1;
818 } else {
819 error("No APM, ACPI, UPOWER, HAL or SPIC support detected.");
822 load_images();
823 load_audio();
825 signal(SIGALRM, alarmhandler);
826 alarmhandler(SIGALRM);
828 while (1) {
829 XEvent ev;
830 XNextEvent(display, &ev);
831 if (ev.type == Expose)
832 redraw_window();