2 * A Simple Battery Status Display
4 * (c) 2007 Martin Mares <mj@ucw.cz>
18 static int total_full, total_capa, discharge_rate;
19 static int charge_time, discharge_time;
21 static unsigned int present_mask, charge_mask, discharge_mask;
23 #define CHECK_PERIOD 60
24 #define SHOW_DURATION 2
25 #define WARN_THRESHOLD 600
27 static char *parse_line(char *line)
29 char *t = strchr(line, '\n');
32 char *val = strchr(line, ':');
36 while (*val == ' ' || *val == '\t')
41 static void scan_ac(void)
45 const char dir[] = "/proc/acpi/ac_adapter";
46 DIR *d = opendir(dir);
51 while (e = readdir(d))
53 if (e->d_name[0] == '.')
55 char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
56 sprintf(n, "%s/%s/state", dir, e->d_name);
57 FILE *f = fopen(n, "r");
61 while (fgets(line, sizeof(line)-1, f))
63 char *val = parse_line(line);
66 if (!strcmp(line, "state") && !strcmp(val, "on-line"))
74 static void scan_batt(void)
76 charge_time = discharge_time = 0;
77 total_full = total_capa = 0;
79 present_mask = charge_mask = discharge_mask = 0;
81 const char dir[] = "/proc/acpi/battery";
82 DIR *d = opendir(dir);
87 while (e = readdir(d))
89 if (e->d_name[0] == '.')
91 char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
92 const char * const names[] = { "state", "info" };
99 for (int i=0; i<2; i++)
101 sprintf(n, "%s/%s/%s", dir, e->d_name, names[i]);
102 FILE *f = fopen(n, "r");
106 while (fgets(line, sizeof(line)-1, f))
108 char *val = parse_line(line);
111 // printf("<%s> <%s>\n", line, val);
112 if (!strcmp(line, "present"))
113 present = !strcmp(val, "yes");
114 else if (!strcmp(line, "charging state"))
115 charging = !strcmp(val, "charging");
116 else if (!strcmp(line, "present rate"))
118 else if (!strcmp(line, "remaining capacity"))
120 else if (!strcmp(line, "last full capacity"))
121 last_full = atol(val);
122 else if (!strcmp(line, "serial number"))
129 if (batt_id < 0 || batt_id > 31)
131 present_mask |= 1 << batt_id;
132 total_full += remains;
133 total_capa += last_full;
136 charge_mask |= 1 << batt_id;
137 int ch = (last_full - remains)*3600 / rate;
138 if (ch > charge_time)
143 discharge_mask |= 1 << batt_id;
144 discharge_rate += rate;
149 discharge_time = total_full*3600 / discharge_rate;
151 discharge_time = 1000000;
156 static void scan(void)
162 static char *batt_mask(char *p, unsigned int mask)
164 if (present_mask & (present_mask-1))
167 for (int i=0; mask; i++)
170 *p = (p == p0) ? ' ' : '+';
172 p += sprintf(p, "B%d", i);
179 static void show(void)
184 p += sprintf(p, "%d%%", 100*total_full/total_capa);
186 p += sprintf(p, "??%%");
187 if (discharge_mask && discharge_time < 1000000)
189 p += sprintf(p, " %d:%02d remains", discharge_time/3600, (discharge_time/60)%60);
190 batt_mask(p, discharge_mask);
192 else if (charge_mask)
194 p += sprintf(p, " %d:%02d charging", charge_time/3600, (charge_time/60)%60);
195 batt_mask(p, charge_mask);
198 p += sprintf(p, " AC");
200 p += sprintf(p, " BATT");
201 xosd_display(osd, 0, XOSD_string, status);
204 static void my_sleep(int sec)
206 struct timespec ts = { .tv_sec = sec };
207 nanosleep(&ts, NULL);
210 static void sig_show(int sig)
215 my_sleep(SHOW_DURATION);
220 static void sig_time(int sig)
223 time_t now = time(NULL);
224 struct tm *tm = localtime(&now);
226 strftime(buf, sizeof(buf), "%d-%m-%Y %H:%M:%S", tm);
228 strcpy(buf, "??:??:??");
229 xosd_display(osd, 0, XOSD_string, buf);
230 my_sleep(SHOW_DURATION);
234 static void sig_check(int sig)
237 if (discharge_mask && discharge_time < WARN_THRESHOLD)
240 my_sleep(SHOW_DURATION);
246 int main(int argc, char **argv)
248 // If daemon mode is requested, fork first.
249 int daemon = (argc > 1 && !strcmp(argv[1], "--daemon"));
250 int watcher = daemon || (argc > 1 && !strcmp(argv[1], "--watch"));
256 fprintf(stderr, "batt: Cannot fork: %m\n");
264 // Block SIGALRM and SIGUSR1, because libxosd will start its own thread
265 // and we don't want it to accept these signals later;
266 sigset_t our_signals;
267 sigemptyset(&our_signals);
268 sigaddset(&our_signals, SIGALRM);
269 sigaddset(&our_signals, SIGUSR1);
270 sigaddset(&our_signals, SIGUSR2);
271 sigprocmask(SIG_BLOCK, &our_signals, NULL);
273 osd = xosd_create(1);
276 fprintf(stderr, "batt: Cannot create on-screen display\n");
279 xosd_set_font(osd, "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*");
280 xosd_set_outline_offset(osd, 2);
281 xosd_set_outline_colour(osd, "black");
282 xosd_set_pos(osd, XOSD_middle);
283 xosd_set_align(osd, XOSD_center);
289 my_sleep(SHOW_DURATION);
294 // Set up signal handlers from which we do everything asynchronously.
295 struct sigaction sa = {
296 .sa_handler = sig_show,
297 .sa_mask = our_signals,
299 sigaction(SIGUSR1, &sa, NULL);
300 sa.sa_handler = sig_time;
301 sigaction(SIGUSR2, &sa, NULL);
302 sa.sa_handler = sig_check;
303 sigaction(SIGALRM, &sa, NULL);
304 sigprocmask(SIG_UNBLOCK, &our_signals, NULL);
307 // We use this connection only for detecting that the session has ended.
308 // In such cases, we are terminated automatically by XLib.
309 Display *dpy = XOpenDisplay(NULL);
312 fprintf(stderr, "batt: Cannot open display\n");
318 XNextEvent(dpy, &ev);