dwm-status

Yet another stupid dwm status tool

git clone https://git.8pit.net/dwm-status.git

  1/* See LICENSE for license details. */
  2
  3#include <assert.h>
  4#include <err.h>
  5#include <stdarg.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <time.h>
 10#include <limits.h>
 11#include <unistd.h>
 12
 13#include <sys/types.h>
 14#include <X11/Xlib.h>
 15#include <tinyalsa/asoundlib.h>
 16
 17static size_t alsavol(char*, size_t);
 18static size_t batcap(char*, size_t);
 19static size_t loadavg(char*, size_t);
 20static size_t curtime(char*, size_t);
 21static size_t separator(char*, size_t);
 22
 23#include "config.h"
 24
 25enum {
 26	STATUSSZ = 128,
 27};
 28
 29static char ststr[STATUSSZ];
 30
 31static int
 32xsnprintf(char *restrict s, size_t n, const char *restrict fmt, ...)
 33{
 34	int ret;
 35	va_list ap;
 36
 37	va_start(ap, fmt);
 38	ret = vsnprintf(s, n, fmt, ap);
 39	va_end(ap);
 40
 41	if (ret < 0) {
 42		err(EXIT_FAILURE, "snprintf failed");
 43	} else if ((size_t)ret >= n) {
 44		warnx("snprintf: insufficient buffer size");
 45		ret = n;
 46	}
 47
 48	return ret;
 49}
 50
 51static double
 52readnum(char *bfp, char *fn)
 53{
 54	int ret;
 55	FILE *file;
 56	size_t rclen;
 57	char buf[16], fp[PATH_MAX + 1], *rc;
 58
 59	rc = NULL;
 60	if ((ret = snprintf(fp, sizeof(fp), "%s/%s", bfp, fn)) < 0)
 61		errx(EXIT_FAILURE, "snprintf failed");
 62	else if ((size_t)ret >= sizeof(fp))
 63		errx(EXIT_FAILURE, "buffer 'fp' is too short");
 64
 65	if (!(file = fopen(fp, "r")))
 66		err(EXIT_FAILURE, "couldn't open '%s'", fp);
 67
 68	if (!(rc = fgets(buf, 16, file))) {
 69		fclose(file);
 70		errx(EXIT_FAILURE, "'%s' seems to be empty", fp);
 71	}
 72
 73	rclen = strlen(buf);
 74	if (rc[rclen - 1] == '\n')
 75		rc[rclen - 1] = '\0';
 76
 77	fclose(file);
 78	return atof(rc);
 79}
 80
 81static size_t
 82actlstr(char *buf, size_t n, char *ch, struct mixer *mx) {
 83	size_t ret;
 84	char *status;
 85	struct mixer_ctl *ctl;
 86
 87	if (!(ctl = mixer_get_ctl_by_name(mx, ch))) {
 88		mixer_close(mx);
 89		errx(EXIT_FAILURE, "couldn't find mixer ctl '%s'\n", ch);
 90	}
 91
 92	switch (mixer_ctl_get_type(ctl)) {
 93	case MIXER_CTL_TYPE_INT:
 94		return xsnprintf(buf, n, "%d%%",
 95			mixer_ctl_get_percent(ctl, 0));
 96	case MIXER_CTL_TYPE_BOOL:
 97		status = mixer_ctl_get_value(ctl, 0) ? "On" : "Off";
 98		ret = stpncpy(buf, status, n) - buf;
 99		break;
100	default:
101		mixer_close(mx);
102		errx(EXIT_FAILURE, "unsupported ctl type '%s'\n",
103			mixer_ctl_get_type_string(ctl));
104	};
105
106	return ret;
107}
108
109static size_t
110batcap(char *dest, size_t n)
111{
112	double res, curc, maxc;
113
114	curc = readnum((char*)sysbat, (char*)syscur);
115	maxc = readnum((char*)sysbat, (char*)sysfull);
116
117	res = 100.0 * (curc / maxc);
118	return xsnprintf(dest, n, "%.2f%%", res);
119}
120
121static size_t
122alsavol(char *dest, size_t n)
123{
124	size_t ret;
125	struct mixer *mx;
126
127	if (!(mx = mixer_open(sndcrd)))
128		errx(EXIT_FAILURE, "couldn't open mixer for card %d\n", sndcrd);
129
130	ret = actlstr(dest, n, (char*)swtchname, mx);
131	if (strcmp(dest, "Off"))
132		ret = actlstr(dest, n, (char*)volumname, mx);
133
134	mixer_close(mx);
135	return ret;
136}
137
138static size_t
139loadavg(char* dest, size_t n)
140{
141	double avgs[3];
142
143	if (!getloadavg(avgs, 3))
144		err(EXIT_FAILURE, "getloadavg failed");
145
146	return xsnprintf(dest, n, "%.2f %.2f %.2f",
147		avgs[0], avgs[1], avgs[2]);
148}
149
150static size_t
151curtime(char *dest, size_t n)
152{
153	time_t tim;
154	struct tm *timtm;
155
156	if ((tim = time(NULL)) == (time_t)-1)
157		err(EXIT_FAILURE, "time failed");
158
159	if (!(timtm = localtime(&tim)))
160		err(EXIT_FAILURE, "localtime failed");
161
162	return strftime(dest, n, timefmt, timtm);
163}
164
165static size_t
166separator(char *dest, size_t n)
167{
168	return stpncpy(dest, statsep, n) - dest;
169}
170
171int
172main(void)
173{
174	Display *dpy;
175	Window root;
176	size_t i, x, fns, max;
177
178	if (!(dpy = XOpenDisplay(NULL)))
179		errx(EXIT_FAILURE, "couldn't open display '%s'", XDisplayName(NULL));
180	root = DefaultRootWindow(dpy);
181
182	max = STATUSSZ - 1;
183	fns = sizeof(sfuncs) / sizeof(sfuncs[0]);
184
185	for (;;) {
186		for (i = 0, x = 0; i < fns && x < max; i++) {
187			assert(max >= x);
188			x += (*sfuncs[i])(&(ststr[x]), max - x);
189		}
190
191		assert(x < STATUSSZ);
192		ststr[x] = '\0';
193
194		XStoreName(dpy, root, ststr);
195		XSync(dpy, False);
196		sleep(delay);
197	}
198
199	XCloseDisplay(dpy);
200	return EXIT_SUCCESS;
201}