]> mj.ucw.cz Git - misc.git/commitdiff
New utility for monitoring battery status.
authorMartin Mares <mj@ucw.cz>
Fri, 13 Jul 2007 10:23:20 +0000 (12:23 +0200)
committerMartin Mares <mj@ucw.cz>
Fri, 13 Jul 2007 10:23:20 +0000 (12:23 +0200)
Makefile
batt.c [new file with mode: 0644]

index d58e0356e3dad3cdb1d980e3af14744f4cf70df9..ff28ffef316839973c9c61509545c5e035c942f4 100644 (file)
--- 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 (file)
index 0000000..f8e1ddc
--- /dev/null
+++ b/batt.c
@@ -0,0 +1,230 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <signal.h>
+#include <time.h>
+#include <xosd.h>
+#include <X11/Xlib.h>
+
+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;
+}