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 }