priv drop, hide on USR1 signal
[pandora_liveinfo.git] / main.c
1 /*
2  * pandora live info
3  * (C) notaz, 2014
4  *
5  * This work is licensed under the terms of 3-clause BSD license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdarg.h>
13 #include <pthread.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <sys/ioctl.h>
18 #include <sys/mman.h>
19 #include <unistd.h>
20 #include <sys/time.h>
21 #include <time.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <signal.h>
25 #include <poll.h>
26 #include <linux/fb.h>
27 #include <linux/omapfb.h>
28 #include <stdint.h>
29 #include "fonts.h"
30
31 #define SCREEN_WIDTH 800
32 #define SCREEN_HEIGHT 480
33 #define WIDTH 80
34 #define HEIGHT 480
35 #define MEM_SIZE (((WIDTH * HEIGHT * 2 * 2) + 0xfff) & ~0xfff)
36 #define Y_STEP 9
37
38 static struct fb_var_screeninfo g_vi;
39 static uint16_t *g_screen_base, *g_screen;
40 static unsigned int g_old_mem;
41 static int g_flip_id;
42 static int g_exit, g_hide;
43 static pthread_cond_t g_cond;
44
45 #define IS_START(buf, str) \
46   !strncmp(buf, str, sizeof(str) - 1)
47
48 static void clear_bytes(void *mem, int bytes);
49
50 static int setup_layer(int fd)
51 {
52   struct omapfb_plane_info pi;
53   struct omapfb_mem_info mi;
54   struct omapfb_color_key key;
55   int ret;
56
57   ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
58   if (ret < 0) {
59     perror("ioctl OMAPFB_QUERY_PLANE");
60     return 1;
61   }
62
63   ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
64   if (ret < 0) {
65     perror("ioctl OMAPFB_QUERY_MEM");
66     return 1;
67   }
68
69   /* must disable when changing stuff */
70   if (pi.enabled) {
71     pi.enabled = 0;
72     ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
73     if (ret < 0) {
74       perror("ioctl OMAPFB_SETUP_PLANE (d)");
75       return 1;
76     }
77   }
78
79   g_old_mem = mi.size;
80   if (mi.size < MEM_SIZE) {
81     mi.size = MEM_SIZE;
82     ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
83     if (ret < 0) {
84       perror("ioctl SETUP_MEM");
85       return 1;
86     }
87   }
88
89   pi.pos_x = SCREEN_WIDTH - WIDTH;
90   pi.pos_y = 0;
91   pi.out_width = WIDTH;
92   pi.out_height = HEIGHT;
93   ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
94   if (ret < 0) {
95     perror("ioctl OMAPFB_SETUP_PLANE (e1)");
96     return 1;
97   }
98
99   ret = ioctl(fd, FBIOGET_VSCREENINFO, &g_vi);
100   if (ret < 0) {
101     perror("ioctl FBIOGET_VSCREENINFO");
102     return 1;
103   }
104
105   g_vi.xres = g_vi.xres_virtual = WIDTH;
106   g_vi.yres = HEIGHT;
107   g_vi.yres_virtual = HEIGHT * 2;
108   g_vi.bits_per_pixel = 16;
109
110   ret = ioctl(fd, FBIOPUT_VSCREENINFO, &g_vi);
111   if (ret < 0) {
112     perror("ioctl FBIOPUT_VSCREENINFO");
113     return 1;
114   }
115
116   pi.enabled = 1;
117   ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
118   if (ret < 0) {
119     perror("ioctl OMAPFB_SETUP_PLANE (e2)");
120     return 1;
121   }
122
123   memset(&key, 0, sizeof(key));
124   key.key_type = OMAPFB_COLOR_KEY_VID_SRC;
125   key.trans_key = 0x07e0;
126
127   ret = ioctl(fd, OMAPFB_SET_COLOR_KEY, &key);
128   if (ret < 0) {
129     perror("ioctl OMAPFB_SET_COLOR_KEY");
130     return 1;
131   }
132
133   g_screen_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
134                   MAP_SHARED, fd, 0);
135   if (g_screen_base == MAP_FAILED) {
136     perror("mmap");
137     g_screen = g_screen_base = NULL;
138     return 1;
139   }
140   clear_bytes(g_screen_base, MEM_SIZE);
141   g_screen = g_screen_base;
142   g_flip_id = 0;
143
144   return 0;
145 }
146
147 static int check_layer(int fd)
148 {
149   struct omapfb_plane_info pi;
150   int ret;
151
152   ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
153   if (ret < 0) {
154     perror("ioctl OMAPFB_QUERY_PLANE");
155     return 1;
156   }
157
158   if (!pi.enabled) {
159     printf("something disabled the layer (tv-out?), quitting\n");
160     return 1;
161   }
162
163   return 0;
164 }
165
166 static void remove_layer(int fd)
167 {
168   struct omapfb_plane_info pi;
169   struct omapfb_mem_info mi;
170   int ret;
171
172   if (g_screen_base != NULL) {
173     munmap(g_screen_base, MEM_SIZE);
174     g_screen = g_screen_base = NULL;
175   }
176
177   memset(&pi, 0, sizeof(pi));
178   pi.enabled = 0;
179   ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
180   if (ret < 0)
181     perror("ioctl OMAPFB_SETUP_PLANE (c)");
182
183   ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
184   if (ret < 0) {
185     perror("ioctl OMAPFB_QUERY_MEM");
186     goto out;
187   }
188
189   if (mi.size != g_old_mem) {
190     mi.size = g_old_mem;
191     ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
192     if (ret < 0) {
193       perror("ioctl SETUP_MEM");
194       goto out;
195     }
196   }
197
198 out:;
199 }
200
201 static void flip_fb(int fd)
202 {
203   g_vi.yoffset = g_flip_id * HEIGHT;
204   ioctl(fd, FBIOPAN_DISPLAY, &g_vi);
205   g_flip_id ^= 1;
206   g_screen = g_screen_base + g_flip_id * WIDTH * HEIGHT;
207 }
208
209 static void handle_term(int num)
210 {
211   g_exit = 1;
212   pthread_cond_signal(&g_cond);
213 }
214
215 static void handle_usr1(int num)
216 {
217   g_hide = !g_hide;
218   pthread_cond_signal(&g_cond);
219 }
220
221 #define s_printf(x, y, fmt, ...) \
222   basic_text_out16(g_screen, WIDTH, x, y, fmt, ##__VA_ARGS__);
223
224 static int read_int_file(const char *fn, int *val)
225 {
226   FILE *f;
227   int ret;
228
229   *val = 0;
230   f = fopen(fn, "r");
231   if (f == NULL)
232     return 1;
233   ret = fscanf(f, "%d", val);
234   fclose(f);
235   if (ret != 1)
236     return 1;
237
238   return 0;
239 }
240
241 static void clear_bytes(void *mem, int bytes)
242 {
243   int *p = mem;
244   int l = bytes / 4;
245
246   for (; l >= 4; l -= 4, p += 4)
247     p[0] = p[1] = p[2] = p[3] = 0x07e007e0;
248   for (; l >  0; l--, p++)
249     *p = 0x07e007e0;
250 }
251
252 static void clear(int h)
253 {
254   clear_bytes(g_screen, WIDTH * h * 2);
255 }
256
257 struct proc_stat {
258   unsigned long idlesum;
259   unsigned long sys;
260   unsigned long irqs;
261   unsigned long ctxt;
262 };
263
264 static struct {
265   struct proc_stat last_st;
266   int cpu_usage;
267   int cpu_sys;
268   int irqs;
269   int ctxt;
270   int mem_free;
271   int mem_huge;
272   int mem_free_swap;
273 } stats;
274
275 static void collect_proc_stat(void)
276 {
277   static long hz;
278   struct proc_stat st;
279   char buf[256];
280   unsigned long wait;
281   unsigned long iowait;
282   FILE *f;
283
284   if (hz == 0) {
285     hz = sysconf(_SC_CLK_TCK);
286     printf("hz: %ld\n", hz);
287   }
288
289   f = fopen("/proc/stat", "r");
290   if (f == NULL)
291     return;
292
293   if (fscanf(f, "cpu %*s %*s %lu %lu %lu",
294       &st.sys, &wait, &iowait) != 3)
295     goto out;
296   do {
297     if (fgets(buf, sizeof(buf), f) == NULL)
298       goto out;
299   } while (fscanf(f, "intr %lu", &st.irqs) != 1);
300   do {
301     if (fgets(buf, sizeof(buf), f) == NULL)
302       goto out;
303   } while (fscanf(f, "ctxt %lu", &st.ctxt) != 1);
304
305   st.idlesum = wait + iowait;
306
307   stats.cpu_usage = hz - (st.idlesum - stats.last_st.idlesum);
308   stats.cpu_sys = st.sys - stats.last_st.sys;
309   if (hz != 100) {
310     stats.cpu_usage = stats.cpu_usage * 100 / hz;
311     stats.cpu_sys = stats.cpu_sys * 100 / hz;
312   }
313   stats.irqs = st.irqs - stats.last_st.irqs;
314   stats.ctxt = st.ctxt - stats.last_st.ctxt;
315
316 out:
317   stats.last_st = st;
318   fclose(f);
319 }
320
321 static void collect_mem_stat(void)
322 {
323   static const char fn[] = "/proc/meminfo";
324   uint32_t total = 0, free = 0, buffers = 0, cached = 0;
325   uint32_t anonhuge = 0, hugetot = 0, hugefree = 0, hugepgsz = 0;
326   uint32_t free_swap = 0;
327   char buf[128];
328   FILE *f;
329
330   f = fopen(fn, "r");
331   if (f == NULL)
332     return;
333
334   while (fgets(buf, sizeof(buf), f)) {
335     if (total == 0 && sscanf(buf, "MemTotal: %u", &total) == 1)
336       continue;
337     if (free == 0 && sscanf(buf, "MemFree: %u", &free) == 1)
338       continue;
339     if (buffers == 0 && sscanf(buf, "Buffers: %u", &buffers) == 1)
340       continue;
341     if (cached == 0 && sscanf(buf, "Cached: %u", &cached) == 1)
342       continue;
343     if (free_swap == 0 && sscanf(buf, "SwapFree: %u", &free_swap) == 1)
344       continue;
345     if (anonhuge == 0 && sscanf(buf, "AnonHugePages: %u", &anonhuge) == 1)
346       continue;
347     if (hugetot == 0 && sscanf(buf, "HugePages_Total: %u", &hugetot) == 1)
348       continue;
349     if (hugefree == 0 && sscanf(buf, "HugePages_Free: %u", &hugefree) == 1)
350       continue;
351     if (hugepgsz == 0 && sscanf(buf, "Hugepagesize: %u", &hugepgsz) == 1)
352       continue;
353   }
354
355   fclose(f);
356
357   hugetot = (hugetot - hugefree) * hugepgsz;
358   free += buffers + cached;
359   stats.mem_free = free >> 10;
360   stats.mem_huge = hugetot >> 10;
361   stats.mem_free_swap = free_swap >> 10;
362 }
363
364 struct io_stat {
365   unsigned long sec_r;
366   unsigned long sec_w;
367 };
368
369 static struct {
370   struct io_stat last_st;
371   int read; // kB/s
372   int write;
373 } io_stats;
374
375 static void collect_io_stat(void)
376 {
377   unsigned long sec_r, sec_w;
378   struct io_stat st = { 0, };
379   char buf[128];
380   FILE *f;
381   int i;
382
383   for (i = 0; i < 2; i++) {
384     snprintf(buf, sizeof(buf), "/sys/block/mmcblk%d/stat", i);
385     f = fopen(buf, "r");
386     if (f == NULL)
387       continue;
388
389     if (fscanf(f, "%*s %*s %lu %*s %*s %*s %lu",
390                &sec_r, &sec_w) == 2)
391     {
392       st.sec_r += sec_r;
393       st.sec_w += sec_w;
394     }
395     fclose(f);
396   }
397
398   io_stats.read = st.sec_r - io_stats.last_st.sec_r;
399   io_stats.write = st.sec_w - io_stats.last_st.sec_w;
400   // assuming 512 byte sectors..
401   io_stats.read >>= 1;
402   io_stats.write >>= 1;
403
404   io_stats.last_st = st;
405 }
406
407 struct net_stat {
408   unsigned long rx;
409   unsigned long tx;
410 };
411
412 static struct {
413   struct net_stat last_st;
414   int down; // kB/s
415   int up;
416   int have_wlan;
417   int link;
418   int level;
419 } net_stats;
420
421 static void collect_net_stat(void)
422 {
423   struct net_stat st = { 0, };
424   unsigned long rx, tx;
425   int found = 0;
426   char buf[256];
427   FILE *f;
428
429   net_stats.down = net_stats.up =
430   net_stats.have_wlan = net_stats.link = net_stats.level = 0;
431
432   f = fopen("/proc/net/dev", "r");
433   if (f == NULL)
434     return;
435
436   while (!feof(f)) {
437     static const char fmt[] =
438       "%255s %lu %*s %*s %*s %*s %*s %*s %lu";
439     if (fscanf(f, fmt, buf, &rx, &tx) != 3)
440       continue;
441     if (IS_START(buf, "lo"))
442       continue;
443     st.rx += rx;
444     st.tx += tx;
445     found = 1;
446   }
447   fclose(f);
448
449   if (found) {
450     net_stats.down = st.rx - net_stats.last_st.rx;
451     net_stats.up = st.tx - net_stats.last_st.tx;
452   }
453   net_stats.last_st = st;
454
455   f = fopen("/proc/net/wireless", "r");
456   if (f == NULL)
457     return;
458
459   while (!feof(f)) {
460     int lnk, lvl;
461     if (fscanf(f, "%255s %*x %d. %d.", buf, &lnk, &lvl) != 3)
462       continue;
463     if (IS_START(buf, "lo"))
464       continue;
465     // first one should be enough
466     net_stats.have_wlan = 1;
467     net_stats.link = lnk;
468     net_stats.level = lvl;
469     break;
470   }
471   fclose(f);
472 }
473
474 static void collect_stats(void)
475 {
476   collect_proc_stat();
477   collect_mem_stat();
478   collect_io_stat();
479   collect_net_stat();
480 }
481
482 static char cpu_reg_path[64];
483
484 static void init_stats(void)
485 {
486   char buf[64];
487   int found;
488   FILE *f;
489   int i;
490
491   // find the CPU regulator
492   for (i = found = 0; ; i++) {
493     snprintf(buf, sizeof(buf),
494       "/sys/class/regulator/regulator.%d/name", i);
495     f = fopen(buf, "r");
496     if (f == NULL)
497       break;
498     fgets(buf, sizeof(buf), f);
499     fclose(f);
500     if (IS_START(buf, "vdd_mpu")) {
501       found = 1;
502       break;
503     }
504   }
505
506   if (found)
507     snprintf(cpu_reg_path, sizeof(cpu_reg_path),
508       "/sys/class/regulator/regulator.%d/microvolts", i);
509
510   // do collect so differential stats get sane values
511   collect_stats();
512 }
513
514 static void get_time(int x, int y)
515 {
516   struct timeval tv;
517   struct tm *t;
518   char buf[16];
519
520   gettimeofday(&tv, NULL);
521   t = localtime(&tv.tv_sec);
522   strftime(buf, sizeof(buf), "%T", t);
523   s_printf(x, y, " %s", buf);
524 }
525
526 static void get_cpu(int x, int y)
527 {
528   s_printf(x, y, "CPU:%5d%%", stats.cpu_usage);
529 }
530
531 static void get_sys(int x, int y)
532 {
533   s_printf(x, y, "SYS:%5d%%", stats.cpu_sys);
534 }
535
536 static void get_irq(int x, int y)
537 {
538   s_printf(x, y, "IRQ:%5u", stats.irqs);
539 }
540
541 static void get_cs(int x, int y)
542 {
543   s_printf(x, y, "CS:%6u", stats.ctxt);
544 }
545
546 static void get_cpuf(int x, int y)
547 {
548   static const char fn[] =
549     "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq";
550   int f;
551
552   if (read_int_file(fn, &f))
553     return;
554   s_printf(x, y, "f:%7dM", f / 1000);
555 }
556
557 static void get_cpuv(int x, int y)
558 {
559   int v = 0;
560
561   if (!cpu_reg_path[0] || read_int_file(cpu_reg_path, &v))
562     return;
563   s_printf(x, y, "V:%7.3fV", (float)v / 1000000.0f);
564 }
565
566 static void get_memfree(int x, int y)
567 {
568   s_printf(x, y, "M:%7dM", stats.mem_free);
569 }
570
571 static void get_memhuge(int x, int y)
572 {
573   s_printf(x, y, "H:%7dM", stats.mem_huge);
574 }
575
576 static void get_memswap(int x, int y)
577 {
578   s_printf(x, y, "S:%7dM", stats.mem_free_swap);
579 }
580
581 #define PS_BASE "/sys/class/power_supply/bq27500-0/"
582
583 static void get_bcap(int x, int y)
584 {
585   static const char fnc[] = PS_BASE "capacity";
586   int c;
587
588   if (read_int_file(fnc, &c))
589     return;
590   s_printf(x, y, "C:%7d%%", c);
591 }
592
593 static void get_btime(int x, int y)
594 {
595   static const char fne[] = PS_BASE "time_to_empty_now";
596   static const char fnf[] = PS_BASE "time_to_full_now";
597   int t = 0;
598
599   if (read_int_file(fne, &t) || t == 0) {
600     if (read_int_file(fnf, &t))
601       return;
602   }
603   s_printf(x, y, "t:%4d:%02d", t / 3600, t / 60 % 60);
604 }
605
606 static void get_bvolts(int x, int y)
607 {
608   static const char fnv[] = PS_BASE "voltage_now";
609   int volt = 0;
610
611   if (read_int_file(fnv, &volt))
612     return;
613
614   s_printf(x, y, "V:%7.3fV", (float)volt / 1000000.0f);
615 }
616
617 static void get_bwatts(int x, int y)
618 {
619   static const char fnc[] = PS_BASE "current_now";
620   static const char fnv[] = PS_BASE "voltage_now";
621   int w, cur = 0, volt = 0;
622
623   if (read_int_file(fnc, &cur))
624     return;
625   if (read_int_file(fnv, &volt))
626     return;
627
628   w = (cur / 1000) * (volt / 1000);
629   s_printf(x, y, "P:%7.3fW", (float)w / 1000000.0f);
630 }
631
632 static void get_btemp(int x, int y)
633 {
634   static const char fnt[] = PS_BASE "temp";
635   int t;
636
637   if (read_int_file(fnt, &t))
638     return;
639   s_printf(x, y, "T:%7.1fC", (float)t / 10.0f);
640 }
641
642 static void get_ioread(int x, int y)
643 {
644   s_printf(x, y, "R:%5.1fM/s", (float)io_stats.read / 1000.0f);
645 }
646
647 static void get_iowrite(int x, int y)
648 {
649   s_printf(x, y, "W:%5.1fM/s", (float)io_stats.write / 1000.0f);
650 }
651
652 static void get_netdown(int x, int y)
653 {
654   s_printf(x, y, "D:%5uK/s", net_stats.down / 1000);
655 }
656
657 static void get_netup(int x, int y)
658 {
659   s_printf(x, y, "U:%5uK/s", net_stats.up / 1000);
660 }
661
662 static void get_netsgnl(int x, int y)
663 {
664   s_printf(x, y, "S:%5ddBm", net_stats.level);
665 }
666
667 int main(int argc, char *argv[])
668 {
669   static const char socket_name[] = "\0liveinfo";
670   static const char fbname[] = "/dev/fb2";
671   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
672   pthread_condattr_t condattr;
673   struct sockaddr_un sun;
674   struct timespec ts;
675   struct pollfd pfd;
676   const char *p;
677   int is_hidden = 0;
678   int y = 0, y_max = 0;
679   int fd = -1;
680   int sock;
681   int ret;
682
683   // look for other instance
684   sock = socket(PF_UNIX, SOCK_STREAM, 0);
685   if (sock == -1) {
686     perror("socket PF_UNIX");
687     return 1;
688   }
689
690   memset(&sun, 0, sizeof(sun));
691   sun.sun_family = AF_UNIX;
692   memcpy(sun.sun_path, socket_name, sizeof(socket_name));
693
694   ret = connect(sock, (struct sockaddr *)&sun, sizeof(sun));
695   if (ret == 0) {
696     printf("other instance detected, sending poke command\n");
697     ret = send(sock, "poke", 5, 0);
698     if (ret != 5)
699       perror("send");
700     close(sock);
701     return ret == 5 ? 0 : 1;
702   }
703
704   fd = open(fbname, O_RDWR);
705   if (fd == -1) {
706     fprintf(stderr, "open %s: ", fbname);
707     perror(NULL);
708     return 1;
709   }
710
711   nice(-1);
712
713   // no need for privileges any more
714   p = getenv("SUDO_UID");
715   if (p != NULL) {
716     ret = atoi(p);
717     if (ret != 0) {
718       ret = setuid(ret);
719       if (ret)
720         perror("setuid");
721     }
722   }
723
724   // bind/listen for commands
725   ret = bind(sock, (struct sockaddr *)&sun, sizeof(sun));
726   if (ret != 0) {
727     perror("bind");
728     close(sock);
729     return 1;
730   }
731
732   ret = listen(sock, 1);
733   if (ret != 0) {
734     perror("listen");
735     close(sock);
736     return 1;
737   }
738
739   pfd.fd = sock;
740   pfd.events = POLLIN | POLLPRI;
741   pfd.revents = 0;
742
743   ret  = pthread_condattr_init(&condattr);
744   ret |= pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
745   ret |= pthread_cond_init(&g_cond, &condattr);
746   pthread_condattr_destroy(&condattr);
747   if (ret != 0) {
748     fprintf(stderr, "cond init failed\n");
749     return 1;
750   }
751
752   ret = setup_layer(fd);
753   if (ret)
754     return ret;
755
756   // clean up on kill too
757   signal(SIGTERM, handle_term);
758   signal(SIGUSR1, handle_usr1);
759
760   pthread_mutex_lock(&mutex);
761   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
762   if (ret != 0) {
763     perror("clock_gettime CLOCK_MONOTONIC");
764     return 1;
765   }
766
767   init_stats();
768
769   while (!g_exit)
770   {
771     // handle hiding
772     if (g_hide) {
773       if (!is_hidden) {
774         remove_layer(fd);
775         is_hidden = 1;
776       }
777       goto do_sleep;
778     }
779     else if (is_hidden) {
780       ret = setup_layer(fd);
781       if (ret)
782         break;
783       is_hidden = 0;
784     }
785
786     collect_stats();
787
788     y += Y_STEP;
789     if (y > y_max)
790       y_max = y;
791     clear(y_max);
792
793     y = 0;
794     get_time   (0, y += Y_STEP);
795     y += Y_STEP / 2;
796     get_cpu    (0, y += Y_STEP);
797     get_sys    (0, y += Y_STEP);
798     get_irq    (0, y += Y_STEP);
799     get_cs     (0, y += Y_STEP);
800     get_cpuf   (0, y += Y_STEP);
801     get_cpuv   (0, y += Y_STEP);
802     y += Y_STEP / 2;
803     s_printf(0, y += Y_STEP, "mem");
804     get_memfree(0, y += Y_STEP);
805     get_memswap(0, y += Y_STEP);
806     if (stats.mem_huge)
807       get_memhuge(0, y += Y_STEP);
808     y += Y_STEP / 2;
809     s_printf(0, y += Y_STEP, "i/o");
810     get_ioread (0, y += Y_STEP);
811     get_iowrite(0, y += Y_STEP);
812     y += Y_STEP / 2;
813     s_printf(0, y += Y_STEP, "net");
814     get_netdown(0, y += Y_STEP);
815     get_netup  (0, y += Y_STEP);
816     if (net_stats.have_wlan)
817       get_netsgnl(0, y += Y_STEP);
818     y += Y_STEP / 2;
819     s_printf(0, y += Y_STEP, "batt");
820     get_bcap   (0, y += Y_STEP);
821     get_btime  (0, y += Y_STEP);
822     get_bvolts (0, y += Y_STEP);
823     get_bwatts (0, y += Y_STEP);
824     get_btemp  (0, y += Y_STEP);
825
826     flip_fb(fd);
827
828     if (check_layer(fd) != 0)
829       break;
830
831     // anything on unix socket?
832     ret = poll(&pfd, 1, 0);
833     if (ret != 0) {
834       printf("poll returned %d\n", ret);
835       break;
836     }
837
838 do_sleep:
839     ts.tv_sec++;
840     pthread_cond_timedwait(&g_cond, &mutex, &ts);
841   }
842
843   remove_layer(fd);
844   close(fd);
845   close(sock);
846
847   return 0;
848 }
849
850 // vim:expandtab:sw=2:ts=2