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