ztatus

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

commit b83fee8c437e333a3dafe030e60b35d86ae8bfb9
Author: Chris Noxz <chris@noxz.tech>
Date:   Thu,  3 Jan 2019 14:06:08 +0100

Initial commit

Diffstat:
A.gitignore | 3+++
ALICENSE | 21+++++++++++++++++++++
AMakefile | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.def.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.mk | 22++++++++++++++++++++++
Aztatus.1 | 36++++++++++++++++++++++++++++++++++++
Aztatus.c | 819+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 1020 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +config.h +ztatus +ztatus.o diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +© 2018-2019 Chris Noxz <chris@noxz.tech> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,51 @@ +.POSIX: + +include config.mk + +SRC = ztatus.c +OBJ = $(SRC:.c=.o) + +all: options ztatus + +options: + @echo ztatus build options: + @echo "VERSION = $(VERSION)" + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + @echo CC $< + $(CC) $(STCFLAGS) -c $< + +${OBJ}: config.h config.mk + +config.h: + @echo creating $@ from config.def.h + @cp config.def.h $@ + +ztatus: $(OBJ) + @echo CC -o $@ + $(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + @echo cleaning + rm -f ztatus $(OBJ) + +install: ztatus + @echo installing executable to ${PREFIX}/bin + mkdir -p $(PREFIX)/bin + cp -f ztatus $(PREFIX)/bin + chmod 755 $(PREFIX)/bin/ztatus + @echo installing manual page to ${MANPREFIX}/man1 + mkdir -p ${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < ztatus.1 > ${MANPREFIX}/man1/ztatus.1 + chmod 644 ${MANPREFIX}/man1/ztatus.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/ztatus + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/ztatus.1 + +.PHONY: all options clean install uninstall diff --git a/config.def.h b/config.def.h @@ -0,0 +1,68 @@ +#define NOTIFICATION_DIR "~/.ztatus" +#define NOTIFICATION_PATH "~/.ztatus/notification" +#define MAIL_DIR "~/mail/webmail/INBOX/new" +#define UPDATES_CMD "pkg queue | wc -l" +#define MIN_INTERVAL 1 // Minumum update time (seconds) +#define DELAY_INTERVAL 5 // Seconds of delay when initialized +#define SIG_VOLUME 35 // RTMIN+1 +#define SIG_MAIL 36 // RTMIN+2 +#define SIG_UPDATES 37 // RTMIN+3 +#define SIG_DELAY 40 // RTMIN+6 +#define SIG_NOTIFY 41 // RTMIN+7 + +#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" + +static Limit limit_volume[] = { + {1, "\x08"}, + {0, "\x07"} +}; +static Limit limit_power[] = { + {75, "\ue011"}, + {50, "\ue012"}, + {25, "\ue013"}, + {0, "\ue014"}, + {0, "\ue00b"} +}; +static Limit limit_temp[] = { + {60, "\x09"}, + {0, "\x07"} +}; +static Limit limit_cpu[] = { + {75, "\x09"}, + {0, "\x07"} +}; +static Limit limit_mem[] = { + {80, "\x09"}, + {0, "\x07"} +}; +static Limit limit_mail[] = { + {1, "\x08"}, + {0, "\x07"} +}; +static Limit limit_updates[] = { + {1, "\x08"}, + {0, "\x07"} +}; + +static Element elements[ELEMENT_COUNT] = { + /* handler renderer format value arguments */ + { get_volume, render_volume, FRMT_VOLUME, -1, { .s = SIG_VOLUME } }, + { get_power, render_power, FRMT_POWER, -1, { .t = 1 } }, + { get_temp, render_temp, FRMT_TEMP, -1, { .t = 10 } }, + { get_cpu, render_cpu, FRMT_CPU, -1, { .t = 1 } }, + { get_memory, render_mem, FRMT_MEM, -1, { .t = 1 } }, + { get_mail, render_mail, FRMT_MAIL, -1, { .s = SIG_MAIL } }, + { get_updates, render_updates, FRMT_UPDATES, -1, { .s = SIG_UPDATES } }, + { get_date, render_date, FRMT_DATE, -1, { .t = -60 } }, + { get_time, render_time, FRMT_TIME, -1, { .t = -60 } }, + { NULL, NULL, FRMT_ICON, -1, { 0 } }, +}; diff --git a/config.mk b/config.mk @@ -0,0 +1,22 @@ +# ztatus version +VERSION = 0.1.1 + +# 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 + +# flags +CFLAGS = -Wall -pedantic -std=c99 +CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) + +# compiler and linker +CC = gcc diff --git a/ztatus.1 b/ztatus.1 @@ -0,0 +1,36 @@ +.Dd $Mdocdate$ +.Dt ZTATUS 1 +.Os +.Sh NAME +.Nm ztatus +.Nd creates a status bar 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, +.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 +.Sh CUSTOMIZATION +.Nm +can be customized by creating a custom config.h and (re)compiling the source +code. +.Sh SEE ALSO +.Xr dwm 1 diff --git a/ztatus.c b/ztatus.c @@ -0,0 +1,819 @@ +#include <dirent.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#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 ELEMENT_COUNT 10 + +typedef struct { + int s; /* signal */ + int t; /* time delay */ +} Arg; + +typedef struct { + int (*handler)(int*); + void (*renderer)(char*, const char*, int); + const char *format; + int value; + const Arg arg; +} Element; + +typedef struct { + int threshold; + const char *symbol; +} Limit; + +/* X11 display */ +static Display *dpy; + +/* resource holders */ +static long cpu_work[2][2] = {{-1}, {-1}}; + +/* flags, and miscellaneous */ +static int is_changed = 0; +static int delay_time = 0; +static int running = 0; +static int self_pid = 0; +static char notification[128] = {0}; + +static int get_volume(int*); +static int get_power(int*); +static int get_temp(int*); +static int get_cpu(int*); +static int get_memory(int*); +static int get_mail(int*); +static int get_updates(int*); +static int get_date(int*); +static int get_time(int*); + +static void render_volume(char*, const char*, int); +static void render_power(char*, const char*, int); +static void render_temp(char*, const char*, int); +static void render_cpu(char*, const char*, int); +static void render_mem(char*, const char*, int); +static void render_mail(char*, const char*, int); +static void render_updates(char*, const char*, int); +static void render_date(char*, const char*, int); +static void render_time(char*, const char*, int); + +#include "config.h" + +/* functions */ +static int check_proc(struct dirent*); +static int count_until_time(void); +static int daemonize(void); +static void delay_handler(int); +static void element_handler(int); +static int expand_tilde(const char*, char**); +static int get_int(const char*, int*); +static int get_pid(void); +static int notify(const char*); +static void notify_handler(int); +static void render_threshold(char*, const char*, int, Limit[], int); +static void render_threshold_start(char*, const char*, int, Limit[], int, int); +static int run(const char*); +static void set_status(char*); +static void sigint_handler(int); +static void usage(void); + +void +sigint_handler(int signum) +{ + running = 0; +} + +void +delay_handler(int signum) +{ + delay_time = DELAY_INTERVAL; +} + +void +element_handler(int signum) +{ + int i; + for (i = 0; i < ELEMENT_COUNT; i++) { + if (elements[i].arg.s) + is_changed |= elements[i].handler(&(elements[i].value)); + } +} + +void +notify_handler(int signum) +{ + char *path = NULL; + FILE *fptr = NULL; + + expand_tilde(NOTIFICATION_PATH, &path); + + if (path == NULL) + return; + + if ((fptr = fopen(path, "rb")) != NULL) { + fread(notification, sizeof(notification), 1, fptr); + notification[ftell(fptr)] = '\0'; + fclose(fptr); + remove(path); + + if (strlen(notification) > 0) + delay_time = DELAY_INTERVAL; + } + + free(path); +} + +int +daemonize(void) +{ + char *status_line; + int i, count = 0; + + if (get_pid() != -1) + return 1; + + if (!(dpy = XOpenDisplay(NULL))) + return 1; + + if ((status_line = malloc(256)) == NULL) + return 1; + + signal(SIG_DELAY, delay_handler); + signal(SIG_NOTIFY, notify_handler); + signal(SIGINT, sigint_handler); + signal(SIGTERM, sigint_handler); + + /* initialize elements, and bind signal handlers */ + for (i = 0; i < ELEMENT_COUNT; i++) { + if (elements[i].arg.s) + signal(elements[i].arg.s, element_handler); + if (elements[i].handler) + elements[i].handler(&(elements[i].value)); + } + + /* get time delay until next full minute. + * this will initially sync the time loop */ + count = -count_until_time(); + + /* TODO :: count will get out of sync as the loop isn't instant and the + * sleep is constant. should this get fixed using a resync after n minutes? + */ + for (running = 1, is_changed = 1; running; sleep(MIN_INTERVAL), count++) { + + /* check for notifications */ + if (notification[0] != 0) { + snprintf(status_line, 256, "\x0a%s", notification); + set_status(status_line); + notification[0] = 0; + is_changed = 1; + } + + /* 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 < ELEMENT_COUNT; i++) { + if (!elements[i].handler) + continue; + if (elements[i].arg.t && ( + (elements[i].arg.t > 0 && count % elements[i].arg.t == 0) + || (elements[i].arg.t < 0 && count >= 0 && count % elements[i].arg.t == 0) + )) + is_changed |= elements[i].handler(&(elements[i].value)); + } + + if (is_changed) { + status_line[0] = '\0'; + + /* render all elements */ + for (i = 0; i < ELEMENT_COUNT; i++) { + if (elements[i].renderer) + elements[i].renderer( + status_line, + elements[i].format, + elements[i].value); + else + strcpy( + status_line + strlen(status_line), + elements[i].format); + } + + set_status(status_line); + } + + } + + /* free up resources when done */ + free(status_line); + XCloseDisplay(dpy); + + return 0; +} + +int +expand_tilde(const char *dir, char **out) +{ + 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; +} + +int +get_int(const char *s, int *i) +{ + const char *c = s; + + *i = 0; + while (*c) { + if (*c < 48 || *c >= 58) + return 0; + *i = *i * 10 - 48 + *c; + c++; + } + + return 1; +} + +int +check_proc(struct dirent *e) +{ + 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 +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 +notify(const char *msg) +{ + int pid; + FILE *fptr = NULL; + struct stat sb; + char *dir = NULL; + char *path = NULL; + int state = 0; + + expand_tilde(NOTIFICATION_DIR, &dir); + expand_tilde(NOTIFICATION_PATH, &path); + + if (state == 0 && (path == NULL || dir == NULL)) + state = 1; + + if (state == 0 && (pid = get_pid()) == -1) + state = 1; + + if (state == 0 && stat(dir, &sb) == -1) + mkdir(dir, 0700); + + if (state == 0 && (fptr = fopen(path, "w+")) == NULL) + state = 1; + + if (state == 0) { + fwrite(msg, 1, strlen(msg), fptr); + fclose(fptr); + + kill(pid, SIG_NOTIFY); + } + + if (dir != NULL) + free(dir); + if (path != NULL) + free(path); + + return state; +} + +int +get_volume(int *value) +{ + 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) +{ + FILE *fp; + int energy_now, energy_full, voltage_now; + char status; + int old_value = *value; + int 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/BAT0/status", "r"); + if (!fp) + return 0; + fscanf(fp, "%c", &status); + fclose(fp); + + *value = ((float)energy_now * 1000 / (float)voltage_now) * 100 / ((float)energy_full * 1000 / (float)voltage_now); + *value += status != 'D' ? 1000 : 0; + + if (*value != old_value || (*value >= 1000) != old_state) + return 1; + return 0; +} + +int +get_temp(int *value) +{ + 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; +} + +int +get_memory(int *value) +{ + 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) +{ + 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) +{ + char *dir = NULL; + struct dirent *dp; + DIR *fd; + int old_value = *value; + *value = 0; + + if (!expand_tilde(MAIL_DIR, &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) +{ + 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) +{ + time_t rawtime; + struct tm *timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + *value = ((timeinfo->tm_year + 1900) * 10000 + + (timeinfo->tm_mon + 1) * 100 + + timeinfo->tm_mday + ); + + return 1; +} + +int +get_time(int *value) +{ + time_t rawtime; + struct tm *timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + *value = timeinfo->tm_hour * 60 + timeinfo->tm_min; + + return 1; +} + +void +render_threshold_start(char *str, const char *format, int value, Limit l[], int start, int count) +{ + char *tmp = NULL; + int i; + for (i = start; i < (start + count); i++) { + if (value >= l[i].threshold) { + tmp = malloc(16); + snprintf(tmp, 16, format, l[i].symbol, value); + break; + } + } + + if (tmp) { + strcpy(str + strlen(str), tmp); + free(tmp); + } +} + +void +render_threshold(char *str, const char *format, int value, Limit l[], int count) +{ + render_threshold_start(str, format, value, l, 0, count); +} + +void +render_volume(char *str, const char *format, int value) +{ + render_threshold(str, format, value, limit_volume, 2); +} + +void +render_power(char *str, const char *format, int value) +{ + char *nformat = malloc(strlen(format) + 1); + int start = 0; + int count = 4; + + strcpy(nformat, format); + if (value >= 1000) { + value -= 1000; + count = 1; + start = 4; + nformat[0] = '\x08'; + } + + render_threshold_start(str, nformat, value, limit_power, start, count); + + if (nformat) + free(nformat); +} + +void +render_temp(char *str, const char *format, int value) +{ + render_threshold(str, format, value, limit_temp, 2); +} + +void +render_cpu(char *str, const char *format, int value) +{ + render_threshold(str, format, value, limit_cpu, 2); +} + +void +render_mem(char *str, const char *format, int value) +{ + render_threshold(str, format, value, limit_mem, 2); +} + +void +render_mail(char *str, const char *format, int value) +{ + render_threshold(str, format, value, limit_mail, 2); +} + +void +render_updates(char *str, const char *format, int value) +{ + render_threshold(str, format, value, limit_updates, 2); +} + +void +render_date(char *str, const char *format, int value) +{ + char *tmp = malloc(20); + + snprintf(tmp, 20, format + ,value / 10000 + ,(value % 10000) / 100 + ,(value % 10000) % 100); + strcpy(str + strlen(str), tmp); + free(tmp); +} + +void +render_time(char *str, const char *format, int value) +{ + char *tmp = malloc(16); + + snprintf(tmp, 16, format, value / 60, value % 60); + strcpy(str + strlen(str), tmp); + free(tmp); +} + +int +run(const char* cmd) +{ + FILE *fp; + char rval[8]; + int val; + + fp = popen(cmd, "r"); + if (fp == NULL) + return -1; + + if (fgets(rval, sizeof(rval), fp) != NULL) + sscanf(rval, "%d", &val); + + pclose(fp); + + return val; +} + +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 +usage(void) +{ + fprintf(stderr, "usage: %s [-d] [-n TEXT]\n", PNAME); + fprintf(stderr, " -d daemonize\n"); + fprintf(stderr, " -n TEXT send notification\n"); +} + +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 == 3 && strcmp(argv[1], "-n") == 0) + return notify(argv[2]); + + usage(); + return 0; +}