commit: b83fee8c437e333a3dafe030e60b35d86ae8bfb9
parent:
author: Chris Noxz <chris@noxz.tech>
date: Thu, 3 Jan 2019 14:06:08 +0100
Initial commit
7 files changed, 1020 insertions(+)
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;
+}