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