dwmblocks

git clone git://mattcarlson.org/repos/dwmblocks.git
Log | Files | Refs

commit 2d61dbcccafd7feebc7b4f9cee6f7ad702c02c74
Author: Matthew Carlson <matt@mcarlson.xyz>
Date:   Thu,  8 Jul 2021 19:25:42 +0000

dwmblocks

Diffstat:
AMakefile | 18++++++++++++++++++
Aconfig.h | 21+++++++++++++++++++++
Adwmblocks.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 244 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,18 @@ +.POSIX: + +PREFIX = /usr/local +CC = gcc + +dwmblocks: dwmblocks.o + $(CC) dwmblocks.o -lX11 -o dwmblocks +dwmblocks.o: dwmblocks.c config.h + $(CC) -c dwmblocks.c +install: dwmblocks + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dwmblocks $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dwmblocks + rm -f dwmblocks *.o +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dwmblocks + +.PHONY: install uninstall diff --git a/config.h b/config.h @@ -0,0 +1,21 @@ +#define CMDLENGTH 50 +#define DELIMITER "  " + +typedef struct { + char* command; + unsigned int interval; + unsigned int signal; + char output[CMDLENGTH]; + int pipe[2]; +} Block; + +static Block blocks[] = { + {"sb-mail", 1800, 1}, + {"sb-music", 0, 2}, + {"sb-disk", 1800, 4}, + {"sb-memory", 10, 3}, + {"sb-loadavg", 10, 9}, + {"sb-volume", 0, 5}, + {"sb-battery", 5, 6}, + {"sb-date", 20, 7}, + {"sb-network", 5, 8}}; diff --git a/dwmblocks.c b/dwmblocks.c @@ -0,0 +1,205 @@ +#include <X11/Xlib.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "config.h" + +#define LEN(arr) (sizeof(arr) / sizeof(arr[0])) +#define MIN(x, y) (x > y ? y : x) + +static Display *dpy; +static int screen; +static Window root; +static char statusBar[2][LEN(blocks) * (CMDLENGTH + LEN(DELIMITER) - 1) + 1]; +static int statusContinue = 1; +void (*writeStatus)(); + +int gcd(int a, int b) { + int temp; + while (b > 0) { + temp = a % b; + a = b; + b = temp; + } + return a; +} + +void replace(char *str, char old, char new) { + for (char *ch = str; *ch; ch++) + if (*ch == old) + *ch = new; +} + +void getCommand(int i, const char *button) { + Block *block = blocks + i; + + if (fork() == 0) { + dup2(block->pipe[1], STDOUT_FILENO); + close(block->pipe[0]); + close(block->pipe[1]); + + // Temporary hack + char cmd[1024]; + sprintf(cmd, "echo \"_$(%s)\"", block->command); + + char *command[] = {"/bin/sh", "-c", cmd, NULL}; + if (button) setenv("BLOCK_BUTTON", button, 1); + setsid(); + execvp(command[0], command); + } +} + +void getCommands(int time) { + for (int i = 0; i < LEN(blocks); i++) + if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0)) + getCommand(i, NULL); +} + +void getSignalCommand(int signal) { + for (int i = 0; i < LEN(blocks); i++) + if (blocks[i].signal == signal) + getCommand(i, NULL); +} + +int getStatus(char *new, char *old) { + strcpy(old, new); + new[0] = '\0'; + for (int i = 0; i < LEN(blocks); i++) { + Block *block = blocks + i; + if (strlen(block->output) > (block->signal != 0)) + strcat(new, DELIMITER); + strcat(new, block->output); + } + new[strlen(new)] = '\0'; + return strcmp(new, old); +} + +void debug() { + // Only write out if text has changed + if (!getStatus(statusBar[0], statusBar[1])) return; + + write(STDOUT_FILENO, statusBar[0], strlen(statusBar[0])); + write(STDOUT_FILENO, "\n", 1); +} + +void setRoot() { + // Only set root if text has changed + if (!getStatus(statusBar[0], statusBar[1])) return; + + Display *d = XOpenDisplay(NULL); + if (d) dpy = d; + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + XStoreName(dpy, root, statusBar[0]); + XCloseDisplay(dpy); +} + +void buttonHandler(int sig, siginfo_t *si, void *ucontext) { + sig = si->si_value.sival_int >> 8; + + int i = 0; + while (blocks[i].signal != sig) i++; + const char button[2] = {'0' + si->si_value.sival_int & 0xff, '\0'}; + getCommand(i, button); +} + +void signalHandler(int signal) { getSignalCommand(signal - SIGRTMIN); } + +void termHandler(int signal) { + statusContinue = 0; + exit(EXIT_SUCCESS); +} + +void childHandler() { + for (int i = 0; i < LEN(blocks); i++) { + Block *block = blocks + i; + + int bytesToRead = CMDLENGTH; + char *output = block->output; + if (block->signal) output++, bytesToRead--; + + char placebo; + if (read(block->pipe[0], &placebo, 1) == 1) { + char buffer[PIPE_BUF]; + read(block->pipe[0], buffer, PIPE_BUF); + replace(buffer, '\n', '\0'); + strncpy(output, buffer, bytesToRead); + break; + } + } + writeStatus(); +} + +void setupSignals() { + signal(SIGTERM, termHandler); + signal(SIGINT, termHandler); + + // Handle block update signals + struct sigaction sa; + for (int i = 0; i < LEN(blocks); i++) { + if (blocks[i].signal > 0) { + signal(SIGRTMIN + blocks[i].signal, signalHandler); + sigaddset(&sa.sa_mask, SIGRTMIN + blocks[i].signal); + } + } + + // Handle mouse events + sa.sa_sigaction = buttonHandler; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGUSR1, &sa, NULL); + + // Handle exit of forks + struct sigaction sigchld_action = { + .sa_handler = childHandler, + .sa_flags = SA_NOCLDWAIT, + }; + sigaction(SIGCHLD, &sigchld_action, NULL); +} + +void statusLoop() { + getCommands(0); + + unsigned int sleepInterval = -1; + for (int i = 0; i < LEN(blocks); i++) + if (blocks[i].interval) + sleepInterval = gcd(blocks[i].interval, sleepInterval); + + unsigned int i = 0; + struct timespec sleepTime = {sleepInterval, 0}; + struct timespec toSleep = sleepTime; + + while (statusContinue) { + // Sleep for `sleepTime` even on being interrupted + if (nanosleep(&toSleep, &toSleep) == -1) continue; + + // Write to status after sleeping + getCommands(i); + + // After sleep, reset timer and update counter + toSleep = sleepTime; + i += sleepInterval; + } +} + +int main(int argc, char **argv) { + writeStatus = setRoot; + for (int i = 0; i < argc; i++) + if (!strcmp("-d", argv[i])) writeStatus = debug; + + for (int i = 0; i < LEN(blocks); i++) { + Block *block = blocks + i; + pipe(block->pipe); + fcntl(block->pipe[0], F_SETFL, O_NONBLOCK); + + if (block->signal) block->output[0] = block->signal; + } + + setupSignals(); + statusLoop(); +}