]> mj.ucw.cz Git - misc.git/blob - batt.c
New utility for monitoring battery status.
[misc.git] / batt.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <dirent.h>
6 #include <signal.h>
7 #include <time.h>
8 #include <xosd.h>
9 #include <X11/Xlib.h>
10
11 static xosd *osd;
12 static int total_discharge, total_charge, total_full, total_capa;
13
14 #define CHECK_PERIOD 10
15 #define SHOW_DURATION 2
16 #define WARN_THRESHOLD 600
17
18 static void scan_batt(void)
19 {
20   total_discharge = total_charge = 0;
21   total_full = total_capa = 0;
22
23   const char dir[] = "/proc/acpi/battery";
24   DIR *d = opendir(dir);
25   if (!d)
26     return;
27
28   struct dirent *e;
29   while (e = readdir(d))
30     {
31       if (e->d_name[0] == '.')
32         continue;
33       char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
34       const char * const names[] = { "state", "info" };
35       int present = 0;
36       int rate = 0;
37       int charging = 0;
38       int remains = 0;
39       int last_full = 0;
40       for (int i=0; i<2; i++)
41         {
42           sprintf(n, "%s/%s/%s", dir, e->d_name, names[i]);
43           FILE *f = fopen(n, "r");
44           if (!f)
45             continue;
46           char line[1024];
47           while (fgets(line, sizeof(line)-1, f))
48             {
49               char *t = strchr(line, '\n');
50               if (t)
51                 *t = 0;
52               char *val = strchr(line, ':');
53               if (!val)
54                 continue;
55               *val++ = 0;
56               while (*val == ' ' || *val == '\t')
57                 *val++ = 0;
58               // printf("<%s> <%s>\n", line, val);
59               if (!strcmp(line, "present"))
60                 present = !strcmp(val, "yes");
61               else if (!strcmp(line, "charging state"))
62                 charging = !strcmp(val, "charging");
63               else if (!strcmp(line, "present rate"))
64                 rate = atol(val);
65               else if (!strcmp(line, "remaining capacity"))
66                 remains = atol(val);
67               else if (!strcmp(line, "last full capacity"))
68                 last_full = atol(val);
69             }
70           fclose(f);
71         }
72       if (present)
73         {
74           total_full += remains;
75           total_capa += last_full;
76           if (charging)
77             {
78               int ch = last_full*3600 / rate;
79               if (ch > total_charge)
80                 total_charge = ch;
81             }
82           else
83             {
84               if (rate <= 0)
85                 total_discharge += 359999;
86               else
87                 total_discharge += remains*3600 / rate;
88             }
89         }
90     }
91
92   closedir(d);
93 }
94
95 static void show(void)
96 {
97   char status[256];
98   char *p = status;
99   if (total_capa)
100     p += sprintf(p, "%d%%", 100*total_full/total_capa);
101   else
102     p += sprintf(p, "??%%");
103   if (total_discharge)
104     p += sprintf(p, "  %d:%02d  remains", total_discharge/3600, (total_discharge/60)%60);
105   else if (total_charge)
106     p += sprintf(p, "  %d:%02d  charging", total_charge/3600, (total_charge/60)%60);
107   else
108     p += sprintf(p, "  ????");
109   xosd_display(osd, 0, XOSD_string, status);
110 }
111
112 static void my_sleep(int sec)
113 {
114   struct timespec ts = { .tv_sec = sec };
115   nanosleep(&ts, NULL);
116 }
117
118 static void sig_show(int sig)
119 {
120   alarm(0);
121   scan_batt();
122   show();
123   my_sleep(SHOW_DURATION);
124   xosd_hide(osd);
125   alarm(CHECK_PERIOD);
126 }
127
128 static void sig_time(int sig)
129 {
130   char buf[256];
131   time_t now = time(NULL);
132   struct tm *tm = localtime(&now);
133   if (tm)
134     strftime(buf, sizeof(buf), "%d-%m-%Y  %H:%M:%S", tm);
135   else
136     strcpy(buf, "??:??:??");
137   xosd_display(osd, 0, XOSD_string, buf);
138   my_sleep(SHOW_DURATION);
139   xosd_hide(osd);
140 }
141
142 static void sig_check(int sig)
143 {
144   scan_batt();
145   if (total_discharge && total_discharge < WARN_THRESHOLD)
146     {
147       show();
148       my_sleep(SHOW_DURATION);
149       xosd_hide(osd);
150     }
151   alarm(CHECK_PERIOD);
152 }
153
154 int main(int argc, char **argv)
155 {
156   // If daemon mode is requested, fork first.
157   int daemon = (argc > 1 && !strcmp(argv[1], "--daemon"));
158   int watcher = daemon || (argc > 1 && !strcmp(argv[1], "--watch"));
159   if (daemon)
160     {
161       pid_t pid = fork();
162       if (pid < 0)
163         {
164           fprintf(stderr, "batt: Cannot fork: %m\n");
165           return 1;
166         }
167       if (pid > 0)
168         return 0;
169       setsid();
170     }
171
172   // Block SIGALRM and SIGUSR1, because libxosd will start its own thread
173   // and we don't want it to accept these signals later;
174   sigset_t our_signals;
175   sigemptyset(&our_signals);
176   sigaddset(&our_signals, SIGALRM);
177   sigaddset(&our_signals, SIGUSR1);
178   sigaddset(&our_signals, SIGUSR2);
179   sigprocmask(SIG_BLOCK, &our_signals, NULL);
180
181   osd = xosd_create(1);
182   if (!osd)
183     {
184       fprintf(stderr, "batt: Cannot create on-screen display\n");
185       return 1;
186     }
187   xosd_set_font(osd, "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*");
188   xosd_set_outline_offset(osd, 2);
189   xosd_set_outline_colour(osd, "black");
190   xosd_set_pos(osd, XOSD_middle);
191   xosd_set_align(osd, XOSD_center);
192
193   if (!watcher)
194     {
195       scan_batt();
196       show();
197       my_sleep(SHOW_DURATION);
198       xosd_destroy(osd);
199       return 0;
200     }
201
202   // Set up signal handlers from which we do everything asynchronously.
203   struct sigaction sa = {
204     .sa_handler = sig_show,
205     .sa_mask = our_signals,
206   };
207   sigaction(SIGUSR1, &sa, NULL);
208   sa.sa_handler = sig_time;
209   sigaction(SIGUSR2, &sa, NULL);
210   sa.sa_handler = sig_check;
211   sigaction(SIGALRM, &sa, NULL);
212   sigprocmask(SIG_UNBLOCK, &our_signals, NULL);
213   alarm(1);
214
215   // We use this connection only for detecting that the session has ended.
216   // In such cases, we are terminated automatically by XLib.
217   Display *dpy = XOpenDisplay(NULL);
218   if (!dpy)
219     {
220       fprintf(stderr, "batt: Cannot open display\n");
221       return 1;
222     }
223   for (;;)
224     {
225       XEvent ev;
226       XNextEvent(dpy, &ev);
227     }
228
229   return 0;
230 }