--- /dev/null
+/*
+ * 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;
+}