Commit: 14800b7b382676f2e8e8760ff99145124337f8b3
Parent: 7ea779050b0a9e71c8d95cf1c8e085c0c49c8d43
Author: Randy Palamar
Date: Mon, 23 Dec 2024 22:21:55 -0700
status rewrite
We no longer use signals to update blocks. They update on an
interval or based on a file watch via inotify. This may be
expanded to include arbitrary file descriptors as well. This has
already lead to lower runtime memory usage despite a big arena
alloc at startup that isn't being fully utilized yet. The binary
size has also been reduced.
There is a potential problem with the X macro producing long lines
but this will be addressed when necessary.
Otherwise this gives blocks much more control over their
individual updates while also producing better code.
Diffstat:
8 files changed, 420 insertions(+), 252 deletions(-)
diff --git a/blocks/date.c b/blocks/date.c
@@ -1,9 +1,25 @@
/* See LICENSE for license details. */
-static size_t
-date(struct Block *b)
+struct date_arg {
+ char *fmt; /* Time Format String (ex: %R) */
+ f32 interval; /* Update Interval [s] */
+};
+
+static BLOCK_UPDATE_FN(date_update)
{
- time_t t = time(NULL);
- strftime(buf, sizeof(buf), b->arg, localtime(&t));
+ struct date_arg *da = b->arg;
+
+ b32 result = timer_update(b->user_data, da->interval, dt);
+ if (result) {
+ time_t t = time(NULL);
+ strftime(buffer, sizeof(buffer), da->fmt, localtime(&t));
+ b->len = snprintf(b->data, sizeof(b->data), b->fmt, buffer);
+ }
- return snprintf(b->curstr, LEN(b->curstr), b->fmt, buf);
+ return result;
+}
+
+static BLOCK_INIT_FN(date_init)
+{
+ b->user_data = alloc(a, f32, 1);
+ date_update(b, 1);
}
diff --git a/blocks/linux/backlight.c b/blocks/linux/backlight.c
@@ -0,0 +1,39 @@
+/* See LICENSE for license details. */
+struct linux_backlight_data {i64 max_brightness; char *brightness_path;};
+
+static BLOCK_UPDATE_FN(backlight_update)
+{
+ if (dt > 0)
+ return 0;
+
+ struct linux_backlight_data *lbd = b->user_data;
+ i64 current;
+ if (pscanf(lbd->brightness_path, "%ld", ¤t) != 1)
+ current = 0;
+
+ f32 percent = 100 * current / (f32)lbd->max_brightness + 0.5;
+ i64 len = snprintf(buffer, sizeof(buffer), "%d%%", (i32)percent);
+ buffer[len] = 0;
+ b->len = snprintf(b->data, sizeof(b->data), b->fmt, buffer);
+
+ return 1;
+}
+
+static BLOCK_INIT_FN(backlight_init)
+{
+ struct linux_backlight_data *lbd;
+ b->user_data = lbd = push_struct(a, struct linux_backlight_data);
+
+ Arena tmp = *a;
+ char *path = alloc(&tmp, char, 4096);
+ snprintf(path, 4096, "/sys/class/backlight/%s/max_brightness", (char *)b->arg);
+ if (pscanf(path, "%ld", &lbd->max_brightness) != 1)
+ die("backlight_init: failed to read max brightness\n");
+
+ i64 len = snprintf(path, 4096, "/sys/class/backlight/%s/brightness", (char *)b->arg);
+ lbd->brightness_path = path;
+ a->beg += len;
+
+ backlight_update(b, 0);
+ add_file_watch(a, lbd->brightness_path, block_index, backlight_update);
+}
diff --git a/blocks/linux/battery.c b/blocks/linux/battery.c
@@ -1,56 +0,0 @@
-/* See LICENSE for license details. */
-#include <limits.h>
-
-struct bat_arg {
- char *bat; /* BAT name (ex. BAT0) */
- char *pre; /* prefix for percentages less than thres */
- char *suf; /* suffix for percentages less than thres */
- int thres; /* % threshold to consider low (-1 to disable) */
-};
-
-static size_t
-batinfo(struct Block *b)
-{
- struct bat_arg *ba = b->arg;
- char *pre = ba->pre ? ba->pre : "";
- char *suf = ba->suf ? ba->suf : "";
- char path[PATH_MAX], state[12];
- int s, perc, h, m;
- long power_now, energy_now, energy_full;
- double timeleft;
-
- snprintf(path, sizeof(path), "/sys/class/power_supply/%s/energy_full", ba->bat);
- if (pscanf(path, "%ld", &energy_full) != 1)
- energy_full = 1;
-
- snprintf(path, sizeof(path), "/sys/class/power_supply/%s/energy_now", ba->bat);
- if (pscanf(path, "%ld", &energy_now) != 1)
- energy_now = 0;
-
- perc = (100 * energy_now / (double)energy_full);
- s = perc < ba->thres;
-
- snprintf(path, sizeof(path), "/sys/class/power_supply/%s/status", ba->bat);
- if (pscanf(path, "%12s", &state) != 1)
- snprintf(state, sizeof(state), "Unknown");
-
- /* NOTE(rnp): proper devices use negative power to indicate discharging but that
- * is not always the case. The status string can mostly be trusted */
- if (!strcmp(state, "Discharging")) {
- snprintf(path, sizeof(path), "/sys/class/power_supply/%s/power_now", ba->bat);
- if (pscanf(path, "%ld", &power_now) != 1)
- power_now = 1;
-
- timeleft = (double)energy_now / (double)ABS(power_now);
- h = timeleft;
- m = (timeleft - (double)h) * 60;
-
- snprintf(buf, sizeof(buf), "%s%d%% (%d:%02d)%s", s ? pre : "",
- perc, h, m, s ? suf : "");
- } else {
- snprintf(buf, sizeof(buf), "%s%d%% (%s)%s", s ? pre : "", perc,
- state, s ? suf : "");
- }
-
- return snprintf(b->curstr, LEN(b->curstr), b->fmt, buf);
-}
diff --git a/blocks/linux/battery_info.c b/blocks/linux/battery_info.c
@@ -0,0 +1,75 @@
+/* See LICENSE for license details. */
+struct bat_arg {
+ char *bat; /* BAT name (ex. BAT0) */
+ char *pre; /* prefix for percentages less than thres */
+ char *suf; /* suffix for percentages less than thres */
+ i32 thres; /* % threshold to consider low (-1 to disable) */
+ f32 interval; /* [s] */
+};
+
+struct linux_battery_data { i64 energy_full; f32 timer; };
+
+static BLOCK_UPDATE_FN(battery_info_update)
+{
+ struct bat_arg *ba = b->arg;
+ struct linux_battery_data *lbd = b->user_data;
+
+ if (!timer_update(&lbd->timer, ba->interval, dt))
+ return 0;
+
+ char *pre = ba->pre ? ba->pre : "";
+ char *suf = ba->suf ? ba->suf : "";
+ char path[4096], state[12];
+
+ i32 h, m;
+ i64 power_now, energy_now;
+ f64 timeleft;
+
+ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/energy_now", ba->bat);
+ if (pscanf(path, "%ld", &energy_now) != 1)
+ energy_now = 0;
+
+ f32 percent = (100 * energy_now / (f64)lbd->energy_full) + 0.5;
+ b32 warn = percent < ba->thres;
+
+ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/status", ba->bat);
+ if (pscanf(path, "%12s", &state) != 1)
+ snprintf(state, sizeof(state), "Unknown");
+
+ /* NOTE(rnp): proper devices use negative power to indicate discharging but that
+ * is not always the case. The status string can mostly be trusted */
+ if (!strcmp(state, "Discharging")) {
+ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/power_now", ba->bat);
+ if (pscanf(path, "%ld", &power_now) != 1)
+ power_now = 1;
+
+ timeleft = energy_now / (f64)ABS(power_now);
+ h = timeleft;
+ m = (timeleft - (f64)h) * 60;
+
+ i64 len = snprintf(buffer, sizeof(buffer), "%s%d%% (%d:%02d)%s", warn? pre : "",
+ (i32)percent, h, m, warn? suf : "");
+ buffer[len] = 0;
+ } else {
+ i64 len = snprintf(buffer, sizeof(buffer), "%s%d%% (%s)%s", warn? pre : "",
+ (i32)percent, state, warn? suf : "");
+ buffer[len] = 0;
+ }
+ b->len = snprintf(b->data, sizeof(b->data), b->fmt, buffer);
+
+ return 1;
+}
+
+static BLOCK_INIT_FN(battery_info_init)
+{
+ struct bat_arg *ba = b->arg;
+ struct linux_battery_data *lbd;
+ b->user_data = lbd = push_struct(a, struct linux_battery_data);
+
+ char path[4096];
+ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/energy_full", ba->bat);
+ if (pscanf(path, "%ld", &lbd->energy_full) != 1)
+ die("battery_info_init: failed to read battery capacity\n");
+
+ battery_info_update(b, 1);
+}
diff --git a/blocks/linux/blight.c b/blocks/linux/blight.c
@@ -1,24 +0,0 @@
-/* See LICENSE for license details. */
-#include <limits.h>
-
-static size_t
-blight(struct Block *b)
-{
- char path[PATH_MAX];
- int perc;
- unsigned long max, now;
-
- snprintf(path, sizeof(path), "/sys/class/backlight/%s/brightness", (char *)b->arg);
- if (pscanf(path, "%lu", &now) != 1)
- now = 0;
-
- snprintf(path, sizeof(path), "/sys/class/backlight/%s/max_brightness", (char *)b->arg);
- if (pscanf(path, "%lu", &max) != 1)
- /* avoid divison by 0 */
- max = 1;
-
- perc = 100 * now / max;
- snprintf(buf, sizeof(buf), "%d%%", perc);
-
- return snprintf(b->curstr, LEN(b->curstr), b->fmt, buf);
-}
diff --git a/build.sh b/build.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-cflags="-O3 -std=c99 -Wall -pedantic"
+cflags="-march=native -O3 -std=c11 -Wall -pedantic"
cflags="$cflags -D_XOPEN_SOURCE=500"
cflags="$cflags -I /usr/X11R6/include"
diff --git a/config.def.h b/config.def.h
@@ -1,15 +1,10 @@
#include "blocks/date.c"
-#include "blocks/linux/battery.c"
-//#include "blocks/linux/blight.c"
+#include "blocks/linux/battery_info.c"
+//#include "blocks/linux/backlight.c"
//#include "blocks/linux/volume.c"
//#include "blocks/mpd.c"
//#include "blocks/script.c"
-/* update intervals: SEC+NANO gives sleep interval */
-/* SEC must be >= 0 and 0 <= NANO <= 999999999 */
-#define INTERVAL_SEC 1
-#define INTERVAL_NANO 0
-
/* mpd_arg.host can be NULL to use the MPD_HOST env variable */
// static enum mpd_tag_type tags[] = { MPD_TAG_TITLE, MPD_TAG_ARTIST };
// static struct mpd_arg ma = { "localhost", "|", tags, 2 };
@@ -18,27 +13,22 @@
/* card is found with 'aplay -L', default is probably correct */
// static struct vol_arg va = { "default", "Speaker" };
-/* check battery.h for info */
-static struct bat_arg ba = { "BAT0", NULL, NULL, -1 };
+/* check blocks/xxx/battery_info.c for info */
+static struct bat_arg ba = {.bat = "BAT0", .interval = 30};
+
+/* check blocks/date.c for info */
+static struct date_arg da = {.fmt = "%R", .interval = 30};
/* status block definitions
*
- * function description arg (ex)
- *
- * batinfo battery percentage and status (struct bat_arg *)
- * blight backlight percentage (char *) backlight name (intel_backlight)
- * date date and time (char *) time fmt string (%R)
- * volume ALSA volume percentage (struct vol_arg *)
- * mpd_tag reads tag from current song (struct mpd_arg *)
- * script run specified script (char *) full script (echo foo | bar)
+ * block description arg (ex)
*
- *
- * interval * INTERVAL above gives actual update interval, 0 only updates
- * at the start and when signaled, -1 only updates when signaled
+ * battery_info battery percentage and status (struct bat_arg *)
+ * backlight percentage (char *) backlight name (intel_backlight)
+ * date date and time (struct date_arg *)
*/
-struct Block blks[] = {
-/* fn fmt interval signal arg */
- { batinfo, "[ %s ]", 30, 0, &ba },
- { date, "[ %s ]", 20, 0, "%R" },
- { NULL },
-};
+
+/* NOTE: X(name, statusline_format, argument) */
+#define BLOCKS \
+ X(battery_info, "[ %s ]", &ba) \
+ X(date, "[ %s ]", &da)
diff --git a/status.c b/status.c
@@ -1,35 +1,102 @@
/* See LICENSE for license details. */
#include <signal.h>
#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+#include <sys/inotify.h>
+#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xlib.h>
-#define ABS(a) ((a) < 0 ? -(a) : (a))
-#define LEN(a) (sizeof(a) / sizeof(*a))
+#define ABS(a) ((a) < 0 ? -(a) : (a))
+#define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a))
#define BLOCKLEN 128
-#define BLOCKPAD 4
-#define STATUSLEN ((LEN(blks) - 1) * BLOCKLEN + 1)
-
-struct Block {
- size_t (*fn)(struct Block *b);
- const char *fmt;
- int interval;
- int signal;
- void *arg;
- char curstr[BLOCKLEN];
- char prevstr[BLOCKLEN];
- size_t len;
-};
-static char buf[BLOCKLEN - BLOCKPAD];
-static Display *dpy;
-static int dflag = 0;
-static sigset_t blocksigmask;
-static struct Block *dirty;
+#define TICK_RATE_SECONDS 1
+#define TICK_RATE_NANOSECONDS 0
+
+#define SLLPush(sll, new) do { \
+ (new)->next = (sll)->next; \
+ (sll)->next = (new); \
+} while(0)
+
+#define KB(n) ((n) << 10ULL)
+
+#ifndef asm
+#define asm __asm__
+#endif
+
+#ifndef typeof
+#define typeof __typeof__
+#endif
+
+#ifdef __ARM_ARCH_ISA_A64
+#define debugbreak() asm volatile ("brk 0xf000")
+#elif __x86_64__
+#define debugbreak() asm volatile ("int3; nop")
+#else
+#error Unsupported Platform!
+#endif
+
+#ifdef _DEBUG
+#define ASSERT(c) do { if (!(c)) debugbreak(); } while (0)
+#define DEBUG_EXPORT
+#else
+#define ASSERT(c) do { (void)(c); } while (0)
+#define DEBUG_EXPORT static
+#endif
+
+typedef float f32;
+typedef double f64;
+typedef uint8_t u8;
+typedef uint32_t b32;
+typedef uint32_t u32;
+typedef int32_t i32;
+typedef uint64_t u64;
+typedef int64_t i64;
+typedef ptrdiff_t size;
+typedef size_t usize;
+
+typedef struct { u8 *beg, *end; } Arena;
+
+typedef struct {
+ u8 *buffer;
+ i32 capacity;
+ i32 write_index;
+ b32 errors;
+} Stream;
+
+typedef struct {
+ void *user_data;
+ void *arg;
+ char *fmt;
+ char data[BLOCKLEN];
+ size len;
+} Block;
+
+#define BLOCK_INIT_FN(name) void name(Block *b, i32 block_index, Arena *a)
+#define BLOCK_UPDATE_FN(name) b32 name(Block *b, f32 dt)
+typedef BLOCK_UPDATE_FN(block_update_fn);
+
+typedef struct FileWatch {
+ struct FileWatch *next;
+ block_update_fn *update_fn;
+ char *path;
+ i32 block_index;
+ i32 wd;
+} FileWatch;
+
+/* TODO(rnp): replace this with arena usage */
+static char buffer[KB(1)];
+static Stream statusline;
+static void *display;
+static i32 dirty_block_index = -1;
+static FileWatch file_watches;
+
+static i32 dflag;
static void
die(const char *errstr, ...)
@@ -39,7 +106,26 @@ die(const char *errstr, ...)
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
- exit(1);
+
+ _exit(1);
+}
+
+static f64
+get_time(void)
+{
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ f64 result = t.tv_sec + ((f64)t.tv_nsec) * 1e-9;
+ return result;
+}
+
+static b32
+timer_update(f32 *timer, f32 interval, f32 dt)
+{
+ b32 result = 0;
+ *timer -= dt;
+ while (*timer < 0) { *timer += interval; result = 1; }
+ return result;
}
static int
@@ -60,163 +146,206 @@ pscanf(const char *path, const char *fmt, ...)
return (ret == EOF) ? -1 : ret;
}
+static void *
+mem_clear(void *_p, u8 c, usize size)
+{
+ u8 *p = _p;
+ for (usize i = 0; i < size; i++)
+ p[i] = c;
+ return p;
+}
+
+#define push_struct(a, t) alloc(a, t, 1)
+#define alloc(a, t, n) (t *)alloc_(a, sizeof(t), _Alignof(t), n)
+static void *
+alloc_(Arena *a, size len, size align, size count)
+{
+ size padding = -(uintptr_t)a->beg & (align - 1);
+ size available = a->end - a->beg - padding;
+ if (available <= 0 || available / len < count) {
+ ASSERT(0);
+ }
+
+ void *p = a->beg + padding;
+ a->beg += padding + count * len;
+ return mem_clear(p, 0, count * len);
+}
+
+static Stream
+stream_alloc(Arena *a, size capacity)
+{
+ Stream result = {0};
+ result.buffer = alloc(a, u8, capacity);
+ result.capacity = capacity;
+ return result;
+}
+
+static void
+add_file_watch(Arena *a, char *path, i32 block_index, block_update_fn *update_fn)
+{
+ i32 wd = inotify_add_watch(file_watches.wd, path, IN_CLOSE_WRITE|IN_MODIFY);
+ if (wd != -1) {
+ /* TODO(rnp): we will need to include inodes if we are expecting to watch
+ * files that could be removed */
+ FileWatch *fw = push_struct(a, FileWatch);
+ fw->wd = wd;
+ fw->path = path;
+ fw->block_index = block_index;
+ fw->update_fn = update_fn;
+ SLLPush(&file_watches, fw);
+ }
+}
+
#include "config.h"
+#define X(name, format, argument) {.fmt = format, .arg = argument},
+static Block blocks[] = {
+ BLOCKS
+};
+#undef X
+
+#if TICK_RATE_NANOSECONDS > 999999999
+#error TICK_RATE_NANOSECONDS must not exceed 999 999 999
+#endif
+
static void
terminate(int signo)
{
if (!dflag) {
- XStoreName(dpy, DefaultRootWindow(dpy), NULL);
- XCloseDisplay(dpy);
+ XStoreName(display, DefaultRootWindow(display), 0);
+ XCloseDisplay(display);
}
-
- exit(0);
+ _exit(0);
}
static void
-updateblock(struct Block *b)
-{
- b->len = b->fn(b);
- if ((b->len == 0 && b->prevstr[0] != 0) ||
- memcmp(b->curstr, b->prevstr, b->len)) {
- if (b->len == 0)
- b->prevstr[0] = b->curstr[0] = 0;
- else
- memcpy(b->prevstr, b->curstr, b->len);
- if (!dirty || b < dirty)
- dirty = b;
+dispatch_file_watch_events(Arena a)
+{
+ u8 *mem = alloc_(&a, 4096, 64, 1);
+ for (;;) {
+ size rlen = read(file_watches.wd, mem, 4096);
+ if (rlen <= 0)
+ break;
+
+ struct inotify_event *ie;
+ for (u8 *data = mem; data < mem + rlen; data += sizeof(*ie) + ie->len) {
+ ie = (void *)data;
+ for (FileWatch *fw = file_watches.next; fw; fw = fw->next) {
+ if (fw->wd != ie->wd)
+ continue;
+
+ b32 file_changed = (ie->mask & IN_CLOSE_WRITE) != 0;
+ file_changed |= (ie->mask & IN_MODIFY) != 0;
+ /* TODO(rnp): it seems like this hits multiple times per update */
+ if (file_changed && fw->update_fn(blocks + fw->block_index, 0)) {
+ /* TODO(rnp): there might be an ordering issue here */
+ if (dirty_block_index == -1 || fw->block_index < dirty_block_index)
+ dirty_block_index = fw->block_index;
+ }
+ }
+ }
}
}
static void
-updatestatus(void)
+update_status(void)
{
- static char status[STATUSLEN];
- struct Block *b;
- char *s = status;
-
- for (b = blks; b < dirty; b++)
- s += b->len;
-
- for (; b->fn; b++) {
- memcpy(s, b->curstr, b->len);
- s += b->len;
+ statusline.write_index = 0;
+ i32 block_index;
+ for (block_index = 0; block_index < dirty_block_index; block_index++)
+ statusline.write_index += blocks[block_index].len;
+
+ for (; block_index < ARRAY_COUNT(blocks); block_index++) {
+ Block *b = blocks + block_index;
+ memcpy(statusline.buffer + statusline.write_index, b->data, b->len);
+ statusline.write_index += b->len;
}
- s[0] = '\0';
- dirty = NULL;
+ statusline.buffer[statusline.write_index] = 0;
if (dflag) {
- puts(status);
- return;
+ puts((char *)statusline.buffer);
+ } else {
+ XStoreName(display, DefaultRootWindow(display), (char *)statusline.buffer);
+ XSync(display, 0);
}
-
- XStoreName(dpy, DefaultRootWindow(dpy), status);
- XSync(dpy, False);
}
static void
-sighandler(int signo, siginfo_t *info, void *context)
+update_blocks(f32 dt)
{
- struct Block *b;
-
- signo -= SIGRTMIN;
- for (b = blks; b->fn; b++)
- if (b->signal == signo)
- updateblock(b);
- if (dirty)
- updatestatus();
+ i32 count = 0;
+ #define X(name, fmt, args) if (name ##_update(blocks + count++, dt)) dirty_block_index = count - 1;
+ BLOCKS
+ #undef X
}
static void
-setupsigs(void)
+status_init(Arena *a)
{
- int i;
- struct Block *b;
- struct sigaction sa;
+ if (!dflag && !(display = XOpenDisplay(0)))
+ die("XOpenDisplay: can't open display\n");
- /* add signals to blocksigmask */
- sigemptyset(&blocksigmask);
- for (b = blks; b->fn; b++) {
- if (b->signal <= 0)
- continue;
+ statusline = stream_alloc(a, 4096);
- if (b->signal > SIGRTMAX - SIGRTMIN)
- die("SIGRTMIN + %d exceeds SIGRTMAX\n", b->signal);
+ file_watches.wd = inotify_init1(O_NONBLOCK|O_CLOEXEC);
- sigaddset(&blocksigmask, SIGRTMIN + b->signal);
- }
+ i32 count = 0;
+ #define X(name, fmt, arg) name ##_init(blocks + count, count, a); count++;
+ BLOCKS
+ #undef X
- /* handle terminating signals */
+ struct sigaction sa;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sa.sa_handler = terminate;
- sigaction(SIGHUP, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, 0);
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
- /* ignore unused realtime signals */
- sa.sa_handler = SIG_IGN;
- for (i = SIGRTMIN + 1; i <= SIGRTMAX; i++)
- sigaction(i, &sa, NULL);
-
- /* handle update signals for blocks */
- sa.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO;
- sa.sa_mask = blocksigmask;
- sa.sa_sigaction = sighandler;
- for (b = blks; b->fn; b++)
- if (b->signal > 0)
- sigaction(SIGRTMIN + b->signal, &sa, NULL);
-
- /* start with signals blocked */
- sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
+ update_status();
}
static void
-statusinit(void)
+status_loop(Arena a)
{
- struct Block *b;
+ fd_set rfd;
+ f64 last_time = get_time();
- setupsigs();
-
- if (!dflag && !(dpy = XOpenDisplay(NULL)))
- die("XOpenDisplay: can't open display\n");
-
- /* initialize blocks before first print */
- for (b = blks; b->fn; b++)
- if (b->interval != -1)
- updateblock(b);
- updatestatus();
+ for (;;) {
+ dirty_block_index = -1;
+ struct timespec t = {.tv_sec = TICK_RATE_SECONDS, .tv_nsec = TICK_RATE_NANOSECONDS};
+ FD_ZERO(&rfd);
+ FD_SET(file_watches.wd, &rfd);
+
+ pselect(file_watches.wd + 1, &rfd, 0, 0, &t, 0);
+ if (FD_ISSET(file_watches.wd, &rfd))
+ dispatch_file_watch_events(a);
+
+ f64 current_time = get_time();
+ f32 dt = current_time - last_time;
+ last_time = current_time;
+
+ update_blocks(dt);
+ if (dirty_block_index >= 0)
+ update_status();
+ }
}
-static void
-statusloop(void)
+static Arena
+get_arena(void)
{
- unsigned int i = 0;
- struct Block *b;
- struct timespec t;
-
- for (;;) {
- sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL);
- t.tv_sec = INTERVAL_SEC;
- t.tv_nsec = INTERVAL_NANO;
- while (nanosleep(&t, &t) == -1);
- sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
-
- for (b = blks; b->fn; b++)
- if (b->interval > 0 && i % b->interval == 0)
- updateblock(b);
- if (dirty)
- updatestatus();
- i++;
- }
+ static u8 memory[KB(64)];
+ Arena a = {0};
+ a.beg = memory;
+ asm("" : "+r"(a.beg));
+ a.end = a.beg + sizeof(memory);
+ return a;
}
-int
-main(int argc, char *argv[])
+i32
+main(i32 argc, char *argv[])
{
- int i;
char *argv0 = *argv;
-
for (argv++; --argc && *argv && argv[0][0] == '-' && argv[0][1]; argv++) {
if (argv[0][1] == '-' && argv[0][2] == '\0') {
argv++;
@@ -224,18 +353,17 @@ main(int argc, char *argv[])
break;
}
- for (i = 1; argv[0][i]; i++)
+ for (i32 i = 1; argv[0][i]; i++) {
switch (argv[0][i]) {
- case 'd':
- dflag = 1;
- break;
- default:
- die("usage: %s [-d]\n", argv0);
+ case 'd': dflag = 1; break;
+ default: die("usage: %s [-d]\n", argv0);
}
+ }
}
- statusinit();
- statusloop();
+ Arena memory = get_arena();
+ status_init(&memory);
+ status_loop(memory);
return 0;
}