ztatus

Status bar for dwm, and simple notification daemon.
git clone git://git.noxz.tech/ztatus
Log | Files | Refs | LICENSE

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

Diffstat:
Mconfig.def.h | 102++++++++-----------------------------------------------------------------------
Mconfig.mk | 7+++----
Mztatus.1 | 23++++++-----------------
Mztatus.c | 1018++++++++++++++++++++-----------------------------------------------------------
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(); }