aboutsummaryrefslogtreecommitdiff
path: root/mstatus.c
diff options
context:
space:
mode:
authorMango0x45 <thomasvoss@live.com> 2021-08-04 11:23:55 +0200
committerMango0x45 <thomasvoss@live.com> 2021-08-04 11:23:55 +0200
commitae1db6545981f7bb55f0d0b662a18c55a0a8f1cf (patch)
tree7137923bae0439d6c3ce9619245c504255c5c8c0 /mstatus.c
Genesis commit
Diffstat (limited to 'mstatus.c')
-rw-r--r--mstatus.c289
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 */
+}