diff options
Diffstat (limited to 'mstatus.c')
-rw-r--r-- | mstatus.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/mstatus.c b/mstatus.c new file mode 100644 index 0000000..2599db0 --- /dev/null +++ b/mstatus.c @@ -0,0 +1,289 @@ +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <linux/limits.h> +#include <paths.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdnoreturn.h> +#include <signal.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <X11/Xlib.h> + +#define CTOI(x) ((x) ^ 48) + +struct Block { + int pos; + char *text; + bool remove; +}; + +bool rflag; +const char *argv0; +struct { + char *str; + size_t len; +} seperator = {.str = " | ", .len = 3}; + +static noreturn void +usage(void) +{ + fprintf(stderr, "Usage: %s [-r] [-s seperator]\n", argv0); + exit(EXIT_FAILURE); +} + +static noreturn void +die(const char *s) +{ + char *err = strerror(errno); + syslog(LOG_ERR, "%s: %s", s, err); + fprintf(stderr, "%s: %s: %s\n", argv0, s, err); + exit(EXIT_FAILURE); +} + +static void +xfork(void) +{ + pid_t pid = fork(); + if (pid == -1) + die("fork"); + if (pid != 0) + exit(EXIT_SUCCESS); +} + +static void * +xrealloc(void *ptr, size_t size) +{ + void *ret; + if (!(ret = realloc(ptr, size))) + die("realloc"); + return ret; +} + +static void +xfree(char **ptr) +{ + free(*ptr); + *ptr = NULL; +} + +static void +write_status(struct Block b) +{ + static struct { + char **blocks; + int count; + size_t length; + } sb; + + if (b.remove) { + if (b.pos > sb.count) + return; + + /* b.remove && !b.pos is a special case to remove everything from the bar */ + if (!b.pos) { + for (int i = 0; i < sb.count; i++) + free(sb.blocks[i]); + sb.count = 0; + sb.blocks = xrealloc(sb.blocks, sizeof(char *)); + goto update_bar; + } + + /* If the block is not NULL we free it */ + if (sb.blocks[--b.pos]) { + sb.length -= strlen(sb.blocks[b.pos]); + xfree(&sb.blocks[b.pos]); + } + + /* If the block is the last one, we resize the bar to remove trailing NULL blocks */ + if (b.pos + 1 == sb.count) { + for (; !sb.blocks[b.pos] && b.pos; b.pos--) + sb.count--; + sb.blocks = xrealloc(sb.blocks, sizeof(char *) * ++b.pos); + } + goto update_bar; + } + + /* If the position exceeds the space allocated, allocate more blocks */ + if (b.pos > sb.count) { + sb.blocks = xrealloc(sb.blocks, sizeof(char *) * b.pos); + /* Make sure to set all the newly allocated blocks to NULL */ + for (int i = sb.count; i < b.pos; i++) + sb.blocks[i] = NULL; + sb.count = b.pos; + } + + /* If the block is NULL we dont need to bother with strlen and free */ + if (sb.blocks[--b.pos]) { + sb.length -= strlen(sb.blocks[b.pos]); + xfree(&sb.blocks[b.pos]); + } + if (!(sb.blocks[b.pos] = strdup(b.text))) + die("strdup"); + sb.length += strlen(b.text); + + /* + * The buffer to store the text that will be displayed in. It needs space for the text, the + * seperators between the different blocks, the NUL byte at the end, and the right padding + * space. + */ +update_bar:; + char buf[sb.length + (sb.count - 1) * seperator.len + 2]; + memset(buf, '\0', sb.length + 1); + + /* Double for loops so that the seperator isnt printed to the left of the first block */ + int i; + for (i = 0; i < sb.count; i++) { + if (sb.blocks[i]) { + strcpy(buf, sb.blocks[i]); + break; + } + } + for (i++; i < sb.count; i++) + if (sb.blocks[i]) + sprintf(buf, "%s%s%s", buf, seperator.str, sb.blocks[i]); + if (rflag) + strcat(buf, " "); + + /* Xlib magic to set the DWM status */ + Display *dpy = XOpenDisplay(NULL); + int screen = DefaultScreen(dpy); + Window root = RootWindow(dpy, screen); + (void) XStoreName(dpy, root, buf); + (void) XCloseDisplay(dpy); +} + +static bool +process(char *line, ssize_t len, struct Block *b) +{ + /* For some reason output with newlines can cause performance issues */ + if (line[--len] == '\n') + line[len] = '\0'; + + if (*line == '-') { + b->remove = true; + line++; + } + else + b->remove = false; + + if (!isdigit(*line)) + b->pos = 1; /* Default position */ + else { + b->pos = 0; + while (isdigit(*line)) + b->pos = b->pos * 10 + CTOI(*line++); + if (!b->pos && !b->remove) + return false; + } + + b->text = line; + return true; +} + +static void +create_fifo(char *fifo_path) +{ + char *runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (runtime_dir) { + size_t end = strlen(runtime_dir) - 1; + if (runtime_dir[end] == '/') + runtime_dir[end] = '\0'; + sprintf(fifo_path, "%s/%s.pipe", runtime_dir, argv0); + } + else + sprintf(fifo_path, _PATH_VARRUN "user/%d/%s.pipe", getuid(), argv0); + + umask(0); + +create_fifo: + if (mkfifo(fifo_path, DEFFILEMODE) == -1) { + if (errno == EEXIST) { + if (unlink(fifo_path) == -1) + die("unlink"); + goto create_fifo; + } + else + die("mkfifo"); + } + + syslog(LOG_INFO, "Created input FIFO '%s'", fifo_path); +} + +static void +daemonize(void) +{ + xfork(); + if (setsid() == -1) + die("setsid"); + + (void) signal(SIGCHLD, SIG_IGN); + xfork(); + + (void) chdir("/"); + (void) close(STDIN_FILENO); + (void) close(STDOUT_FILENO); + (void) close(STDERR_FILENO); + + stdin = fopen(_PATH_DEVNULL, "r"); + stdout = fopen(_PATH_DEVNULL, "w+"); + stderr = fopen(_PATH_DEVNULL, "w+"); + + syslog(LOG_INFO, "Daemonized '%s'", argv0); +} + +int +main(int argc, char **argv) +{ + (void) argc; + argv0 = argv[0]; + + int opt; + while ((opt = getopt(argc, argv, ":rs:")) != -1) { + switch (opt) { + case 'r': + rflag = true; + break; + case 's': + seperator.str = optarg; + seperator.len = strlen(optarg); + break; + default: + usage(); + } + } + + openlog(argv0, LOG_PID | LOG_CONS, LOG_DAEMON); + char fifo_path[PATH_MAX]; + create_fifo(fifo_path); + daemonize(); + + char *line = NULL; + size_t len = 0; + while (true) { + FILE *fp; + if (!(fp = fopen(fifo_path, "r"))) + die("fopen"); + + ssize_t nr; + while ((nr = getline(&line, &len, fp)) != -1) { + syslog(LOG_INFO, "Recieved command '%s'", line); + struct Block b; + if (!process(line, nr, &b)) + continue; + write_status(b); + } + if (ferror(fp)) + die("getline"); + + (void) fclose(fp); + } + /* NOTREACHED */ +} |