statbar

statbar.git
git clone git://git.lenczewski.org/statbar.git
Log | Files | Refs

statbar-wl.c (5242B)


      1 #include <errno.h>
      2 #include <fcntl.h>
      3 #include <inttypes.h>
      4 #include <stdarg.h>
      5 #include <stdbool.h>
      6 #include <stdio.h>
      7 #include <stdint.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <time.h>
     11 #include <unistd.h>
     12 
     13 typedef uint8_t u8;
     14 typedef uint16_t u16;
     15 typedef uint32_t u32;
     16 typedef uint64_t u64;
     17 
     18 typedef int8_t s8;
     19 typedef int16_t s16;
     20 typedef int32_t s32;
     21 typedef int64_t s64;
     22 
     23 #define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0])
     24 
     25 #define NSECS 1000000000UL
     26 
     27 #define BUFSZ 8192
     28 static char statbuf[BUFSZ];
     29 
     30 #define BLOCKSEP " | "
     31 
     32 struct block { u64 (*fn)(char *buf, u64 len); };
     33 
     34 u64 cpuinfo(char *buf, u64 len);
     35 u64 meminfo(char *buf, u64 len);
     36 u64 batinfo(char *buf, u64 len);
     37 u64 datetime(char *buf, u64 len);
     38 
     39 static struct block blocks[] = {
     40 	{ .fn = cpuinfo, },
     41 	{ .fn = meminfo, },
     42 //	{ .fn = batinfo, },
     43 	{ .fn = datetime, },
     44 };
     45 
     46 static inline void
     47 difftimespec(struct timespec const *restrict lhs, struct timespec const *restrict rhs,
     48 	     struct timespec *restrict out) {
     49 	out->tv_sec = lhs->tv_sec - rhs->tv_sec - (lhs->tv_nsec < rhs->tv_nsec);
     50 	out->tv_nsec = lhs->tv_nsec - rhs->tv_nsec + (lhs->tv_nsec < rhs->tv_nsec) * NSECS;
     51 }
     52 
     53 static char *somebar_path = NULL;
     54 
     55 void
     56 read_env_vars(void) {
     57 	somebar_path = strcat(getenv("XDG_RUNTIME_DIR"), "/somebar-0");
     58 }
     59 
     60 void
     61 exec_blocks(void) {
     62 	u64 written = 0, limit = BUFSZ - 1;
     63 	for (u64 i = 0; i < ARRLEN(blocks) && written < limit; i++) {
     64 		written += blocks[i].fn(statbuf + written, limit - written);
     65 	}
     66 
     67 	statbuf[written] = '\0';
     68 }
     69 
     70 void
     71 output_status(void) {
     72 	static int somebarfd = -1;
     73 
     74 	if (somebarfd < 0) {
     75 		if ((somebarfd = open(somebar_path, O_WRONLY|O_CLOEXEC)) < 0 && errno == ENOENT) {
     76 			sleep(1); // TODO: wait for somebar path to appear
     77 			somebarfd = open(somebar_path, O_WRONLY|O_CLOEXEC);
     78 		}
     79 
     80 		if (somebarfd < 0) {
     81 			perror("open");
     82 			exit(EXIT_FAILURE);
     83 		}
     84 	}
     85 
     86 	dprintf(somebarfd, "status %s\n", statbuf);
     87 }
     88 
     89 int
     90 main(void) {
     91 	read_env_vars();
     92 
     93 	struct timespec start, end, diff, wait, interval = { .tv_sec = 1, };
     94 	while (true) {
     95 		if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
     96 			perror("clock_gettime");
     97 			exit(EXIT_FAILURE);
     98 		}
     99 
    100 		exec_blocks();
    101 		output_status();
    102 
    103 		if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
    104 			perror("clock_gettime");
    105 			exit(EXIT_FAILURE);
    106 		}
    107 
    108 		difftimespec(&end, &start, &diff);
    109 		difftimespec(&interval, &diff, &wait);
    110 
    111 		if (wait.tv_sec >= 0 && nanosleep(&wait, NULL) < 0 && errno != EINTR) {
    112 			perror("nanosleep");
    113 			exit(EXIT_FAILURE);
    114 		}
    115 	}
    116 
    117 	return 0;
    118 }
    119 
    120 /* ===========================================================================
    121  * status bar blocks
    122  * ===========================================================================
    123  */
    124 
    125 static int
    126 pscanf(const char *path, const char *fmt, ...) {
    127 	FILE *fp;
    128 	va_list ap;
    129 	int n;
    130 
    131 	if (!(fp = fopen(path, "r"))) {
    132 		fprintf(stderr, "fopen '%s':", path);
    133 		return -1;
    134 	}
    135 	va_start(ap, fmt);
    136 	n = vfscanf(fp, fmt, ap);
    137 	va_end(ap);
    138 	fclose(fp);
    139 
    140 	return (n == EOF) ? -1 : n;
    141 }
    142 
    143 u64
    144 cpuinfo(char *buf, u64 len) {
    145 	static long double a[7];
    146 	long double b[7], sum;
    147 	s32 usage;
    148 
    149 	memcpy(b, a, sizeof(b));
    150 
    151 	/* cpu user nice system idle iowait irq softirq */
    152 	if (pscanf("/proc/stat", "%*s %LF %LF %LF %LF %LF %LF %LF",
    153 	           &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6]) != 7) {
    154 		perror("vfscanf");
    155 		exit(EXIT_FAILURE);
    156 	}
    157 
    158 	sum = (b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6]) -
    159 	      (a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6]);
    160 
    161 	if (sum == 0) {
    162 		usage = 0;
    163 	} else {
    164 		usage = (s32)(100 * 
    165 			     ((b[0] + b[1] + b[2] + b[5] + b[6]) -
    166 			      (a[0] + a[1] + a[2] + a[5] + a[6])) / sum);
    167 	}
    168 
    169 	u64 written;
    170 	if (!(written = snprintf(buf, len, "CPU: %" PRIi32 "%%" BLOCKSEP, usage))) {
    171 		perror("snprintf");
    172 		exit(EXIT_FAILURE);
    173 	}
    174 
    175 	return written;
    176 }
    177 
    178 u64
    179 meminfo(char *buf, u64 len) {
    180 	uintmax_t total, free, buffers, cached;
    181 	int percent;
    182 
    183 	if (pscanf("/proc/meminfo",
    184 	           "MemTotal: %ju kB\n"
    185 	           "MemFree: %ju kB\n"
    186 	           "MemAvailable: %ju kB\n"
    187 	           "Buffers: %ju kB\n"
    188 	           "Cached: %ju kB\n",
    189 	           &total, &free, &buffers, &buffers, &cached) != 5) {
    190 		perror("vfscanf");
    191 		exit(EXIT_FAILURE);
    192 	}
    193 
    194 	if (total == 0) {
    195 		percent = 0;
    196 	} else {
    197 		percent = 100 * ((total - free) - (buffers + cached)) / total;
    198 	}
    199 
    200 	u64 written;
    201 	if (!(written = snprintf(buf, len, "MEM: %" PRIi32 "%%" BLOCKSEP, percent))) {
    202 		perror("snprintf");
    203 		exit(EXIT_FAILURE);
    204 	}
    205 
    206 	return written;
    207 }
    208 
    209 u64
    210 batinfo(char *buf, u64 len) {
    211 	s32 capacity;
    212 	char status[13]; // "Charging" | "Discharging" | "Full" | "Not Charging"
    213 
    214 	if (pscanf("/sys/class/power_supply/BAT0/capacity", "%" SCNi32 "", &capacity) != 1) {
    215 		perror("vfscanf");
    216 		exit(EXIT_FAILURE);
    217 	}
    218 
    219 	if (pscanf("/sys/class/power_supply/BAT0/status", "%12[a-zA-Z ]", &status) != 1) {
    220 		perror("vfscanf");
    221 		exit(EXIT_FAILURE);
    222 	}
    223 
    224 	u64 written;
    225 	if (!(written = snprintf(buf, len, "BAT: %" PRIi32 "%% %12s" BLOCKSEP, capacity, status))) {
    226 		perror("snprintf");
    227 		exit(EXIT_FAILURE);
    228 	}
    229 
    230 	return written;
    231 }
    232 
    233 u64
    234 datetime(char *buf, u64 len) {
    235 	char const *timefmt = "%F %T";
    236 	time_t t = time(NULL);
    237 
    238 	u64 written;
    239 	if (!(written = strftime(buf, len, timefmt, localtime(&t)))) {
    240 		perror("strftime");
    241 		exit(EXIT_FAILURE);
    242 	}
    243 
    244 	return written;
    245 }