]> mj.ucw.cz Git - misc.git/blob - batt.c
Merge with git+ssh://git.ucw.cz/home/mj/GIT/misc.git
[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 60
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 - remains)*3600 / rate;
79               if (ch > total_charge)
80                 total_charge = ch;
81             }
82           else
83             {
84               if (rate <= 0)
85                 total_discharge += 1000000;
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 && total_discharge < 1000000)
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   xosd_display(osd, 0, XOSD_string, status);
108 }
109
110 static void my_sleep(int sec)
111 {
112   struct timespec ts = { .tv_sec = sec };
113   nanosleep(&ts, NULL);
114 }
115
116 static void sig_show(int sig)
117 {
118   alarm(0);
119   scan_batt();
120   show();
121   my_sleep(SHOW_DURATION);
122   xosd_hide(osd);
123   alarm(CHECK_PERIOD);
124 }
125
126 static void sig_time(int sig)
127 {
128   char buf[256];
129   time_t now = time(NULL);
130   struct tm *tm = localtime(&now);
131   if (tm)
132     strftime(buf, sizeof(buf), "%d-%m-%Y  %H:%M:%S", tm);
133   else
134     strcpy(buf, "??:??:??");
135   xosd_display(osd, 0, XOSD_string, buf);
136   my_sleep(SHOW_DURATION);
137   xosd_hide(osd);
138 }
139
140 static void sig_check(int sig)
141 {
142   scan_batt();
143   if (total_discharge && total_discharge < WARN_THRESHOLD)
144     {
145       show();
146       my_sleep(SHOW_DURATION);
147       xosd_hide(osd);
148     }
149   alarm(CHECK_PERIOD);
150 }
151
152 int main(int argc, char **argv)
153 {
154   // If daemon mode is requested, fork first.
155   int daemon = (argc > 1 && !strcmp(argv[1], "--daemon"));
156   int watcher = daemon || (argc > 1 && !strcmp(argv[1], "--watch"));
157   if (daemon)
158     {
159       pid_t pid = fork();
160       if (pid < 0)
161         {
162           fprintf(stderr, "batt: Cannot fork: %m\n");
163           return 1;
164         }
165       if (pid > 0)
166         return 0;
167       setsid();
168     }
169
170   // Block SIGALRM and SIGUSR1, because libxosd will start its own thread
171   // and we don't want it to accept these signals later;
172   sigset_t our_signals;
173   sigemptyset(&our_signals);
174   sigaddset(&our_signals, SIGALRM);
175   sigaddset(&our_signals, SIGUSR1);
176   sigaddset(&our_signals, SIGUSR2);
177   sigprocmask(SIG_BLOCK, &our_signals, NULL);
178
179   osd = xosd_create(1);
180   if (!osd)
181     {
182       fprintf(stderr, "batt: Cannot create on-screen display\n");
183       return 1;
184     }
185   xosd_set_font(osd, "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*");
186   xosd_set_outline_offset(osd, 2);
187   xosd_set_outline_colour(osd, "black");
188   xosd_set_pos(osd, XOSD_middle);
189   xosd_set_align(osd, XOSD_center);
190
191   if (!watcher)
192     {
193       scan_batt();
194       show();
195       my_sleep(SHOW_DURATION);
196       xosd_destroy(osd);
197       return 0;
198     }
199
200   // Set up signal handlers from which we do everything asynchronously.
201   struct sigaction sa = {
202     .sa_handler = sig_show,
203     .sa_mask = our_signals,
204   };
205   sigaction(SIGUSR1, &sa, NULL);
206   sa.sa_handler = sig_time;
207   sigaction(SIGUSR2, &sa, NULL);
208   sa.sa_handler = sig_check;
209   sigaction(SIGALRM, &sa, NULL);
210   sigprocmask(SIG_UNBLOCK, &our_signals, NULL);
211   alarm(1);
212
213   // We use this connection only for detecting that the session has ended.
214   // In such cases, we are terminated automatically by XLib.
215   Display *dpy = XOpenDisplay(NULL);
216   if (!dpy)
217     {
218       fprintf(stderr, "batt: Cannot open display\n");
219       return 1;
220     }
221   for (;;)
222     {
223       XEvent ev;
224       XNextEvent(dpy, &ev);
225     }
226
227   return 0;
228 }