From 2867b0d0e025f8ecb6915c000f3805abc0002754 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Fri, 13 Jul 2007 12:23:20 +0200 Subject: [PATCH] New utility for monitoring battery status. --- Makefile | 3 + batt.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 batt.c diff --git a/Makefile b/Makefile index d58e035..ff28ffe 100644 --- a/Makefile +++ b/Makefile @@ -8,5 +8,8 @@ all: parrot: parrot.c +batt: CFLAGS+=$(shell xosd-config --cflags) +batt: LDFLAGS+=$(shell xosd-config --libs) + clean: rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*` diff --git a/batt.c b/batt.c new file mode 100644 index 0000000..f8e1ddc --- /dev/null +++ b/batt.c @@ -0,0 +1,230 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static xosd *osd; +static int total_discharge, total_charge, total_full, total_capa; + +#define CHECK_PERIOD 10 +#define SHOW_DURATION 2 +#define WARN_THRESHOLD 600 + +static void scan_batt(void) +{ + total_discharge = total_charge = 0; + total_full = total_capa = 0; + + const char dir[] = "/proc/acpi/battery"; + DIR *d = opendir(dir); + if (!d) + return; + + struct dirent *e; + while (e = readdir(d)) + { + if (e->d_name[0] == '.') + continue; + char n[sizeof(dir) + 1 + strlen(e->d_name) + 6]; + const char * const names[] = { "state", "info" }; + int present = 0; + int rate = 0; + int charging = 0; + int remains = 0; + int last_full = 0; + for (int i=0; i<2; i++) + { + sprintf(n, "%s/%s/%s", dir, e->d_name, names[i]); + FILE *f = fopen(n, "r"); + if (!f) + continue; + char line[1024]; + while (fgets(line, sizeof(line)-1, f)) + { + char *t = strchr(line, '\n'); + if (t) + *t = 0; + char *val = strchr(line, ':'); + if (!val) + continue; + *val++ = 0; + while (*val == ' ' || *val == '\t') + *val++ = 0; + // printf("<%s> <%s>\n", line, val); + if (!strcmp(line, "present")) + present = !strcmp(val, "yes"); + else if (!strcmp(line, "charging state")) + charging = !strcmp(val, "charging"); + else if (!strcmp(line, "present rate")) + rate = atol(val); + else if (!strcmp(line, "remaining capacity")) + remains = atol(val); + else if (!strcmp(line, "last full capacity")) + last_full = atol(val); + } + fclose(f); + } + if (present) + { + total_full += remains; + total_capa += last_full; + if (charging) + { + int ch = last_full*3600 / rate; + if (ch > total_charge) + total_charge = ch; + } + else + { + if (rate <= 0) + total_discharge += 359999; + else + total_discharge += remains*3600 / rate; + } + } + } + + closedir(d); +} + +static void show(void) +{ + char status[256]; + char *p = status; + if (total_capa) + p += sprintf(p, "%d%%", 100*total_full/total_capa); + else + p += sprintf(p, "??%%"); + if (total_discharge) + p += sprintf(p, " %d:%02d remains", total_discharge/3600, (total_discharge/60)%60); + else if (total_charge) + p += sprintf(p, " %d:%02d charging", total_charge/3600, (total_charge/60)%60); + else + p += sprintf(p, " ????"); + xosd_display(osd, 0, XOSD_string, status); +} + +static void my_sleep(int sec) +{ + struct timespec ts = { .tv_sec = sec }; + nanosleep(&ts, NULL); +} + +static void sig_show(int sig) +{ + alarm(0); + scan_batt(); + show(); + my_sleep(SHOW_DURATION); + xosd_hide(osd); + alarm(CHECK_PERIOD); +} + +static void sig_time(int sig) +{ + char buf[256]; + time_t now = time(NULL); + struct tm *tm = localtime(&now); + if (tm) + strftime(buf, sizeof(buf), "%d-%m-%Y %H:%M:%S", tm); + else + strcpy(buf, "??:??:??"); + xosd_display(osd, 0, XOSD_string, buf); + my_sleep(SHOW_DURATION); + xosd_hide(osd); +} + +static void sig_check(int sig) +{ + scan_batt(); + if (total_discharge && total_discharge < WARN_THRESHOLD) + { + show(); + my_sleep(SHOW_DURATION); + xosd_hide(osd); + } + alarm(CHECK_PERIOD); +} + +int main(int argc, char **argv) +{ + // If daemon mode is requested, fork first. + int daemon = (argc > 1 && !strcmp(argv[1], "--daemon")); + int watcher = daemon || (argc > 1 && !strcmp(argv[1], "--watch")); + if (daemon) + { + pid_t pid = fork(); + if (pid < 0) + { + fprintf(stderr, "batt: Cannot fork: %m\n"); + return 1; + } + if (pid > 0) + return 0; + setsid(); + } + + // Block SIGALRM and SIGUSR1, because libxosd will start its own thread + // and we don't want it to accept these signals later; + sigset_t our_signals; + sigemptyset(&our_signals); + sigaddset(&our_signals, SIGALRM); + sigaddset(&our_signals, SIGUSR1); + sigaddset(&our_signals, SIGUSR2); + sigprocmask(SIG_BLOCK, &our_signals, NULL); + + osd = xosd_create(1); + if (!osd) + { + fprintf(stderr, "batt: Cannot create on-screen display\n"); + return 1; + } + xosd_set_font(osd, "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*"); + xosd_set_outline_offset(osd, 2); + xosd_set_outline_colour(osd, "black"); + xosd_set_pos(osd, XOSD_middle); + xosd_set_align(osd, XOSD_center); + + if (!watcher) + { + scan_batt(); + show(); + my_sleep(SHOW_DURATION); + xosd_destroy(osd); + return 0; + } + + // Set up signal handlers from which we do everything asynchronously. + struct sigaction sa = { + .sa_handler = sig_show, + .sa_mask = our_signals, + }; + sigaction(SIGUSR1, &sa, NULL); + sa.sa_handler = sig_time; + sigaction(SIGUSR2, &sa, NULL); + sa.sa_handler = sig_check; + sigaction(SIGALRM, &sa, NULL); + sigprocmask(SIG_UNBLOCK, &our_signals, NULL); + alarm(1); + + // We use this connection only for detecting that the session has ended. + // In such cases, we are terminated automatically by XLib. + Display *dpy = XOpenDisplay(NULL); + if (!dpy) + { + fprintf(stderr, "batt: Cannot open display\n"); + return 1; + } + for (;;) + { + XEvent ev; + XNextEvent(dpy, &ev); + } + + return 0; +} -- 2.39.2