ztatus

[discontinued] Status bar for dwm, and simple notification daemon.
git clone https://noxz.tech/git/ztatus.git
ztatus

commit: 8212082542cf4f90ab5aa4e20d46f48a20d42fd7
parent: 7935a9de1bc0b06000d6712d675f78a27b0b5d5a
author: Chris Noxz <chris@noxz.tech>
date:   Mon, 30 Mar 2020 14:20:26 +0200
simplify ztatus to a simple clock and notification daemon
Mconfig.def.h102+-
Mconfig.mk7+-
Mztatus.123+-
Mztatus.c1018+++++---------------
4 files changed, 280 insertions(+), 870 deletions(-)
diff --git a/config.def.h b/config.def.h
@@ -1,96 +1,14 @@
-#define FIFO_PATH           "/tmp/ztatus.fifo"
-#define MAIL_DIR_0          "~/mail/webmail-0-local/INBOX/new"
-#define MAIL_DIR_1          "~/mail/webmail-1-local/INBOX/new"
-#define UPDATES_CMD         "pkg queue | wc -l"
-#define MIN_INTERVAL        1
-#define DELAY_INTERVAL      5
+/* format of the clock normally displayed */
+#define FORMAT_TIME     "\x07%04u-%02u-%02u %02u:%02u:%02u \x08 \ue029\ue02a "
 
-#define FRMT_VOLUME         "%s\ue00a\x07%2d%%  "
-#define FRMT_POWER          "\x09%s\x07%2d%%  "
-#define FRMT_TEMP           "\x07\ue00c%s%2d°C  "
-#define FRMT_CPU            "\x07\ue015%s%2d%%  "
-#define FRMT_MEM            "\x07\ue016%s%2d%%  "
-#define FRMT_MAIL           "%s\ue00d\x07%d  "
-#define FRMT_UPDATES        "%s\ue00e\x07%d  "
-#define FRMT_DATE           "\x07\ue00f\x07%04u-%02u-%02u  "
-#define FRMT_TIME           "\x09\ue010\x07%02u:%02u  "
-#define FRMT_ICON           "\x02\ue000"
+/* format of notifications */
+#define FORMAT_NOTIFY   "\x0a %s "
 
-static Limit limit_volume[] = {
-    {1, "\x08"},
-    {0, "\x07"},
-    {-1}
-};
-static Limit limit_power[] = {
-    {75, "\ue011"},
-    {50, "\ue012"},
-    {25, "\ue013"},
-    {0, "\ue014"},
-    {0, "\ue00b"},
-    {-1}
-};
-static Limit limit_temp[] = {
-    {60, "\x09"},
-    {0, "\x07"},
-    {-1}
-};
-static Limit limit_cpu[] = {
-    {75, "\x09"},
-    {0, "\x07"},
-    {-1}
-};
-static Limit limit_mem[] = {
-    {80, "\x09"},
-    {0, "\x07"},
-    {-1}
-};
-static Limit limit_mail[] = {
-    {1, "\x08"},
-    {0, "\x07"},
-    {-1}
-};
-static Limit limit_updates[] = {
-    {1, "\x08"},
-    {0, "\x07"},
-    {-1}
-};
+/* path of fifo */
+#define FIFO_PATH       "/tmp/ztatus.fifo"
 
-static Element elements[] = {
-    /* handler          renderer          format          val/vis arguments */
-    [ElmVolume]         = { get_volume,   render_generic, FRMT_VOLUME,    -1,1,   {.l = limit_volume} },
-    [ElmPower]          = { get_power,    render_power,   FRMT_POWER,     -1,1,   {.l = limit_power, .d = 10} },
-    [ElmTemperature]    = { get_temp,     render_generic, FRMT_TEMP,      -1,1,   {.l = limit_temp, .d = 10} },
-    [ElmCPU]            = { get_cpu,      render_generic, FRMT_CPU,       -1,1,   {.l = limit_cpu, .d = 1} },
-    [ElmMemory]         = { get_memory,   render_generic, FRMT_MEM,       -1,1,   {.l = limit_mem, .d = 1} },
-    [ElmMail0]          = { get_mail,     render_generic, FRMT_MAIL,      -1,1,   {.l = limit_mail, .t = MAIL_DIR_0} },
-    [ElmMail1]          = { get_mail,     render_generic, FRMT_MAIL,      -1,1,   {.l = limit_mail, .t = MAIL_DIR_1} },
-    [ElmUpdates]        = { get_updates,  render_generic, FRMT_UPDATES,   -1,1,   {.l = limit_updates} },
-    [ElmDate]           = { get_date,     render_date,    FRMT_DATE,      -1,0,   {.d = -60} },
-    [ElmTime]           = { get_time,     render_time,    FRMT_TIME,      -1,0,   {.d = -60} },
-    [ElmIcon]           = { NULL,         NULL,           FRMT_ICON,      -1,1,   {0} },
-};
+/* maximum length of the status bar */
+#define STATUS_LENGTH   256
 
-static Command commands[] = {
-    /* --- toggles ----------------------------------------------------------*/
-    { "toggle volume",      toggle_element, {.v = &elements[ElmVolume]} },
-    { "toggle power",       toggle_element, {.v = &elements[ElmPower]} },
-    { "toggle temperature", toggle_element, {.v = &elements[ElmTemperature]} },
-    { "toggle cpu",         toggle_element, {.v = &elements[ElmCPU]} },
-    { "toggle memory",      toggle_element, {.v = &elements[ElmMemory]} },
-    { "toggle mail0",       toggle_element, {.v = &elements[ElmMail0]} },
-    { "toggle mail1",       toggle_element, {.v = &elements[ElmMail1]} },
-    { "toggle updates",     toggle_element, {.v = &elements[ElmUpdates]} },
-    { "toggle date",        toggle_element, {.v = &elements[ElmDate]} },
-    { "toggle time",        toggle_element, {.v = &elements[ElmTime]} },
-    { "toggle icon",        toggle_element, {.v = &elements[ElmIcon]} },
-
-    /* --- updates ----------------------------------------------------------*/
-    { "update volume",      update_element, {.v = &elements[ElmVolume]} },
-    { "update mail0",       update_element, {.v = &elements[ElmMail0]} },
-    { "update mail1",       update_element, {.v = &elements[ElmMail1]} },
-    { "update updates",     update_element, {.v = &elements[ElmUpdates]} },
-    { "update power",       update_element, {.v = &elements[ElmPower]} },
-
-    /* --- miscellaneous ----------------------------------------------------*/
-    { "notify ...",         notify,         {0} },
-};
+/* amount of time a notification is displayed */
+#define DELAY_INTERVAL  5
diff --git a/config.mk b/config.mk
@@ -1,16 +1,15 @@
 # ztatus version
-VERSION = 0.1.1
+VERSION = 0.9
 
 # paths
 PREFIX = /usr/local
 MANPREFIX = $(PREFIX)/share/man
 X11INC = /usr/X11R6/include
 X11LIB = /usr/X11R6/lib
-ASOUND = /usr/include/alsa
 
 # includes and libs
-INCS = -I$(X11INC) -I$(ASOUND)
-LIBS = -L$(X11LIB) -lX11 -lasound -lz
+INCS = -I$(X11INC)
+LIBS = -L$(X11LIB) -lX11
 
 # flags
 CFLAGS = -Wall -pedantic -std=c99
diff --git a/ztatus.1 b/ztatus.1
@@ -3,31 +3,20 @@
 .Os
 .Sh NAME
 .Nm ztatus
-.Nd creates a status bar for dwm, and also acts as a simple notification
-daemon.
+.Nd creates a status bar containing just a simple clock for dwm, and also acts
+as a simple notification daemon.
 .Sh SYNOPSIS
 .Nm
-.Op Fl d
-.Op Fl n Ar text
 .Sh DESCRIPTION
 .Nm
 creates a status bar for dwm, and other window managers using the root WM_NAME
-as input for displaying a status bar. Except for creating a status bar,
+as input for displaying a status bar. In addition to creating a status bar,
 .Nm
 also acts as a simple notification daemon.
-.Sh OPTIONS
-.Bl -tag -width Ds
-.It Fl d
-Specifies that
+
 .Nm
-should run continuously as a daemon.
-.It Fl n Ar text
-Sends a notification with the value of
-.Ar text
-to a running
-.Nm
-daemon.
-.El
+uses a fifo for dispatching notifications to the status bar. Simply send
+the notifications to the default path /tmp/ztatus.fifo for it to show up.
 .Sh CUSTOMIZATION
 .Nm
 can be customized by creating a custom config.h and (re)compiling the source
diff --git a/ztatus.c b/ztatus.c
@@ -1,6 +1,9 @@
+#include <X11/Xlib.h>
 #include <dirent.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -8,823 +11,324 @@
 #include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
-#include <X11/Xlib.h>
-
-/* alsa */
-#include <alsa/asoundlib.h>
-#include <alsa/mixer.h>
-
-#define PNAME               "ztatus"
-#define LIMIT(x, a, b)      ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
-#define MAX(a, b)           ((a) > (b) ? (a) : (b))
-#define LENGTH(x)           (sizeof (x) / sizeof (x[0]))
-#define STATUS_LENGTH       256
-
-enum {
-    ElmVolume,
-    ElmPower,
-    ElmTemperature,
-    ElmCPU,
-    ElmMemory,
-    ElmMail0,
-    ElmMail1,
-    ElmUpdates,
-    ElmDate,
-    ElmTime,
-    ElmIcon
-};
-
-typedef struct {
-    int threshold;
-    const char *symbol;
-} Limit;
-
-typedef struct {
-    int d;          /* time delay */
-    const char *t;  /* text */
-    const Limit *l; /* limit */
-} Attr;
-
-typedef struct {
-    int i;
-    const void *v;
-} Arg;
-
-typedef struct {
-    int (*handler)(int*, const Attr*);
-    void (*renderer)(char*, void*);
-    const char *format;
-    int value;
-    int visible;
-    const Attr attr;
-} Element;
-
-typedef struct {
-    const char *name;
-    void (*func)(const Arg *, const char*, char*);
-    const Arg arg;
-} Command;
-
-/* global variables */
-static Display *dpy;
-static long cpu_work[2][2] = {{-1}, {-1}};
-static int is_changed = 0;
-static int delay_time = 0;
-static int running = 0;
-static int self_pid = 0;
-static char *status_line = NULL;
-static int fifofd;
-
-/* --- functions referenced in config --- */
-/* get function, to gather data */
-static int get_volume(int*, const Attr*);
-static int get_power(int*, const Attr*);
-static int get_temp(int*, const Attr*);
-static int get_cpu(int*, const Attr*);
-static int get_memory(int*, const Attr*);
-static int get_mail(int*, const Attr*);
-static int get_updates(int*, const Attr*);
-static int get_date(int*, const Attr*);
-static int get_time(int*, const Attr*);
-/* render functions, to render elements */
-static void render_generic(char*, void*);
-static void render_power(char*, void*);
-static void render_date(char*, void*);
-static void render_time(char*, void*);
-/* miscellaneous functions */
-static void toggle_element(const Arg*, const char*, char*);
-static void update_element(const Arg*, const char*, char*);
-static void notify(const Arg*, const char*, char*);
 
 #include "config.h"
 
+#define PNAME "ztatus"
+
 /* functions */
 static int  check_proc(struct dirent*);
-static int  count_until_time(void);
-static int  daemonize(void);
-static int  expand_tilde(const char*, char**);
+static int die(const char*, ...);
+static void dispatch(void);
 static int  get_int(const char*, int*);
 static int  get_pid(void);
-static void render_threshold(char*, const char*, int, const Limit[], int);
-static int  run(const char*);
-static void set_status(char*);
+static void notify(const char*);
+static void render_time(char*);
+static int  run(void);
+static void set_status(const char*);
 static void sigint_handler(int);
-static int list_commands(void);
-static int usage(void);
 
-void
-sigint_handler(int signum)
-{
-    running = 0;
-}
+/* global variables */
+static Display *dpy;
+static int delay_time = 0;
+static int running = 0;
+static int self_pid = 0;
+static int fifofd;
 
 void
-dispatchcmd(void)
-{
-    char buf[BUFSIZ];
-    char *ptr, *line, *next;
-    ssize_t n;
-    int i;
-
-    if ((n = read(fifofd, buf, sizeof(buf) - 1)) == -1)
-        return;
-
-    buf[n] = '\0';
-    line = buf;
-
-    /* read each line as a single command */
-    while (line) {
-        next = strchr(line, '\n');
-        if (next)
-            *next = '\0';
-        for (i = 0; i < LENGTH(commands); i++) {
-            /* check if command has a trailing argument */
-            if ((ptr = strstr(commands[i].name, "...")))
-                n = ptr - commands[i].name;
-            else
-                n = MAX(strlen(line), strlen(commands[i].name));
-            if (strncmp(commands[i].name, line, n) == 0) {
-                commands[i].func(&commands[i].arg, commands[i].name, line);
-                break;
-            }
-        }
-        if (next)
-            *next = '\n';
-        line = next ? next + 1 : NULL;
-    }
-
-    /* make sure fifo is empty */
-    while (errno != EWOULDBLOCK)
-        read(fifofd, buf, sizeof(buf) - 1);
-}
-
-int
-daemonize(void)
-{
-    int i, rv, count;
-    fd_set rfds;
-    struct timeval tv;
-
-    unlink(FIFO_PATH);
-
-    /* setup essentials */
-    if (get_pid() != -1)
-        return 1;
-    if ((mknod(FIFO_PATH, S_IFIFO | 0600, 0)) < 0)
-        return 1;
-    if ((fifofd = open(FIFO_PATH, O_RDWR | O_NONBLOCK)) < 0)
-        return 1;
-    if (!(dpy = XOpenDisplay(NULL)))
-        return 1;
-    if ((status_line = malloc(STATUS_LENGTH)) == NULL)
-        return 1;
-
-    /* handle interupts and terminations */
-    signal(SIGINT, sigint_handler);
-    signal(SIGTERM, sigint_handler);
-
-    /* initialize elements, and bind signal handlers */
-    for (i = 0; i < LENGTH(elements); i++) {
-        if (elements[i].handler)
-            elements[i].handler(&(elements[i].value), &(elements[i].attr));
-    }
-
-    /* get time delay until next full minute.
-     * this will initially sync the time loop */
-    count = -count_until_time() - 1;
-    running = 1;
-
-    while (running) {
-        /* resync count after 5 minutes, as it may shift
-         * due to various things */
-        if (count >= 300)
-            count = 60 - count_until_time();
-
-        count++;
-
-        FD_ZERO(&rfds);
-        FD_SET(fifofd, &rfds);
-
-        tv.tv_sec = MIN_INTERVAL;
-        tv.tv_usec = 0;
-
-        /* select fifo descriptor, and break on failure */
-        if ((rv = select(fifofd + 1, &rfds, NULL, NULL, &tv)) < 0)
-            break;
-
-        /* dispatch command on fifo read */
-        if (FD_ISSET(fifofd, &rfds))
-            dispatchcmd();
-
-        /* prevent data printing if it's delayed.
-         * this is so an external signal can borrow the status line
-         * for a short time, determined by the delay_time */
-        if (delay_time > 0) {
-            delay_time--;
-            continue;
-        }
-
-        /* handle time based elements */
-        for (i = 0; i < LENGTH(elements); i++) {
-            if (!elements[i].handler)
-                continue;
-            if (elements[i].attr.d && (
-                (elements[i].attr.d > 0 && count % elements[i].attr.d == 0)
-                || (elements[i].attr.d < 0 && count >= 0
-                    && count % elements[i].attr.d == 0)
-            ))
-                is_changed |= elements[i].handler(
-                    &(elements[i].value),
-                    &(elements[i].attr));
-        }
-
-        /* render all elements on any change */
-        if (is_changed) {
-            status_line[0] = '\0';
-            for (i = 0; i < LENGTH(elements); i++) {
-                if (!elements[i].visible)
-                    continue;
-                if (elements[i].renderer)
-                    elements[i].renderer(status_line, &elements[i]);
-                else
-                    strcpy(
-                        status_line + strlen(status_line),
-                        elements[i].format);
-            }
-
-            set_status(status_line);
-        }
-    }
-
-    /* free up resources when done */
-    free(status_line);
-    XCloseDisplay(dpy);
-    if (fifofd >= 0)
-        unlink(FIFO_PATH);
-
-    return 0;
-}
-
-int
-expand_tilde(const char *dir, char **out)
+sigint_handler(int signum)
 {
-    const char *home = getenv("HOME");
-
-    if (!(home && strchr(home, '/')))
-        return 0;
-
-    if (strchr(dir, '~') != NULL) {
-        *out = malloc(strlen(home) + strlen(dir));
-        strcpy(*out, home);
-        strcat(*out, dir + 1);
-    } else {
-        *out = malloc(strlen(dir) + 1);
-        strcpy(*out, dir);
-    }
-
-    if (*out)
-        return 1;
-    return 0;
+	running = 0;
 }
 
 int
-get_int(const char *s, int *i)
+die(const char *format, ...)
 {
-    const char *c = s;
+	va_list args;
 
-    *i = 0;
-    while (*c) {
-        if (*c < 48 || *c >= 58)
-            return 0;
-        *i = *i * 10 - 48 + *c;
-        c++;
-    }
+	va_start(args, format);
+	vfprintf(stderr, format, args);
+	va_end(args);
 
-    return 1;
+	return 1;
 }
 
 int
-check_proc(struct dirent *e)
+run(void)
 {
-    char *base_p = NULL;
-    char *comm_p = NULL;
-    char *cmdline_p = NULL;
-    char comm[7] = {0};
-    char cmdline[64] = {0};
-    FILE *fptr = NULL;
-    struct stat sb;
-    int i, l;
-    int pid;
-    int state = 0;
-
-    if (get_int(e->d_name, &pid) == 0 || pid == self_pid)
-        state = -1;
-
-    if (state == 0) {
-        base_p = malloc(strlen(e->d_name) + 7);
-        strcpy(base_p, "/proc/");
-        strcat(base_p, e->d_name);
-
-        if (stat(base_p, &sb) != 0 || !S_ISDIR(sb.st_mode))
-            state = -1;
-    }
-
-    if (state == 0) {
-        comm_p = malloc(strlen(base_p) + 6);
-        strcpy(comm_p, base_p);
-        strcat(comm_p, "/comm");
-
-        if (stat(comm_p, &sb) != 0 || !S_ISREG(sb.st_mode))
-            state = -1;
-    }
-
-    if (state == 0) {
-        cmdline_p = malloc(strlen(base_p) + 9);
-        strcpy(cmdline_p, base_p);
-        strcat(cmdline_p, "/cmdline");
-
-        if (stat(cmdline_p, &sb) != 0 || !S_ISREG(sb.st_mode))
-            state = -1;
-    }
-
-    if (state == 0) {
-        fptr = fopen(comm_p, "rb");
-        for (i = 0, l = sizeof(comm); i < l; i++)
-            comm[i] = fgetc(fptr);
-        fclose(fptr);
-        if (comm[6] != '\n') {
-            state = -1;
-        } else {
-            comm[6] = '\0';
-            if (strcmp(comm, PNAME) != 0)
-                state = -1;
-        }
-    }
-
-    if (state == 0) {
-        fptr = fopen(cmdline_p, "rb");
-        fread(cmdline, sizeof(cmdline), 1, fptr);
-        l = ftell(fptr);
-        fclose(fptr);
-        if (l - 1 > strlen(cmdline))
-            memmove(
-                cmdline,
-                cmdline + strlen(cmdline) + 1,
-                l - strlen(cmdline));
-        if (strcmp(cmdline, "-d") == 0)
-            return pid;
-    }
-
-    if (base_p != NULL)
-        free(base_p);
-    if (comm_p != NULL)
-        free(comm_p);
-    if (cmdline_p != NULL)
-        free(cmdline_p);
-
-    return -1;
+	int rv, count;
+	fd_set rfds;
+	struct timeval tv;
+	char *status_line = NULL;
+
+	unlink(FIFO_PATH);
+
+	/* setup, or fail */
+	if (get_pid() != -1)
+		return die("error: another instance of %s is already running\n", PNAME);
+	if ((mknod(FIFO_PATH, S_IFIFO | 0600, 0)) < 0)
+		return die("error: cannot create fifo '%s'\n", FIFO_PATH);
+	if ((fifofd = open(FIFO_PATH, O_RDWR | O_NONBLOCK)) < 0)
+		return die("error: cannot open fifo '%s'\n", FIFO_PATH);
+	if (!(dpy = XOpenDisplay(NULL)))
+		return die("error: cannot open display\n", FIFO_PATH);
+	if ((status_line = malloc(STATUS_LENGTH)) == NULL)
+		return die("error: failed to allocate memory\n", FIFO_PATH);
+
+	/* handle interrupts and terminations */
+	signal(SIGINT, sigint_handler);
+	signal(SIGTERM, sigint_handler);
+
+	fprintf(stderr, "%s is running as pid: %d\n", PNAME, self_pid);
+
+	running = 1;
+	while (running) {
+		count++;
+
+		/* wait for 1 second or fifofd */
+		FD_ZERO(&rfds);
+		FD_SET(fifofd, &rfds);
+		tv.tv_sec = 1;
+		tv.tv_usec = 0;
+
+		/* select fifo descriptor, and break on failure */
+		if ((rv = select(fifofd + 1, &rfds, NULL, NULL, &tv)) < 0)
+			break;
+
+		/* dispatch on fifo read */
+	if (FD_ISSET(fifofd, &rfds))
+		dispatch();
+
+	/* prevent data printing if it's delayed.
+	 * this is so an external signal can borrow the status line
+	 * for a short time, determined by the delay_time */
+	if (delay_time > 0) {
+		delay_time--;
+		continue;
+	}
+
+		render_time(status_line);
+		set_status(status_line);
+	}
+
+	/* free up resources when done */
+	free(status_line);
+	XCloseDisplay(dpy);
+	if (fifofd >= 0)
+		unlink(FIFO_PATH);
+
+	return 0;
 }
 
 int
 get_pid(void)
 {
-    char path[] = "/proc";
-    DIR *dir;
-    struct dirent *e;
-    int p = -1;
-
-    if ((dir = opendir(path)) != NULL) {
-        while (
-            (e = readdir(dir)) != NULL
-            && (p = check_proc(e)) == -1
-        );
-        closedir(dir);
-    }
-
-    return p;
-}
-
-int
-get_volume(int *value, const Attr *attr)
-{
-    snd_mixer_t *handle;
-    snd_mixer_selem_id_t *sid;
-    const char *card = "default";
-    const char *selem_name = "Master";
-    long min, max, volume = 0;
-    int old_state = *value;
-
-    snd_mixer_open(&handle, 0);
-    snd_mixer_attach(handle, card);
-    snd_mixer_selem_register(handle, NULL, NULL);
-    snd_mixer_load(handle);
-
-    snd_mixer_selem_id_alloca(&sid);
-    snd_mixer_selem_id_set_index(sid, 0);
-    snd_mixer_selem_id_set_name(sid, selem_name);
-    snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
-
-    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
-    snd_mixer_selem_get_playback_volume(elem, 0, &volume);
-    snd_mixer_close(handle);
-
-    *value = ((double)volume / max) * 100;
-
-    if (*value != old_state)
-        return 1;
-    return 0;
-}
-
-int
-get_power(int *value, const Attr *attr)
-{
-    FILE *fp;
-    int energy_now, energy_full, voltage_now, status;
-    int old_value;
-    int old_state;
-
-    old_value = *value;
-    old_state = *value >= 1000;
-
-    fp = fopen("/sys/class/power_supply/BAT0/energy_now", "r");
-    if (!fp)
-        return 0;
-    fscanf(fp, "%d", &energy_now);
-    fclose(fp);
-
-    fp = fopen("/sys/class/power_supply/BAT0/energy_full", "r");
-    if (!fp)
-        return 0;
-    fscanf(fp, "%d", &energy_full);
-    fclose(fp);
-
-    fp = fopen("/sys/class/power_supply/BAT0/voltage_now", "r");
-    if (!fp)
-        return 0;
-    fscanf(fp, "%d", &voltage_now);
-    fclose(fp);
-
-    fp = fopen("/sys/class/power_supply/AC/online", "r");
-    if (!fp)
-        return 0;
-    fscanf(fp, "%d", &status);
-    fclose(fp);
-
-    *value = ((float)energy_now * 1000 / (float)voltage_now) * 100 /\
-        ((float)energy_full * 1000 / (float)voltage_now);
-    *value += (status == 1 ? 1000 : 0);
-
-    if (*value != old_value || (*value >= 1000) != old_state)
-        return 1;
-    return 0;
-}
-
-int
-get_temp(int *value, const Attr *attr)
-{
-    FILE *fp;
-    double res, val = 0;
-    unsigned int i, max;
-    const char* hwmon_file[] = {
-        "/sys/class/hwmon/hwmon1/temp2_input",
-        "/sys/class/hwmon/hwmon1/temp3_input"
-    };
-    int old_value = *value;
-
-    for (i = 0, max = 2; i < max; i++) {
-        if ((fp = fopen(hwmon_file[i], "r")) == NULL)
-            return 0;
-
-        fscanf(fp, "%lf", &res);
-        fclose(fp);
-
-        val = ((i * val) + res) / (i + 1);
-    }
-
-    *value = (int)(val / 1000);
-
-    if (*value != old_value)
-        return 1;
-    return 0;
+	const char *path = "/proc";
+	DIR *dir;
+	struct dirent *e;
+	int p = -1;
+
+	/* check all processes */
+	if ((dir = opendir(path)) != NULL) {
+		while ((e = readdir(dir)) != NULL && (p = check_proc(e)) == -1);
+		closedir(dir);
+	}
+
+	return p;
 }
 
 int
-get_memory(int *value, const Attr *attr)
-{
-    FILE *fp;
-    long ma[5];
-    int old_value = *value;
-
-    fp = fopen("/proc/meminfo", "r");
-    if (fp == NULL)
-        return 0;
-
-    fscanf(fp,
-        "MemTotal: %ld kB\n"
-        "MemFree: %ld kB\n"
-        "MemAvailable: %ld kB\n"
-        "Buffers: %ld kB\n"
-        "Cached: %ld kB\n"
-        , &ma[0], &ma[1], &ma[2], &ma[3], &ma[4]
-    );
-    fclose(fp);
-
-    *value = (int)(100 * ((ma[0] - ma[1]) - (ma[3] + ma[4])) / ma[0]);
-
-    if (*value != old_value)
-        return 1;
-    return 0;
-}
-
-int
-get_cpu(int *value, const Attr *attr)
-{
-    FILE *fp;
-    long la[7];
-    int old_value = *value;
-    int r = 0;
-
-    if ((fp = fopen("/proc/stat", "rb"))) {
-        r = fscanf(fp, "cpu %ld %ld %ld %ld %ld %ld %ld",
-            &la[0], &la[1], &la[2], &la[3], &la[4], &la[5], &la[6]
-        );
-        fclose(fp);
-    }
-
-    if (r != 7)
-        return 0;
-
-    cpu_work[0][0] = la[0] + la[1] + la[2] + la[5] + la[6];
-    cpu_work[0][1] = cpu_work[0][0] + la[3] + la[4];
-
-    if (cpu_work[0][1] == cpu_work[1][1])
-        return 0;
-
-    if (*value >= 0)
-        *value = 100 * \
-            (cpu_work[0][0] - cpu_work[1][0]) /\
-            (cpu_work[0][1] - cpu_work[1][1]);
-    else
-        *value = 0;
-
-    LIMIT(*value, 0, 100);
-
-    /* store work value for later use */
-    cpu_work[1][0] = cpu_work[0][0];
-    cpu_work[1][1] = cpu_work[0][1];
-
-    if (*value != old_value)
-        return 1;
-    return 0;
-}
-
-int
-get_mail(int *value, const Attr *attr)
-{
-    char *dir = NULL;
-    struct dirent *dp;
-    DIR *fd;
-    int old_value = *value;
-    *value = 0;
-
-    if (!expand_tilde(attr->t, &dir))
-        return 0;
-
-    fd = opendir(dir);
-    free(dir);
-
-    if (fd == NULL)
-        return 0;
-
-    while ((dp = readdir(fd)) != NULL) {
-        if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
-            (*value)++;
-    }
-
-    closedir(fd);
-
-    if (*value != old_value)
-        return 1;
-    return 0;
-}
-
-int
-get_updates(int *value, const Attr *attr)
-{
-    int old_value = *value;
-
-    if ((*value = run(UPDATES_CMD)) == -1)
-        *value = old_value;
-
-    if (*value != old_value)
-        return 1;
-    return 0;
-}
-
-int
-get_date(int *value, const Attr *attr)
+get_int(const char *s, int *i)
 {
-    time_t rawtime;
-    struct tm *timeinfo;
-
-    time(&rawtime);
-    timeinfo = localtime(&rawtime);
+	const char *c = s;
 
-    *value = ((timeinfo->tm_year + 1900) * 10000
-        + (timeinfo->tm_mon + 1) * 100
-        + timeinfo->tm_mday
-    );
+	*i = 0;
+	while (*c) {
+		if (*c < 48 || *c >= 58)
+			return 0;
+		*i = *i * 10 - 48 + *c;
+		c++;
+	}
 
-    return 1;
+	return 1;
 }
 
 int
-get_time(int *value, const Attr *attr)
-{
-    time_t rawtime;
-    struct tm *timeinfo;
-
-    time(&rawtime);
-    timeinfo = localtime(&rawtime);
-
-    *value = timeinfo->tm_hour * 60 + timeinfo->tm_min;
-
-    return 1;
-}
-
-void
-render_threshold(char *str, const char *format, int value, const Limit limits[], int start)
+check_proc(struct dirent *e)
 {
-    const Limit *limit = (limits + start);
-    char *tmp = NULL;
-
-    while (limit->threshold >= 0) {
-        if (value >= limit->threshold) {
-            tmp = malloc(16);
-            snprintf(tmp, 16, format, limit->symbol, value);
-            break;
-        }
-        limit++;
-    }
-
-    if (tmp) {
-        strcpy(str + strlen(str), tmp);
-        free(tmp);
-    }
+	char *base_p = NULL;
+	char *comm_p = NULL;
+	char *cmdline_p = NULL;
+	char comm[7] = {0};
+	char cmdline[64] = {0};
+	FILE *fptr = NULL;
+	struct stat sb;
+	int i, l;
+	int pid;
+	int state = 0;
+
+	if (get_int(e->d_name, &pid) == 0 || pid == self_pid)
+		state = -1;
+
+	if (state == 0) {
+		base_p = malloc(strlen(e->d_name) + 7);
+		strcpy(base_p, "/proc/");
+		strcat(base_p, e->d_name);
+		if (stat(base_p, &sb) != 0 || !S_ISDIR(sb.st_mode))
+			state = -1;
+	}
+
+	if (state == 0) {
+		comm_p = malloc(strlen(base_p) + 6);
+		strcpy(comm_p, base_p);
+		strcat(comm_p, "/comm");
+		if (stat(comm_p, &sb) != 0 || !S_ISREG(sb.st_mode))
+			state = -1;
+	}
+
+	if (state == 0) {
+		cmdline_p = malloc(strlen(base_p) + 9);
+		strcpy(cmdline_p, base_p);
+		strcat(cmdline_p, "/cmdline");
+		if (stat(cmdline_p, &sb) != 0 || !S_ISREG(sb.st_mode))
+			state = -1;
+	}
+
+	if (state == 0) {
+		fptr = fopen(comm_p, "rb");
+		for (i = 0, l = sizeof(comm); i < l; i++)
+			comm[i] = fgetc(fptr);
+		fclose(fptr);
+		if (comm[6] != '\n') {
+			state = -1;
+		} else {
+			comm[6] = '\0';
+			if (strcmp(comm, PNAME) != 0)
+				state = -1;
+		}
+	}
+
+	if (state == 0) {
+		fptr = fopen(cmdline_p, "rb");
+		fread(cmdline, sizeof(cmdline), 1, fptr);
+		l = ftell(fptr);
+		fclose(fptr);
+		if (strchr(cmdline, '/') != NULL)
+			memmove(cmdline,
+			        strrchr(cmdline, '/') + 1,
+			        strlen(strrchr(cmdline, '/') + 1)
+			);
+		if (strcmp(cmdline, PNAME) == 0)
+			return pid;
+	}
+
+	if (base_p != NULL)
+		free(base_p);
+	if (comm_p != NULL)
+		free(comm_p);
+	if (cmdline_p != NULL)
+		free(cmdline_p);
+
+	return -1;
 }
 
 void
-render_generic(char *str, void *element)
+render_time(char *str)
 {
-    Element *e = ((Element *)element);
-    render_threshold(str, e->format, e->value, e->attr.l, 0);
+	int z, y, era, time_val;
+	unsigned doe, yoe, doy, mp, d, m;
+	char *tmp = malloc(32);
+	time_t currentTime = time(NULL);
+	struct tm *localTime;
+
+	localTime = localtime(&currentTime);
+	time_val = currentTime + localTime->tm_gmtoff;
+
+	z = time_val / 86400 + 719468;
+	era = (z >= 0 ? z : z - 146096) / 146097;
+	doe = (unsigned)(z - era * 146097);
+	yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
+	y = (int)yoe + era * 400;
+	doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
+	mp = (5 * doy + 2) / 153;
+	d = doy - (153 * mp + 2) / 5 + 1;
+	m = mp + (mp < 10 ? 3 : -9);
+	y += (m <= 2);
+
+	snprintf(tmp, 32, FORMAT_TIME
+		,y ,m ,d
+		,(time_val / 3600) % 24
+		,(time_val / 60) % 60
+		,time_val % 60
+	);
+	strcpy(str, tmp);
+	free(tmp);
 }
 
 void
-render_power(char *str, void *element)
-{
-    Element *e = ((Element *)element);
-    char *nformat = malloc(strlen(e->format) + 1);
-    int start = 0;
-    int v = e->value;
-
-    strcpy(nformat, e->format);
-    if (v >= 1000) {
-        v -= 1000;
-        start = 4;
-        nformat[0] = '\x08';
-    }
-
-    render_threshold(str, nformat, v, e->attr.l, start);
-
-    if (nformat)
-        free(nformat);
+set_status(const char *line) {
+	XStoreName(dpy, DefaultRootWindow(dpy), line);
+	XSync(dpy, False);
 }
 
 void
-render_date(char *str, void *element)
+dispatch(void)
 {
-    Element *e = ((Element *)element);
-    char *tmp = malloc(20);
-
-    snprintf(tmp, 20, e->format
-        ,e->value / 10000
-        ,(e->value % 10000) / 100
-        ,(e->value % 10000) % 100);
-    strcpy(str + strlen(str), tmp);
-    free(tmp);
+	char buf[BUFSIZ];
+	char *line, *next;
+	ssize_t n;
+
+	if ((n = read(fifofd, buf, sizeof(buf) - 1)) == -1)
+		return;
+
+	buf[n] = '\0';
+	line = buf;
+
+	/* read each line as a single notification,
+	 * which means that only that last line will be seen */
+	while (line) {
+		next = strchr(line, '\n');
+		if (next)
+			*next = '\0';
+		notify(line);
+		if (next)
+			*next = '\n';
+		line = next ? next + 1 : NULL;
+	}
+
+	/* make sure fifo is empty */
+	while (errno != EWOULDBLOCK)
+		read(fifofd, buf, sizeof(buf) - 1);
 }
 
 void
-render_time(char *str, void *element)
-{
-    Element *e = ((Element *)element);
-    char *tmp = malloc(16);
-
-    snprintf(tmp, 16, e->format, e->value / 60, e->value % 60);
-    strcpy(str + strlen(str), tmp);
-    free(tmp);
-}
-
-int
-run(const char* cmd)
+notify(const char* input)
 {
-    FILE *fp;
-    char rval[8];
-    int val;
+	char *status_line = NULL;
 
-    if ((fp = popen(cmd, "r")) == NULL)
-        return -1;
+	if ((status_line = malloc(STATUS_LENGTH)) == NULL)
+		return;
 
-    if (fgets(rval, sizeof(rval), fp) != NULL)
-        sscanf(rval, "%d", &val);
+	delay_time = (int)(DELAY_INTERVAL);
 
-    pclose(fp);
-
-    return val;
+	snprintf(status_line, STATUS_LENGTH, FORMAT_NOTIFY, input);
+	set_status(status_line);
+	free(status_line);
 }
 
 int
-count_until_time(void) {
-    time_t rawtime;
-    struct tm *timeinfo;
-
-    time(&rawtime);
-    timeinfo = localtime(&rawtime);
-
-    return 60 - timeinfo->tm_sec;
-}
-
-void
-set_status(char *value) {
-    XStoreName(dpy, DefaultRootWindow(dpy), value);
-    XSync(dpy, False);
-
-    is_changed = 0;
-}
-
-void
-toggle_element(const Arg *arg, const char* cmd, char* input)
-{
-    Element *e = ((Element *)arg->v);
-    e->visible ^= 1;
-    is_changed = 1;
-}
-
-void
-update_element(const Arg *arg, const char* cmd, char* input)
-{
-    Element *e = ((Element *)arg->v);
-    is_changed |= e->handler(&(e->value), &(e->attr));
-}
-
-void
-notify(const Arg *arg, const char* cmd, char* input)
+main(int argc, char *argv[])
 {
-    int l = strlen(cmd) - 3;
-
-    if (l <= 0 || strlen(input) <= l)
-        return;
+	char *s;
+	self_pid = getpid();
 
-    delay_time = (int)(DELAY_INTERVAL / MIN_INTERVAL);
-
-    snprintf(status_line, STATUS_LENGTH - 1, "\x0a%s", input + l);
-    set_status(status_line);
-}
-
-int
-list_commands(void)
-{
-    int i;
-    for (i = 0; i < LENGTH(commands); i++)
-        fprintf(stdout, "%s\n", commands[i].name);
-    return 0;
-}
+	if ((s = strrchr(argv[0], '/')) == NULL)
+		s = argv[0];
+	else
+		s++;
 
-int
-usage(void)
-{
-    fprintf(stderr, "usage: %s [-d] [-l]\n", PNAME);
-    fprintf(stderr, "  -d   daemonize\n");
-    fprintf(stderr, "  -l   list commands\n");
-    return 0;
-}
+	if (strcmp(s, PNAME) != 0)
+		return die("error: program must run as '%s'\n", PNAME);
 
-int
-main(int argc, char *argv[])
-{
-    char *s;
-    self_pid = getpid();
-
-    if ((s = strrchr(argv[0], '/')) == NULL)
-        s = argv[0];
-    else
-        s++;
-
-    if (argc == 2 && strcmp(argv[1], "-d") == 0 && strcmp(s, PNAME) == 0)
-        return daemonize();
-    else if (argc == 2 && strcmp(argv[1], "-l") == 0)
-        return list_commands();
-    else
-        return usage();
+	return run();
 }