wmsl

Block-based window manager status line
git clone https://git.sinitax.com/sinitax/wmsl
Log | Files | Refs | README | LICENSE | sfeed.txt

commit 6aa02a11915923e7fadde44164f299a21d462f93
parent fced537a700b48906b97c66828ef38822b7b9b4d
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon,  5 Feb 2024 23:59:38 +0100

Add config file support and refactor

Diffstat:
M.gitignore | 3---
MMakefile | 5+----
Dblocks.def.h | 12------------
Msigwmsl.c | 42+++++++++++-------------------------------
Mwmsl.c | 369++++++++++++++++++++++++++++++++++++++++---------------------------------------
5 files changed, 201 insertions(+), 230 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,5 +1,2 @@ sigwmsl wmsl -blocks.h -compile_commands.json -.clangd diff --git a/Makefile b/Makefile @@ -15,10 +15,7 @@ all: wmsl sigwmsl clean: rm -f wmsl sigwmsl -blocks.h: blocks.def.h - cp blocks.def.h blocks.h - -wmsl: wmsl.c blocks.h +wmsl: wmsl.c $(CC) -o $@ $< $(CFLAGS) $(LDLIBS) sigwmsl: sigwmsl.c diff --git a/blocks.def.h b/blocks.def.h @@ -1,12 +0,0 @@ -#define SCRIPT_DIR(x) "/opt/wmsl-scripts/" x - -/* use flags to set the scripts starting behavior */ - -struct block blocks[] = { - /* command block-id interval flags */ -// { SCRIPT_DIR("example.sh"), 1, 5, READY } -}; - -const char delim[] = " | "; -const char prefix[] = ""; -const char suffix[] = " "; diff --git a/sigwmsl.c b/sigwmsl.c @@ -1,4 +1,4 @@ -#include <asm-generic/errno-base.h> +#include <err.h> #include <errno.h> #include <signal.h> #include <stdarg.h> @@ -6,26 +6,7 @@ #include <string.h> #include <stdlib.h> -static const char* pid_file = "/tmp/.wmsl.pid"; - -static void -die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fputs("sigwmsl: ", stderr); - vprintf(fmt, ap); - if (*fmt && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - va_end(ap); - - exit(1); -} +static const char *pid_file = "/tmp/wmsl.pid"; int main(int argc, const char **argv) @@ -34,7 +15,7 @@ main(int argc, const char **argv) pid_t pid; sigval_t sigval; int block_id; - FILE *f; + FILE *file; if (argc != 2) { fprintf(stderr, "Usage: sigwmsl <block-id>\n"); @@ -42,25 +23,24 @@ main(int argc, const char **argv) } block_id = atoi(argv[1]); - if (!block_id) - die("sigwmsl: invalid block id\n"); + if (!block_id) errx(1, "invalid block id"); - f = fopen(pid_file, "r"); - if (!f) die("sigwmsl: Failed to open pid file %s\n", pid_file); + file = fopen(pid_file, "r"); + if (!file) err(1, "fopen '%s'", pid_file); - if (!fgets(buffer, 64, f)) - die("sigwmsl: PID file is empty\n"); + if (!fgets(buffer, 64, file)) + errx(1, "pid file empty"); pid = atoi(buffer); sigval.sival_int = block_id; if (sigqueue(pid, SIGALRM, sigval) == -1) { if (errno == EINVAL) { - die("sigwmsl: invalid signal value\n"); + errx(1, "invalid signal value"); } else if (errno == ESRCH) { - die("sigwmsl: wmsl is not running\n"); + errx(1, "wmsl not running"); } } - fclose(f); + fclose(file); } diff --git a/wmsl.c b/wmsl.c @@ -1,5 +1,6 @@ #include <X11/Xlib.h> +#include <sys/poll.h> #include <sys/wait.h> #include <unistd.h> #include <err.h> @@ -14,78 +15,38 @@ #include <stdio.h> #include <stdint.h> -#define ARRSIZE(x) (sizeof(x)/sizeof(x[0])) -#define OUTPUTMAX 256 -#define STATUSMAX 1024 - -enum block_state { - IDLE, - READY -}; +#define OUTPUTMAX 2048 struct block { + struct block *next; const char *command; - unsigned int id, sleep_max; - int flags; + bool ready; + int id, sleep_max; float sleep_left; char output[OUTPUTMAX]; }; static const char *usage = "Usage: wmsl [-h] [-v]\n"; -static const char *pid_file = "/tmp/.wmsl.pid"; +static const char *pid_file = "/tmp/wmsl.pid"; + +static char status_text[OUTPUTMAX]; +static size_t status_len; -static char status_text[STATUSMAX]; static float last_sleep; + static Display *dpy; static Window root; -static size_t status_len; + static int verbose; static pid_t pid; -#include "blocks.h" - -static void -__attribute__((format(printf,1,2))) -die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fputs("wmsl: ", stderr); - vfprintf(stderr, fmt, ap); - if (*fmt && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - va_end(ap); - - exit(EXIT_FAILURE); -} - -static void -__attribute__((format(printf,1,2))) -debug(const char *fmt, ...) -{ - char buf[12]; - struct tm *tm; - time_t sec; - va_list ap; - - if (!verbose) - return; +static char config[1024]; - time(&sec); - tm = localtime(&sec); +static struct block *blocks; - strftime(buf, sizeof(buf), "[%T] ", tm); - fprintf(stderr, "%s", buf); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} +static char *delim; +static char *prefix; +static char *suffix; static void update_status(const char *str) @@ -95,26 +56,24 @@ update_status(const char *str) } static void -clean_status(int sig) +sigexit(int sig) { update_status(""); - exit(0); + exit(128+sig); } static void signal_handler(int sig, siginfo_t *info, void *context) { + struct block *block; int block_id; - size_t i; block_id = info->si_value.sival_int; if (!block_id || info->si_pid == pid) return; - debug("received signal for block with id %i\n", block_id); - - for (i = 0; i < ARRSIZE(blocks); i++) { - if (blocks[i].id == block_id) { - blocks[i].flags |= READY; + for (block = blocks; block; block = block->next) { + if (block->id == block_id) { + block->ready = true; break; } } @@ -123,11 +82,11 @@ signal_handler(int sig, siginfo_t *info, void *context) static void concat_status(const char *text, size_t len) { - if (status_len + len + 1 > STATUSMAX) - len = STATUSMAX - status_len; + if (status_len + len + 1 > OUTPUTMAX) + len = OUTPUTMAX - status_len; memcpy(&status_text[status_len], text, len); status_len += len; - if (status_len == STATUSMAX) + if (status_len == OUTPUTMAX) strcpy(&status_text[status_len - 4], "..."); status_text[status_len] = '\0'; } @@ -135,139 +94,120 @@ concat_status(const char *text, size_t len) static int run_block(const char *cmd, pid_t *pid) { - int output[2]; + int out[2]; - if (pipe(output) == -1) - err(EXIT_FAILURE, "pipe"); + if (pipe(out) == -1) + err(1, "pipe"); *pid = fork(); - if (*pid < 0) err(EXIT_FAILURE, "fork"); + if (*pid < 0) + err(1, "fork"); if (*pid != 0) { - close(output[1]); + close(out[1]); } else { - signal(SIGINT, NULL); - signal(SIGTERM, NULL); - close(0); - close(1); - dup2(output[1], 1); - close(output[0]); - close(output[1]); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + dup2(out[1], 1); + close(out[0]); + close(out[1]); execl("/bin/sh", "sh", "-c", cmd, NULL); abort(); } - return output[0]; + return out[0]; } -static bool +static size_t read_block(int fd, char *buf, size_t size) { - const uint64_t timeout_ns = 1000000000; - struct timeval timeout; - struct timespec start, stop; - char tmp[256]; - fd_set fdset; + struct timespec now; + time_t now_ms, end_ms; + struct pollfd pollfd; ssize_t nread; - uint64_t ns; - char *tok; - int ret; + char *tok, *end; - clock_gettime(CLOCK_REALTIME, &start); + pollfd.fd = fd; + pollfd.events = POLLIN; - tok = NULL; - ns = 0; - do { - FD_ZERO(&fdset); - FD_SET(fd, &fdset); + clock_gettime(CLOCK_REALTIME, &now); + now_ms = now.tv_nsec / 1000000L + now.tv_sec * 1000L; - timeout.tv_sec = (time_t) (timeout_ns - ns) / 1000000000; - timeout.tv_usec = (time_t) (timeout_ns - ns) / 1000 % 1000000000; + end_ms = now_ms + 1000; /* 1 second timeout */ - ret = select(fd + 1, &fdset, NULL, NULL, &timeout); + end = buf; + while (end_ms > now_ms) { + if (poll(&pollfd, 1, (int) (end_ms - now_ms)) <= 0) + break; - clock_gettime(CLOCK_REALTIME, &stop); + clock_gettime(CLOCK_REALTIME, &now); + now_ms = now.tv_nsec / 1000000L + now.tv_sec * 1000L; - ns = (uint64_t) ((stop.tv_sec - start.tv_sec) * 1000000000 - + (stop.tv_nsec - start.tv_nsec)); + if (!(pollfd.revents & POLLIN)) + continue; - if (ret < 0 && errno == EINTR) continue; - if (ret <= 0) break; + nread = read(fd, end, size); + if (nread <= 0) break; - if (!tok) { - nread = read(fd, buf, size); - if (nread <= 0) break; + tok = memchr(end, '\n', (size_t) nread); + if (tok) *tok = '\0'; - tok = memchr(buf, '\n', (size_t) nread); - if (tok) *tok = '\0'; + end += nread; + size -= (size_t) nread; - buf += nread; - size -= (size_t) nread; - } else { - nread = read(fd, tmp, sizeof(tmp)); - if (nread <= 0) break; - } - } while (ns < timeout_ns); + if (tok) return strlen(buf); + } - return tok != NULL; + return 0; } static void update_blocks(void) { - size_t i, output_len; + size_t output_len; char tmp_output[OUTPUTMAX]; + struct block *block; pid_t child; bool update; int fd; - debug("checking block output..\n"); - - memcpy(status_text, prefix, ARRSIZE(prefix) - 1); + status_text[0] = '\0'; + status_len = 0; - concat_status(prefix, ARRSIZE(prefix) - 1); + concat_status(prefix, strlen(prefix)); update = false; - for (status_len = i = 0; i < ARRSIZE(blocks); i++) { - if ((blocks[i].flags & READY) || - blocks[i].sleep_max && blocks[i].sleep_left <= 0.2) { - blocks[i].sleep_left = (float) blocks[i].sleep_max; - blocks[i].flags = IDLE; + for (block = blocks; block; block = block->next) { + if (block->ready || block->sleep_max && block->sleep_left <= 0.2) { + block->sleep_left = (float) block->sleep_max; + block->ready = false; - /* get command output */ - fd = run_block(blocks[i].command, &child); + fd = run_block(block->command, &child); memset(tmp_output, 0, sizeof(tmp_output)); - if (read_block(fd, tmp_output, sizeof(tmp_output)-1)) { - output_len = strlen(tmp_output); - debug("%lu: %s\n", i, tmp_output); - } else { - output_len = 0; - debug("%lu: NO OUTPUT\n", i); - } + output_len = read_block(fd, tmp_output, sizeof(tmp_output)-1); - if (strcmp(tmp_output, blocks[i].output)) { - strncpy(blocks[i].output, tmp_output, OUTPUTMAX); - debug("new output from cmd: %s\n", blocks[i].command); + if (strcmp(tmp_output, block->output)) { + strncpy(block->output, tmp_output, OUTPUTMAX); update = true; } - kill(child, SIGKILL); close(fd); + kill(child, SIGKILL); waitpid(child, NULL, 0); } else { - output_len = strlen(blocks[i].output); + output_len = strlen(block->output); } if (output_len) { if (status_len > 0) - concat_status(delim, ARRSIZE(delim) - 1); - concat_status(blocks[i].output, output_len); + concat_status(delim, strlen(delim)); + concat_status(block->output, output_len); } } if (!update) return; - concat_status(suffix, ARRSIZE(suffix) - 1); + concat_status(suffix, strlen(suffix)); update_status(status_text); } @@ -280,7 +220,7 @@ get_elapsed(void) float diff; if (clock_gettime(CLOCK_MONOTONIC, &cur) == -1) - die("wmsl: failed to get time via clock()\n"); + err(1, "clock_gettime"); if (last.tv_nsec == -1) diff = 0; @@ -295,40 +235,104 @@ get_elapsed(void) static void sleep_till_next(void) { - unsigned int minsleep; - size_t i; + struct block *block; + unsigned int min_sleep; last_sleep = 0; /* reset last_sleep in case we return early */ - for (i = 0, minsleep = 0; i < ARRSIZE(blocks); i++) { - if ((blocks[i].flags & READY) || - blocks[i].sleep_max && blocks[i].sleep_left <= 0) + + min_sleep = 0; + for (block = blocks; block; block = block->next) { + if (block->ready || block->sleep_max && block->sleep_left <= 0) return; - if (!minsleep || blocks[i].sleep_max && blocks[i].sleep_left < minsleep) - minsleep = (unsigned int) ceil(blocks[i].sleep_left); + if (!min_sleep || block->sleep_max && block->sleep_left < min_sleep) + min_sleep = (unsigned int) ceil(block->sleep_left); } - if (minsleep < 1) - minsleep = 1; - - debug("sleeping for %i seconds\n", minsleep); + if (min_sleep < 1) + min_sleep = 1; - alarm(minsleep); + alarm(min_sleep); pause(); last_sleep = get_elapsed(); - debug("slept for %f seconds\n", last_sleep); +} + +static void +save_pid(void) +{ + FILE *file; + + pid = getpid(); + file = fopen(pid_file, "w+"); + if (!file) err(1, "fopen '%s'", pid_file); + fprintf(file, "%i", pid); + fclose(file); +} + +static void +read_config(void) +{ + struct block **block; + char line[256], *tok; + FILE *file; + + file = fopen(config, "r"); + if (!file) err(1, "fopen '%s'", config); + + block = &blocks; + while (fgets(line, 256, file)) { + if (strspn(line, " \n\v\t") == strlen(line)) + continue; + if ((tok = strchr(line, '\n'))) + *tok = '\0'; + if (!strncmp(line, "delim=", 6)) { + delim = strdup(line + 6); + } else if (!strncmp(line, "prefix=", 7)) { + prefix = strdup(line + 7); + } else if (!strncmp(line, "suffix=", 7)) { + suffix = strdup(line + 7); + } else if (!strncmp(line, "block=", 6)) { + if (block != &blocks || *block) + block = &(*block)->next; + *block = malloc(sizeof(struct block)); + (*block)->command = strdup(line + 6); + (*block)->id = 0; + (*block)->sleep_left = 0; + (*block)->sleep_max = 0; + (*block)->ready = true; + (*block)->next = NULL; + } else if (!strncmp(line, "block-id=", 9)) { + if (!*block) errx(1, "missing block="); + (*block)->id = atoi(line + 9); + } else if (!strncmp(line, "block-sleep=", 12)) { + if (!*block) errx(1, "missing block="); + (*block)->sleep_max = atoi(line + 12); + } else { + errx(1, "invalid config"); + } + } + + if (!suffix) suffix = " "; + if (!prefix) prefix = ""; + if (!delim) delim = " | "; + + fclose(file); } int main(int argc, const char **argv) { struct sigaction sa; + const char *home; + struct block *block; int i, screen; - FILE *f; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') switch (argv[i][1]) { + case 'C': + strncpy(config, argv[++i], sizeof(config)); + break; case 'v': verbose = 1; break; @@ -341,41 +345,47 @@ main(int argc, const char **argv) } } - /* get root window */ - dpy = XOpenDisplay(NULL); - if (!dpy) die("wmsl: Failed to open display\n"); - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); + /* get and parse config */ + if (!*config) { + home = getenv("HOME"); + if (!home) errx(1, "not config"); + snprintf(config, sizeof(config), + "%s/.config/wmsl/wmslrc", home); + } + read_config(); - /* setup signal handlers */ + /* setup signal handlers & cleanup */ sigemptyset(&sa.sa_mask); sa.sa_handler = NULL; sa.sa_sigaction = signal_handler; sa.sa_flags = SA_RESTART | SA_SIGINFO; - if (sigaction(SIGALRM, &sa, NULL) == -1) - die("wmsl: Failed to setup signal handler (SIGALRM)\n"); + if (sigaction(SIGALRM, &sa, NULL)) + err(1, "sigaction"); + signal(SIGINT, sigexit); + signal(SIGTERM, sigexit); - signal(SIGINT, clean_status); - signal(SIGTERM, clean_status); + /* get root window */ + dpy = XOpenDisplay(NULL); + if (!dpy) errx(1, "XOpenDisplay root window"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); - /* save pid */ - pid = getpid(); - f = fopen(pid_file, "w+"); - if (!f) die("wmsl: Failed to open pid file %s\n", pid_file); - fprintf(f, "%i", pid); - fclose(f); + save_pid(); /* check if any blocks require timers */ - for (i = 0; i < ARRSIZE(blocks); i++) - if (blocks[i].sleep_max != 0) break; + for (block = blocks; block; block = block->next) { + if (block->sleep_max != 0) break; + } - if (i != ARRSIZE(blocks)) { + if (block) { get_elapsed(); while (1) { sleep_till_next(); - for (i = 0; i < ARRSIZE(blocks); i++) - if (blocks[i].sleep_max) - blocks[i].sleep_left -= last_sleep; + for (block = blocks; block; block = block->next) { + if (block->sleep_max) { + block->sleep_left -= last_sleep; + } + } update_blocks(); } } else { @@ -385,4 +395,3 @@ main(int argc, const char **argv) } } } -