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}