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