]> mj.ucw.cz Git - osdd.git/blobdiff - osd-batt.c
Added a battery checking client
[osdd.git] / osd-batt.c
diff --git a/osd-batt.c b/osd-batt.c
new file mode 100644 (file)
index 0000000..9d7a6ca
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ *     A Simple Battery Status Display via OSD
+ *
+ *     (c) 2007--2010 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <getopt.h>
+
+#include "util.h"
+#include "send.h"
+
+static int check_mode;
+static int warn_threshold = 600;
+
+static int total_full, total_capa, discharge_rate;
+static int charge_time, discharge_time;
+static int ac_online;
+static unsigned int present_mask, charge_mask, discharge_mask;
+
+static char *parse_line(char *line)
+{
+  char *t = strchr(line, '\n');
+  if (t)
+    *t = 0;
+  char *val = strchr(line, ':');
+  if (!val)
+    return NULL;
+  *val++ = 0;
+  while (*val == ' ' || *val == '\t')
+    *val++ = 0;
+  return val;
+}
+
+static void scan_ac(void)
+{
+  ac_online = 0;
+
+  const char dir[] = "/proc/acpi/ac_adapter";
+  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];
+      sprintf(n, "%s/%s/state", dir, e->d_name);
+      FILE *f = fopen(n, "r");
+      if (!f)
+       continue;
+      char line[1024];
+      while (fgets(line, sizeof(line)-1, f))
+       {
+         char *val = parse_line(line);
+         if (!val)
+           continue;
+         if (!strcmp(line, "state") && !strcmp(val, "on-line"))
+           ac_online = 1;
+       }
+      fclose(f);
+    }
+  closedir(d);
+}
+
+static void scan_batt(void)
+{
+  charge_time = discharge_time = 0;
+  total_full = total_capa = 0;
+  discharge_rate = 0;
+  present_mask = charge_mask = discharge_mask = 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;
+      int batt_id = -1;
+      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 *val = parse_line(line);
+             if (!val)
+               continue;
+             // 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);
+             else if (!strcmp(line, "serial number"))
+               batt_id = atol(val);
+           }
+         fclose(f);
+       }
+      if (present)
+        {
+         if (batt_id < 0 || batt_id > 31)
+           batt_id = 0;
+         present_mask |= 1 << batt_id;
+         total_full += remains;
+         total_capa += last_full;
+         if (charging)
+           {
+             charge_mask |= 1 << batt_id;
+             int ch = (last_full - remains)*3600 / rate;
+             if (ch > charge_time)
+               charge_time = ch;
+           }
+         else if (rate > 0)
+           {
+             discharge_mask |= 1 << batt_id;
+             discharge_rate += rate;
+           }
+       }
+    }
+  if (discharge_rate)
+    discharge_time = total_full*3600 / discharge_rate;
+  else
+    discharge_time = 1000000;
+
+  closedir(d);
+}
+
+static void scan(void)
+{
+  scan_ac();
+  scan_batt();
+}
+
+static char *batt_mask(char *p, unsigned int mask)
+{
+  if (present_mask & (present_mask-1))
+    {
+      char *p0 = p;
+      for (int i=0; mask; i++)
+        if (mask & (1 << i))
+         {
+           *p = (p == p0) ? ' ' : '+';
+           p++;
+           p += sprintf(p, "B%d", i);
+           mask &= ~(1 << i);
+         }
+    }
+  return p;
+}
+
+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 (discharge_mask && discharge_time < 1000000)
+    {
+      p += sprintf(p, "  %d:%02d  remains", discharge_time/3600, (discharge_time/60)%60);
+      batt_mask(p, discharge_mask);
+    }
+  else if (charge_mask)
+    {
+      p += sprintf(p, "  %d:%02d  charging", charge_time/3600, (charge_time/60)%60);
+      batt_mask(p, charge_mask);
+    }
+  else if (ac_online)
+    p += sprintf(p, " AC");
+  else
+    p += sprintf(p, " BATT");
+
+  struct osd_msg *msg = osd_new_msg();
+  osd_add_line(msg, NULL, status);
+  osd_send(msg);
+}
+
+static void NONRET
+usage(void)
+{
+  fprintf(stderr, "\
+Usage: osd-batt <options>\n\
+\n\
+Options:\n\
+-c, --check\t\tDisplay status only if battery is low\n\
+-w, --warn=<sec>\tBattery is low if less than <sec> seconds remain\n\
+");
+  exit(1);
+}
+
+static const char short_opts[] = "cw:";
+
+static const struct option long_opts[] = {
+  { "check",           no_argument,            NULL,   'c' },
+  { "warn",            required_argument,      NULL,   'w' },
+  { NULL,              0,                      NULL,   0   },
+};
+
+int main(int argc, char **argv)
+{
+  int opt;
+  while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
+    switch (opt)
+      {
+      case 'c':
+       check_mode++;
+       break;
+      case 'w':
+       warn_threshold = atoi(optarg);
+       break;
+      default:
+       usage();
+      }
+  if (optind < argc)
+    usage();
+
+  scan();
+  if (!check_mode || (discharge_mask && discharge_time < warn_threshold))
+    show();
+
+  return 0;
+}