wmsl

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

commit a67f1c4669f1f015b2b1bf0a69453474f51020c7
parent 6aa02a11915923e7fadde44164f299a21d462f93
Author: Louis Burda <quent.burda@gmail.com>
Date:   Tue,  6 Feb 2024 01:26:46 +0100

Improve readme and remove some more bloat

Diffstat:
DREADME | 37-------------------------------------
AREADME.rst | 13+++++++++++++
Msigwmsl.c | 2+-
Mwmsl.c | 255++++++++++++++++++++++++++++++++-----------------------------------------------
Awmslrc | 14++++++++++++++
5 files changed, 132 insertions(+), 189 deletions(-)

diff --git a/README b/README @@ -1,37 +0,0 @@ -wmsl - window manager status line -================================= - -wmsl is a simple and extensible status line for dwm - - -Features --------- -- status 'blocks' are updated by timer or unique signal -- scriptable, blocks can be added dynamically without recompilation -- simple and extensible <200 lines of C - - -Methodology ------------ -To update blocks independent of the timer, a signal handler is bound to SIGALRM, -the block to update is specified via id in the SIGVAL. This way, both update -signals and timers (through alarm()) use the same handler. This results in a -very simple and neat implementation, where the signals specified for each block -are also independent of the range of realtime signal for the system. The -downside is, that the signal value cannot be used to pass information to a -script, however, this can easily be done via a tmp file with more flexibility. - -Instead of waiting the GCD of all timer values (or worse, plainly 1 sec), wmsl -waits as long as possible, until the next block must be run. This way, given two -scripts with update intervals 3 and 10, which are coprime, instead of waiting 10 -times in 10 seconds, we wait 4 times e.g. 3s, 3s, 3s, 1s. - -The status bar is only updated, if the status has changed. However, a new status -is compared with the previously set status BY WMSL, not the current, potentially -changed status. - - -Requirements ------------- -Xlib header files for setting root window property WM_NAME - diff --git a/README.rst b/README.rst @@ -0,0 +1,13 @@ +wmsl - Window Manager Status Line +================================= + +wmsl is a simple status line program for window managers like dwm. + + +Features +-------- + +- status *blocks* are updated by timer or unique signal +- simple runtime config format (see `wmslrc`) +- extensible source in 350 LOC + diff --git a/sigwmsl.c b/sigwmsl.c @@ -34,7 +34,7 @@ main(int argc, const char **argv) pid = atoi(buffer); sigval.sival_int = block_id; - if (sigqueue(pid, SIGALRM, sigval) == -1) { + if (sigqueue(pid, SIGUSR1, sigval) == -1) { if (errno == EINVAL) { errx(1, "invalid signal value"); } else if (errno == ESRCH) { diff --git a/wmsl.c b/wmsl.c @@ -1,5 +1,6 @@ #include <X11/Xlib.h> +#include <bits/time.h> #include <sys/poll.h> #include <sys/wait.h> #include <unistd.h> @@ -21,25 +22,21 @@ struct block { struct block *next; const char *command; bool ready; - int id, sleep_max; - float sleep_left; + int id, sleep; + time_t sleep_left; char output[OUTPUTMAX]; }; -static const char *usage = "Usage: wmsl [-h] [-v]\n"; +static const char *usage = "Usage: wmsl [-C FILE] [-h] [-v]\n"; static const char *pid_file = "/tmp/wmsl.pid"; +static pid_t pid; static char status_text[OUTPUTMAX]; static size_t status_len; -static float last_sleep; - static Display *dpy; static Window root; -static int verbose; -static pid_t pid; - static char config[1024]; static struct block *blocks; @@ -59,11 +56,22 @@ static void sigexit(int sig) { update_status(""); - exit(128+sig); + exit(128 + sig); +} + +static time_t +now_ms(void) +{ + struct timespec now; + + if (clock_gettime(CLOCK_REALTIME, &now)) + err(1, "clock_gettime REALTIME"); + + return now.tv_sec * 1000L + now.tv_nsec / 1000000L; } static void -signal_handler(int sig, siginfo_t *info, void *context) +sighandler(int sig, siginfo_t *info, void *context) { struct block *block; int block_id; @@ -80,10 +88,11 @@ signal_handler(int sig, siginfo_t *info, void *context) } static void -concat_status(const char *text, size_t len) +concat_status(const char *text) { + size_t len = strlen(text); if (status_len + len + 1 > OUTPUTMAX) - len = OUTPUTMAX - status_len; + len = OUTPUTMAX - status_len - 1; memcpy(&status_text[status_len], text, len); status_len += len; if (status_len == OUTPUTMAX) @@ -100,14 +109,12 @@ run_block(const char *cmd, pid_t *pid) err(1, "pipe"); *pid = fork(); - if (*pid < 0) - err(1, "fork"); + if (*pid < 0) err(1, "fork"); - if (*pid != 0) { + if (*pid) { close(out[1]); } else { - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); + close(0); dup2(out[1], 1); close(out[0]); close(out[1]); @@ -118,55 +125,47 @@ run_block(const char *cmd, pid_t *pid) return out[0]; } -static size_t +static void read_block(int fd, char *buf, size_t size) { - struct timespec now; - time_t now_ms, end_ms; + time_t start_ms, end_ms; struct pollfd pollfd; ssize_t nread; - char *tok, *end; + char *tok; pollfd.fd = fd; pollfd.events = POLLIN; - clock_gettime(CLOCK_REALTIME, &now); - now_ms = now.tv_nsec / 1000000L + now.tv_sec * 1000L; + start_ms = now_ms(); + end_ms = start_ms + 1000; /* 1 second timeout */ - end_ms = now_ms + 1000; /* 1 second timeout */ - - end = buf; - while (end_ms > now_ms) { - if (poll(&pollfd, 1, (int) (end_ms - now_ms)) <= 0) + *buf = '\0'; + while (end_ms > start_ms) { + if (poll(&pollfd, 1, (int) (end_ms - start_ms)) <= 0) break; - - clock_gettime(CLOCK_REALTIME, &now); - now_ms = now.tv_nsec / 1000000L + now.tv_sec * 1000L; + start_ms = now_ms(); if (!(pollfd.revents & POLLIN)) continue; - nread = read(fd, end, size); + nread = read(fd, buf, size); if (nread <= 0) break; - tok = memchr(end, '\n', (size_t) nread); + tok = memchr(buf, '\n', (size_t) nread); if (tok) *tok = '\0'; - end += nread; + buf += nread; size -= (size_t) nread; - if (tok) return strlen(buf); + if (tok) break; } - - return 0; } static void update_blocks(void) { - size_t output_len; - char tmp_output[OUTPUTMAX]; struct block *block; + char buf[OUTPUTMAX]; pid_t child; bool update; int fd; @@ -174,99 +173,65 @@ update_blocks(void) status_text[0] = '\0'; status_len = 0; - concat_status(prefix, strlen(prefix)); + concat_status(prefix); update = false; 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; + if (block->ready || block->sleep && block->sleep_left <= 50) { + block->sleep_left = block->sleep * 1000; block->ready = false; fd = run_block(block->command, &child); - memset(tmp_output, 0, sizeof(tmp_output)); - output_len = read_block(fd, tmp_output, sizeof(tmp_output)-1); - - if (strcmp(tmp_output, block->output)) { - strncpy(block->output, tmp_output, OUTPUTMAX); + read_block(fd, buf, sizeof(buf)-1); + if (strcmp(buf, block->output)) { + strncpy(block->output, buf, OUTPUTMAX); update = true; } close(fd); kill(child, SIGKILL); waitpid(child, NULL, 0); - } else { - output_len = strlen(block->output); } - if (output_len) { + if (*block->output) { if (status_len > 0) - concat_status(delim, strlen(delim)); - concat_status(block->output, output_len); + concat_status(delim); + concat_status(block->output); } } if (!update) return; - concat_status(suffix, strlen(suffix)); + concat_status(suffix); update_status(status_text); } -static float -get_elapsed(void) -{ - static struct timespec last = { .tv_nsec = -1 }; - struct timespec cur; - float diff; - - if (clock_gettime(CLOCK_MONOTONIC, &cur) == -1) - err(1, "clock_gettime"); - - if (last.tv_nsec == -1) - diff = 0; - else - diff = (float) (cur.tv_sec - last.tv_sec) - + (float) (cur.tv_nsec - last.tv_nsec) / 1000000000.f; - - last = cur; - return diff; -} - static void -sleep_till_next(void) +sleep_next(void) { struct block *block; - unsigned int min_sleep; - - last_sleep = 0; /* reset last_sleep in case we return early */ + time_t min_sleep; + time_t start_ms; + time_t stop_ms; min_sleep = 0; for (block = blocks; block; block = block->next) { - if (block->ready || block->sleep_max && block->sleep_left <= 0) + if (block->ready || block->sleep && block->sleep_left <= 0) return; - if (!min_sleep || block->sleep_max && block->sleep_left < min_sleep) - min_sleep = (unsigned int) ceil(block->sleep_left); + if (!min_sleep || block->sleep && block->sleep_left < min_sleep) + min_sleep = block->sleep_left; } - if (min_sleep < 1) - min_sleep = 1; - - alarm(min_sleep); - pause(); - - last_sleep = get_elapsed(); -} - -static void -save_pid(void) -{ - FILE *file; + start_ms = now_ms(); + poll(NULL, 0, (int) min_sleep); + stop_ms = now_ms(); - pid = getpid(); - file = fopen(pid_file, "w+"); - if (!file) err(1, "fopen '%s'", pid_file); - fprintf(file, "%i", pid); - fclose(file); + for (block = blocks; block; block = block->next) { + if (block->sleep) { + block->sleep_left -= (stop_ms - start_ms); + } + } } static void @@ -281,7 +246,7 @@ read_config(void) block = &blocks; while (fgets(line, 256, file)) { - if (strspn(line, " \n\v\t") == strlen(line)) + if (*line == '#' || strspn(line, " \n\v\t") == strlen(line)) continue; if ((tok = strchr(line, '\n'))) *tok = '\0'; @@ -298,7 +263,7 @@ read_config(void) (*block)->command = strdup(line + 6); (*block)->id = 0; (*block)->sleep_left = 0; - (*block)->sleep_max = 0; + (*block)->sleep = 0; (*block)->ready = true; (*block)->next = NULL; } else if (!strncmp(line, "block-id=", 9)) { @@ -306,7 +271,7 @@ read_config(void) (*block)->id = atoi(line + 9); } else if (!strncmp(line, "block-sleep=", 12)) { if (!*block) errx(1, "missing block="); - (*block)->sleep_max = atoi(line + 12); + (*block)->sleep = atoi(line + 12); } else { errx(1, "invalid config"); } @@ -322,47 +287,38 @@ read_config(void) int main(int argc, const char **argv) { + const char **arg; struct sigaction sa; const char *home; - struct block *block; - int i, screen; - - 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; - case 'h': - fputs(usage, stdout); - return 0; - default: - fputs(usage, stderr); - return 1; + int screen; + FILE *file; + + if (argc <= 0) return 1; + + for (arg = argv + 1; *arg; arg++) { + if ((*arg)[0] != '-') + errx(1, "invalid arg: %s", *arg); + switch ((*arg)[1]) { + case 'C': + strncpy(config, *++arg, sizeof(config)); + break; + case 'h': + fputs(usage, stdout); + return 0; + default: + fputs(usage, stderr); + return 1; } } - /* 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 & 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)) - err(1, "sigaction"); - signal(SIGINT, sigexit); - signal(SIGTERM, sigexit); + read_config(); /* get root window */ dpy = XOpenDisplay(NULL); @@ -370,28 +326,25 @@ main(int argc, const char **argv) screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); - save_pid(); + /* save pid */ + pid = getpid(); + file = fopen(pid_file, "w+"); + if (!file) err(1, "fopen '%s'", pid_file); + fprintf(file, "%i", pid); + fclose(file); - /* check if any blocks require timers */ - for (block = blocks; block; block = block->next) { - if (block->sleep_max != 0) break; - } + /* setup signal handlers & cleanup */ + sigemptyset(&sa.sa_mask); + sa.sa_handler = NULL; + sa.sa_sigaction = sighandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction(SIGUSR1, &sa, NULL)) + err(1, "sigaction"); + signal(SIGINT, sigexit); + signal(SIGTERM, sigexit); - if (block) { - get_elapsed(); - while (1) { - sleep_till_next(); - for (block = blocks; block; block = block->next) { - if (block->sleep_max) { - block->sleep_left -= last_sleep; - } - } - update_blocks(); - } - } else { - while (1) { - pause(); - update_blocks(); - } + while (1) { + sleep_next(); + update_blocks(); } } diff --git a/wmslrc b/wmslrc @@ -0,0 +1,14 @@ +prefix= +suffix= +delim= | + +# a block that is only updated on signal +block=date +block-id=10 + +# a block that is update every 5 seconds +block=date +block-sleep=5 + +# a block that is only updated on start +block=date