]> mj.ucw.cz Git - osdd.git/blob - osd-batt.c
Added a battery checking client
[osdd.git] / osd-batt.c
1 /*
2  *      A Simple Battery Status Display via OSD
3  *
4  *      (c) 2007--2010 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <dirent.h>
12 #include <getopt.h>
13
14 #include "util.h"
15 #include "send.h"
16
17 static int check_mode;
18 static int warn_threshold = 600;
19
20 static int total_full, total_capa, discharge_rate;
21 static int charge_time, discharge_time;
22 static int ac_online;
23 static unsigned int present_mask, charge_mask, discharge_mask;
24
25 static char *parse_line(char *line)
26 {
27   char *t = strchr(line, '\n');
28   if (t)
29     *t = 0;
30   char *val = strchr(line, ':');
31   if (!val)
32     return NULL;
33   *val++ = 0;
34   while (*val == ' ' || *val == '\t')
35     *val++ = 0;
36   return val;
37 }
38
39 static void scan_ac(void)
40 {
41   ac_online = 0;
42
43   const char dir[] = "/proc/acpi/ac_adapter";
44   DIR *d = opendir(dir);
45   if (!d)
46     return;
47
48   struct dirent *e;
49   while (e = readdir(d))
50     {
51       if (e->d_name[0] == '.')
52         continue;
53       char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
54       sprintf(n, "%s/%s/state", dir, e->d_name);
55       FILE *f = fopen(n, "r");
56       if (!f)
57         continue;
58       char line[1024];
59       while (fgets(line, sizeof(line)-1, f))
60         {
61           char *val = parse_line(line);
62           if (!val)
63             continue;
64           if (!strcmp(line, "state") && !strcmp(val, "on-line"))
65             ac_online = 1;
66         }
67       fclose(f);
68     }
69   closedir(d);
70 }
71
72 static void scan_batt(void)
73 {
74   charge_time = discharge_time = 0;
75   total_full = total_capa = 0;
76   discharge_rate = 0;
77   present_mask = charge_mask = discharge_mask = 0;
78
79   const char dir[] = "/proc/acpi/battery";
80   DIR *d = opendir(dir);
81   if (!d)
82     return;
83
84   struct dirent *e;
85   while (e = readdir(d))
86     {
87       if (e->d_name[0] == '.')
88         continue;
89       char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
90       const char * const names[] = { "state", "info" };
91       int present = 0;
92       int rate = 0;
93       int charging = 0;
94       int remains = 0;
95       int last_full = 0;
96       int batt_id = -1;
97       for (int i=0; i<2; i++)
98         {
99           sprintf(n, "%s/%s/%s", dir, e->d_name, names[i]);
100           FILE *f = fopen(n, "r");
101           if (!f)
102             continue;
103           char line[1024];
104           while (fgets(line, sizeof(line)-1, f))
105             {
106               char *val = parse_line(line);
107               if (!val)
108                 continue;
109               // printf("<%s> <%s>\n", line, val);
110               if (!strcmp(line, "present"))
111                 present = !strcmp(val, "yes");
112               else if (!strcmp(line, "charging state"))
113                 charging = !strcmp(val, "charging");
114               else if (!strcmp(line, "present rate"))
115                 rate = atol(val);
116               else if (!strcmp(line, "remaining capacity"))
117                 remains = atol(val);
118               else if (!strcmp(line, "last full capacity"))
119                 last_full = atol(val);
120               else if (!strcmp(line, "serial number"))
121                 batt_id = atol(val);
122             }
123           fclose(f);
124         }
125       if (present)
126         {
127           if (batt_id < 0 || batt_id > 31)
128             batt_id = 0;
129           present_mask |= 1 << batt_id;
130           total_full += remains;
131           total_capa += last_full;
132           if (charging)
133             {
134               charge_mask |= 1 << batt_id;
135               int ch = (last_full - remains)*3600 / rate;
136               if (ch > charge_time)
137                 charge_time = ch;
138             }
139           else if (rate > 0)
140             {
141               discharge_mask |= 1 << batt_id;
142               discharge_rate += rate;
143             }
144         }
145     }
146   if (discharge_rate)
147     discharge_time = total_full*3600 / discharge_rate;
148   else
149     discharge_time = 1000000;
150
151   closedir(d);
152 }
153
154 static void scan(void)
155 {
156   scan_ac();
157   scan_batt();
158 }
159
160 static char *batt_mask(char *p, unsigned int mask)
161 {
162   if (present_mask & (present_mask-1))
163     {
164       char *p0 = p;
165       for (int i=0; mask; i++)
166         if (mask & (1 << i))
167           {
168             *p = (p == p0) ? ' ' : '+';
169             p++;
170             p += sprintf(p, "B%d", i);
171             mask &= ~(1 << i);
172           }
173     }
174   return p;
175 }
176
177 static void show(void)
178 {
179   char status[256];
180   char *p = status;
181   if (total_capa)
182     p += sprintf(p, "%d%%", 100*total_full/total_capa);
183   else
184     p += sprintf(p, "??%%");
185   if (discharge_mask && discharge_time < 1000000)
186     {
187       p += sprintf(p, "  %d:%02d  remains", discharge_time/3600, (discharge_time/60)%60);
188       batt_mask(p, discharge_mask);
189     }
190   else if (charge_mask)
191     {
192       p += sprintf(p, "  %d:%02d  charging", charge_time/3600, (charge_time/60)%60);
193       batt_mask(p, charge_mask);
194     }
195   else if (ac_online)
196     p += sprintf(p, " AC");
197   else
198     p += sprintf(p, " BATT");
199
200   struct osd_msg *msg = osd_new_msg();
201   osd_add_line(msg, NULL, status);
202   osd_send(msg);
203 }
204
205 static void NONRET
206 usage(void)
207 {
208   fprintf(stderr, "\
209 Usage: osd-batt <options>\n\
210 \n\
211 Options:\n\
212 -c, --check\t\tDisplay status only if battery is low\n\
213 -w, --warn=<sec>\tBattery is low if less than <sec> seconds remain\n\
214 ");
215   exit(1);
216 }
217
218 static const char short_opts[] = "cw:";
219
220 static const struct option long_opts[] = {
221   { "check",            no_argument,            NULL,   'c' },
222   { "warn",             required_argument,      NULL,   'w' },
223   { NULL,               0,                      NULL,   0   },
224 };
225
226 int main(int argc, char **argv)
227 {
228   int opt;
229   while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
230     switch (opt)
231       {
232       case 'c':
233         check_mode++;
234         break;
235       case 'w':
236         warn_threshold = atoi(optarg);
237         break;
238       default:
239         usage();
240       }
241   if (optind < argc)
242     usage();
243
244   scan();
245   if (!check_mode || (discharge_mask && discharge_time < warn_threshold))
246     show();
247
248   return 0;
249 }