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
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(¤tTime);
+ 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();
}