]> mj.ucw.cz Git - osdd.git/blob - osd-batt.c
2d36aa0ddf3bd4a5389c9574d0d89f01f8ed6cc4
[osdd.git] / osd-batt.c
1 /*
2  *      A Simple Battery Status Display via OSD
3  *
4  *      (c) 2007--2012 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef DEBUG
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <dirent.h>
14 #include <getopt.h>
15 #include <fcntl.h>
16
17 #include "util.h"
18 #include "osd.h"
19
20 static int check_mode;
21 static int check_every;
22 static int warn_threshold = 600;
23
24 static int total_now, total_full, discharge_rate;
25 static int charge_time, discharge_time;
26 static int ac_online;
27 static unsigned int present_mask, charge_mask, discharge_mask;
28
29 static unsigned int last_charge_mask, last_discharge_mask;
30 static int last_ac_online = -1;
31
32 #define MAX_BATTS 4
33 #define BATT_NAME_LEN 32
34 static char batt_names[MAX_BATTS][BATT_NAME_LEN];
35
36 static char sys_dir[256];
37 #define BUFSIZE 256
38
39 static int sys_read(char *buf, char *attribute)
40 {
41   char name[256];
42   snprintf(name, sizeof(name), "%s/%s", sys_dir, attribute);
43
44   int fd = open(name, O_RDONLY);
45   if (fd < 0)
46     return 0;
47
48   int n = read(fd, buf, BUFSIZE);
49   close(fd);
50   if (n < 0)
51     return 0;
52
53   buf[BUFSIZE-1] = 0;
54   char *nl = strchr(buf, '\n');
55   if (nl)
56     *nl = 0;
57   DBG("\t%s=%s\n", attribute, buf);
58   return 1;
59 }
60
61 static int sys_read_int(char *attribute, int default_value)
62 {
63   char buf[BUFSIZE];
64   if (!sys_read(buf, attribute) || !buf[0])
65     return default_value;
66   else
67     return atoi(buf);
68 }
69
70 static void parse_ac(void)
71 {
72   ac_online = sys_read_int("online", 0);
73 }
74
75 static int get_batt_id(char *batt_name)
76 {
77   for (int i=0; i<MAX_BATTS; i++)
78     {
79       if (!strcmp(batt_names[i], batt_name))
80         return i;
81       if (!batt_names[i][0])
82         {
83           snprintf(batt_names[i], BATT_NAME_LEN, "%s", batt_name);
84           return i;
85         }
86     }
87   return MAX_BATTS;
88 }
89
90 static void parse_batt(char *batt_name)
91 {
92   int batt_id = get_batt_id(batt_name);
93   DBG("\t-> id %d\n", batt_id);
94
95   if (!sys_read_int("present", 1))
96     return;
97
98   char status[BUFSIZE];
99   int charging = sys_read(status, "status") && !strcmp(status, "Charging");
100   int charge_full = sys_read_int("charge_full", 0);
101   int charge_now = sys_read_int("charge_now", 0);
102   int current_now = sys_read_int("current_now", 0);
103
104   present_mask |= 1 << batt_id;
105   total_now += charge_now;
106   total_full += charge_full;
107   if (charging && current_now > 0)
108     {
109       charge_mask |= 1 << batt_id;
110       int ch = (long long)(charge_full - charge_now)*3600 / current_now;
111       if (ch > charge_time)
112         charge_time = ch;
113     }
114   else if (current_now > 0)
115     {
116       discharge_mask |= 1 << batt_id;
117       discharge_rate += current_now;
118     }
119 }
120
121 static void scan(void)
122 {
123   ac_online = 0;
124   charge_time = discharge_time = 0;
125   total_now = total_full = 0;
126   discharge_rate = 0;
127   present_mask = charge_mask = discharge_mask = 0;
128
129   const char dir[] = "/sys/class/power_supply";
130   DIR *d = opendir(dir);
131   if (!d)
132     return;
133
134   struct dirent *e;
135   while (e = readdir(d))
136     {
137       if (e->d_name[0] == '.')
138         continue;
139       snprintf(sys_dir, sizeof(sys_dir), "%s/%s", dir, e->d_name);
140       DBG("%s\n", sys_dir);
141
142       char type[BUFSIZE];
143       if (!sys_read(type, "type"))
144         continue;
145
146       if (!strcmp(type, "Mains"))
147         parse_ac();
148       else if (!strcmp(type, "Battery"))
149         parse_batt(e->d_name);
150     }
151   if (discharge_rate)
152     discharge_time = (long long) total_now*3600 / discharge_rate;
153   else
154     discharge_time = 1000000;
155
156   closedir(d);
157   DBG("=> Capacity: now=%d full=%d\n", total_now, total_full);
158   DBG("=> Charge: mask=%d time=%d\n", charge_mask, charge_time);
159   DBG("=> Discharge: mask=%d rate=%d time=%d\n", discharge_mask, discharge_rate, discharge_time);
160 }
161
162 static char *batt_mask(char *p, unsigned int mask)
163 {
164   if (present_mask & (present_mask-1))
165     {
166       char *p0 = p;
167       for (int i=0; mask; i++)
168         if (mask & (1 << i))
169           {
170             *p = (p == p0) ? ' ' : '+';
171             p++;
172             p += sprintf(p, "B%d", i);
173             mask &= ~(1 << i);
174           }
175     }
176   return p;
177 }
178
179 static void show(void)
180 {
181   char status[256];
182   char *p = status;
183   if (total_full)
184     p += sprintf(p, "%d%%", (int)((long long)100*total_now/total_full));
185   else
186     p += sprintf(p, "??%%");
187   if (discharge_mask && discharge_time < 1000000)
188     {
189       p += sprintf(p, "  %d:%02d  remains", discharge_time/3600, (discharge_time/60)%60);
190       batt_mask(p, discharge_mask);
191     }
192   else if (charge_mask)
193     {
194       p += sprintf(p, "  %d:%02d  charging", charge_time/3600, (charge_time/60)%60);
195       batt_mask(p, charge_mask);
196     }
197   else if (ac_online)
198     p += sprintf(p, " AC");
199   else
200     p += sprintf(p, " BATT");
201
202   struct osd_msg *msg = osd_new_msg();
203   osd_add_line(msg, NULL, status);
204   osd_send(msg);
205 }
206
207 static void show_if_warn(void)
208 {
209   if (discharge_mask && discharge_time < warn_threshold ||
210       last_ac_online >= 0 && (
211         charge_mask != last_charge_mask ||
212         discharge_mask != last_discharge_mask ||
213         ac_online != last_ac_online))
214     show();
215
216   last_charge_mask = charge_mask;
217   last_discharge_mask = discharge_mask;
218   last_ac_online = ac_online;
219 }
220
221 static void NONRET
222 usage(void)
223 {
224   fprintf(stderr, "\
225 Usage: osd-batt <options>\n\
226 \n\
227 Options:\n\
228 -c, --check\t\tDisplay status only if battery is low\n\
229 -e, --check-every=<sec>\tRun on background and check every <sec> seconds\n\
230 -w, --warn=<sec>\tBattery is low if less than <sec> seconds remain (default: 600)\n\
231 ");
232   exit(1);
233 }
234
235 static const char short_opts[] = "ce:w:";
236
237 static const struct option long_opts[] = {
238   { "check",            no_argument,            NULL,   'c' },
239   { "check-every",      required_argument,      NULL,   'e' },
240   { "warn",             required_argument,      NULL,   'w' },
241   { NULL,               0,                      NULL,   0   },
242 };
243
244 int main(int argc, char **argv)
245 {
246   int opt;
247   while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
248     switch (opt)
249       {
250       case 'c':
251         check_mode++;
252         break;
253       case 'e':
254         check_every = atoi(optarg);
255         break;
256       case 'w':
257         warn_threshold = atoi(optarg);
258         break;
259       default:
260         usage();
261       }
262   if (optind < argc)
263     usage();
264
265   if (check_every)
266     {
267       osd_fork();
268       for (;;)
269         {
270           scan();
271           show_if_warn();
272           osd_wait(check_every);
273         }
274     }
275
276   scan();
277   if (check_mode)
278     show_if_warn();
279   else
280     show();
281
282   return 0;
283 }