1 8f16cb3e 2024-01-04 benni #include <sys/types.h>
2 8f16cb3e 2024-01-04 benni #include <machine/apmvar.h>
3 8f16cb3e 2024-01-04 benni #include <sys/signal.h>
4 8f16cb3e 2024-01-04 benni #include <sys/sched.h>
5 8f16cb3e 2024-01-04 benni #include <sys/sysctl.h>
6 8f16cb3e 2024-01-04 benni #include <sys/ioctl.h>
7 8f16cb3e 2024-01-04 benni #include <sys/sensors.h>
8 8f16cb3e 2024-01-04 benni #include <stdbool.h>
9 8f16cb3e 2024-01-04 benni #include <ncurses.h>
10 8f16cb3e 2024-01-04 benni #include <locale.h>
11 8f16cb3e 2024-01-04 benni #include <limits.h>
12 8f16cb3e 2024-01-04 benni #include <unistd.h>
13 8f16cb3e 2024-01-04 benni #include <string.h>
14 8f16cb3e 2024-01-04 benni #include <stdint.h>
15 8f16cb3e 2024-01-04 benni #include <stdlib.h>
16 8f16cb3e 2024-01-04 benni #include <stdio.h>
17 8f16cb3e 2024-01-04 benni #include <fcntl.h>
18 8f16cb3e 2024-01-04 benni #include <time.h>
19 8f16cb3e 2024-01-04 benni #include <err.h>
21 8f16cb3e 2024-01-04 benni #define MAXSENSORS 16
22 8f16cb3e 2024-01-04 benni #define LOGLEN 512
23 8f16cb3e 2024-01-04 benni #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 8f16cb3e 2024-01-04 benni static int power_sensors[MAXSENSORS];
26 8f16cb3e 2024-01-04 benni static int cputemp_sensor = -1;
27 8f16cb3e 2024-01-04 benni static int num_power_sensors = 0;
28 8f16cb3e 2024-01-04 benni static int entries = 0;
31 8f16cb3e 2024-01-04 benni G_CPUUSAGE,
32 8f16cb3e 2024-01-04 benni G_CPUSPEED,
39 8f16cb3e 2024-01-04 benni struct graph {
40 8f16cb3e 2024-01-04 benni WINDOW *win;
41 8f16cb3e 2024-01-04 benni int log[LOGLEN];
45 8f16cb3e 2024-01-04 benni struct display {
46 8f16cb3e 2024-01-04 benni struct graph g[NGRAPH];
48 8f16cb3e 2024-01-04 benni struct cpu_usage *cpus;
49 8f16cb3e 2024-01-04 benni int fd_apm, ncpu;
50 8f16cb3e 2024-01-04 benni int ngraph;
53 8f16cb3e 2024-01-04 benni bool running;
56 8f16cb3e 2024-01-04 benni struct cpu_usage {
57 8f16cb3e 2024-01-04 benni struct cpustats new;
58 8f16cb3e 2024-01-04 benni struct cpustats old;
59 8f16cb3e 2024-01-04 benni struct cpustats diffs;
62 8f16cb3e 2024-01-04 benni static void
63 8f16cb3e 2024-01-04 benni find_sensors (void)
65 8f16cb3e 2024-01-04 benni for (int i = 0; i < MAXSENSORS; ++i) {
66 8f16cb3e 2024-01-04 benni const int mib[3] = { CTL_HW, HW_SENSORS, i };
67 8f16cb3e 2024-01-04 benni struct sensordev dev;
68 8f16cb3e 2024-01-04 benni size_t sdlen = sizeof dev;
70 8f16cb3e 2024-01-04 benni if (sysctl (mib, 3, &dev, &sdlen, NULL, 0) == -1)
73 8f16cb3e 2024-01-04 benni if (dev.maxnumt[SENSOR_WATTS] > 0) {
74 8f16cb3e 2024-01-04 benni power_sensors[num_power_sensors++] = i;
77 8f16cb3e 2024-01-04 benni if (dev.maxnumt[SENSOR_TEMP] > 0
78 8f16cb3e 2024-01-04 benni && memcmp (dev.xname, "cpu", 3) == 0
79 8f16cb3e 2024-01-04 benni && cputemp_sensor == -1) {
80 8f16cb3e 2024-01-04 benni cputemp_sensor = i;
86 8f16cb3e 2024-01-04 benni num_cpu (void)
88 8f16cb3e 2024-01-04 benni const int mib[] = { CTL_HW, HW_NCPU };
89 8f16cb3e 2024-01-04 benni int ncpu = 0;
90 8f16cb3e 2024-01-04 benni size_t size = sizeof ncpu;
92 8f16cb3e 2024-01-04 benni if (sysctl (mib, 2, &ncpu, &size, NULL, 0) < 0)
95 8f16cb3e 2024-01-04 benni return ncpu;
98 8f16cb3e 2024-01-04 benni static int64_t
99 8f16cb3e 2024-01-04 benni percentages (struct cpustats *out, struct cpu_usage *cpu)
101 8f16cb3e 2024-01-04 benni int64_t total_change = 0;
103 8f16cb3e 2024-01-04 benni for (int i = 0; i < 6; ++i) {
104 8f16cb3e 2024-01-04 benni int64_t change = cpu->new.cs_time[i] - cpu->old.cs_time[i];
105 8f16cb3e 2024-01-04 benni if (change < 0)
106 8f16cb3e 2024-01-04 benni change = INT64_MAX - cpu->old.cs_time[i] - cpu->new.cs_time[i];
108 8f16cb3e 2024-01-04 benni cpu->diffs.cs_time[i] = change;
109 8f16cb3e 2024-01-04 benni total_change += change;
110 8f16cb3e 2024-01-04 benni cpu->old.cs_time[i] = cpu->new.cs_time[i];
113 8f16cb3e 2024-01-04 benni if (total_change == 0)
114 8f16cb3e 2024-01-04 benni total_change = 1;
116 8f16cb3e 2024-01-04 benni for (int i = 0; i < 6; ++i) {
117 8f16cb3e 2024-01-04 benni out->cs_time[i] = (cpu->diffs.cs_time[i] * 1000 + total_change / 2) / total_change;
120 8f16cb3e 2024-01-04 benni return total_change;
123 8f16cb3e 2024-01-04 benni static int
124 8f16cb3e 2024-01-04 benni cpuspeed (void)
126 8f16cb3e 2024-01-04 benni const int mib[2] = { CTL_HW, HW_CPUSPEED };
127 8f16cb3e 2024-01-04 benni int speed;
128 8f16cb3e 2024-01-04 benni size_t len = sizeof speed;
130 8f16cb3e 2024-01-04 benni if (sysctl (mib, 2, &speed, &len, NULL, 0) == -1)
131 8f16cb3e 2024-01-04 benni return -1;
133 8f16cb3e 2024-01-04 benni return speed;
136 8f16cb3e 2024-01-04 benni static int
137 8f16cb3e 2024-01-04 benni cputemp (void)
139 8f16cb3e 2024-01-04 benni const int mib[5] = { CTL_HW, HW_SENSORS, cputemp_sensor, SENSOR_TEMP, 0 };
140 8f16cb3e 2024-01-04 benni struct sensor sensor;
141 8f16cb3e 2024-01-04 benni size_t len = sizeof sensor;
144 8f16cb3e 2024-01-04 benni ret = sysctl (mib, 5, &sensor, &len, NULL, 0);
146 8f16cb3e 2024-01-04 benni return (ret == -1 || sensor.status & SENSOR_FINVALID) ? -1 : sensor.value / 10000000;
149 8f16cb3e 2024-01-04 benni static int
150 8f16cb3e 2024-01-04 benni cpu (int ncpu, struct cpu_usage *cpus)
152 8f16cb3e 2024-01-04 benni uint64_t sum = 0;
154 8f16cb3e 2024-01-04 benni for (int i = 0; i < ncpu; ++i) {
155 8f16cb3e 2024-01-04 benni const int mib[] = { CTL_KERN, KERN_CPUSTATS, i };
156 8f16cb3e 2024-01-04 benni struct cpustats tmp;
157 8f16cb3e 2024-01-04 benni size_t size = sizeof tmp;
159 8f16cb3e 2024-01-04 benni if (sysctl (mib, 3, &cpus[i].new, &size, NULL, 0) < 0)
162 8f16cb3e 2024-01-04 benni percentages (&tmp, &cpus[i]);
163 8f16cb3e 2024-01-04 benni sum += 1000 - tmp.cs_time[5];
166 8f16cb3e 2024-01-04 benni return sum / ncpu / 10;
169 8f16cb3e 2024-01-04 benni static int
170 8f16cb3e 2024-01-04 benni bat (int fd)
172 8f16cb3e 2024-01-04 benni struct apm_power_info info;
174 8f16cb3e 2024-01-04 benni if (fd < 0 || ioctl (fd, APM_IOC_GETPOWER, &info) != 0)
177 8f16cb3e 2024-01-04 benni return info.battery_life;
180 8f16cb3e 2024-01-04 benni static int
181 8f16cb3e 2024-01-04 benni power (void)
183 8f16cb3e 2024-01-04 benni int64_t total = 0;
185 8f16cb3e 2024-01-04 benni for (int i = 0; i < num_power_sensors; ++i) {
186 8f16cb3e 2024-01-04 benni const int mib[5] = { CTL_HW, HW_SENSORS, power_sensors[i], SENSOR_WATTS, 0 };
187 8f16cb3e 2024-01-04 benni struct sensor sensor;
188 8f16cb3e 2024-01-04 benni size_t slen = sizeof sensor;
190 8f16cb3e 2024-01-04 benni if (sysctl (mib, 5, &sensor, &slen, NULL, 0) == -1)
193 8f16cb3e 2024-01-04 benni if (sensor.flags & SENSOR_FINVALID)
196 8f16cb3e 2024-01-04 benni total += sensor.value;
199 8f16cb3e 2024-01-04 benni return total / 1000;
202 8f16cb3e 2024-01-04 benni static void
203 8f16cb3e 2024-01-04 benni create_win (struct display *dpy)
205 8f16cb3e 2024-01-04 benni int j = 0, w, h, wh;
207 8f16cb3e 2024-01-04 benni dpy->ngraph = 0;
208 8f16cb3e 2024-01-04 benni for (int i = 0; i < NGRAPH; ++i) {
209 8f16cb3e 2024-01-04 benni if (!dpy->g[i].hide)
210 8f16cb3e 2024-01-04 benni ++dpy->ngraph;
213 8f16cb3e 2024-01-04 benni getmaxyx (stdscr, h, w);
214 8f16cb3e 2024-01-04 benni dpy->width = MIN (w - 2, LOGLEN);
216 8f16cb3e 2024-01-04 benni wh = h / dpy->ngraph;
218 8f16cb3e 2024-01-04 benni if (entries > dpy->width) {
219 8f16cb3e 2024-01-04 benni const int diff = entries - dpy->width;
220 8f16cb3e 2024-01-04 benni for (int i = 0; i < NGRAPH; ++i) {
221 8f16cb3e 2024-01-04 benni struct graph *g = &dpy->g[i];
222 8f16cb3e 2024-01-04 benni memmove (g->log, g->log + diff, dpy->width * sizeof (g->log[0]));
224 8f16cb3e 2024-01-04 benni entries = dpy->width;
227 8f16cb3e 2024-01-04 benni for (int i = 0; i < NGRAPH; ++i) {
228 8f16cb3e 2024-01-04 benni struct graph *g = &dpy->g[i];
229 8f16cb3e 2024-01-04 benni if (g->win != NULL)
230 8f16cb3e 2024-01-04 benni delwin (g->win);
231 8f16cb3e 2024-01-04 benni g->win = g->hide ? NULL : newwin (wh, w, 1 + wh * j++, 0);
235 8f16cb3e 2024-01-04 benni static void
236 8f16cb3e 2024-01-04 benni draw_graph (struct graph *g, const char *fmt, int min, int max)
240 8f16cb3e 2024-01-04 benni if (g->hide || g->win == NULL)
243 8f16cb3e 2024-01-04 benni getmaxyx (g->win, h, w);
247 8f16cb3e 2024-01-04 benni wclear (g->win);
249 8f16cb3e 2024-01-04 benni for (int i = 0; i < entries; ++i) {
250 8f16cb3e 2024-01-04 benni int v = g->log[i];
251 8f16cb3e 2024-01-04 benni if (v < min)
253 8f16cb3e 2024-01-04 benni if (v > max)
257 8f16cb3e 2024-01-04 benni if (min < max) {
258 8f16cb3e 2024-01-04 benni for (int i = 0; i < entries; ++i) {
259 8f16cb3e 2024-01-04 benni int v = g->log[i];
260 8f16cb3e 2024-01-04 benni int y = (int)((float)(v - min) / (max - min) * h);
261 8f16cb3e 2024-01-04 benni mvwaddch (g->win, h - y + 1, i + 1, 'X');
265 8f16cb3e 2024-01-04 benni box (g->win, 0, 0);
267 8f16cb3e 2024-01-04 benni mvwprintw (g->win, 0, 3, fmt, g->log[entries - 1]);
268 8f16cb3e 2024-01-04 benni wprintw (g->win, " (min: %d/max: %d)", min, max);
272 8f16cb3e 2024-01-04 benni static void
273 8f16cb3e 2024-01-04 benni draw (struct display *dpy)
277 8f16cb3e 2024-01-04 benni draw_graph (&dpy->g[G_CPUUSAGE], "CPU: %d%%", 0, 100);
278 8f16cb3e 2024-01-04 benni draw_graph (&dpy->g[G_CPUTEMP], "CPU.: %d C", 0, 100);
279 8f16cb3e 2024-01-04 benni draw_graph (&dpy->g[G_CPUSPEED], "CPU: %d MHz", INT_MAX, INT_MIN);
280 8f16cb3e 2024-01-04 benni draw_graph (&dpy->g[G_BATTERY], "BAT: %d%%", 0, 100);
281 8f16cb3e 2024-01-04 benni draw_graph (&dpy->g[G_POWER], "PWR: %dmW", INT_MAX, INT_MIN);
283 8f16cb3e 2024-01-04 benni mvprintw (0, 0, "Panels: ");
284 8f16cb3e 2024-01-04 benni for (int i = 0; i < NGRAPH; ++i) {
285 8f16cb3e 2024-01-04 benni addch (dpy->g[i].hide ? ' ' : ('1' + i));
288 8f16cb3e 2024-01-04 benni printw (" %c delay=%d", dpy->running ? 'R' : 'S', dpy->delay);
290 8f16cb3e 2024-01-04 benni refresh ();
291 8f16cb3e 2024-01-04 benni for (int i = 0; i < NGRAPH; ++i)
292 8f16cb3e 2024-01-04 benni wnoutrefresh (dpy->g[i].win);
293 8f16cb3e 2024-01-04 benni doupdate ();
294 8f16cb3e 2024-01-04 benni move (0, 0);
298 8f16cb3e 2024-01-04 benni static void
299 8f16cb3e 2024-01-04 benni update_graph (int w, struct graph *g, int value)
301 8f16cb3e 2024-01-04 benni if (entries == w) {
302 8f16cb3e 2024-01-04 benni memmove (g->log, g->log + 1, (LOGLEN - 1) * sizeof (g->log[0]));
303 8f16cb3e 2024-01-04 benni g->log[entries - 1] = value;
305 8f16cb3e 2024-01-04 benni g->log[entries] = value;
307 8f16cb3e 2024-01-04 benni fprintf (stderr, "entries=%d, w=%d, value=%d\n", entries, w, value);
310 8f16cb3e 2024-01-04 benni static void
311 8f16cb3e 2024-01-04 benni update (struct display *dpy)
313 8f16cb3e 2024-01-04 benni int w = dpy->width;
315 8f16cb3e 2024-01-04 benni update_graph (w, &dpy->g[G_CPUUSAGE], cpu (dpy->ncpu, dpy->cpus));
316 8f16cb3e 2024-01-04 benni update_graph (w, &dpy->g[G_CPUTEMP], cputemp ());
317 8f16cb3e 2024-01-04 benni update_graph (w, &dpy->g[G_CPUSPEED], cpuspeed ());
318 8f16cb3e 2024-01-04 benni update_graph (w, &dpy->g[G_BATTERY], bat (dpy->fd_apm));
319 8f16cb3e 2024-01-04 benni update_graph (w, &dpy->g[G_POWER], power ());
321 8f16cb3e 2024-01-04 benni entries++;
322 8f16cb3e 2024-01-04 benni if (entries > w)
323 8f16cb3e 2024-01-04 benni entries = w;
327 8f16cb3e 2024-01-04 benni main (int argc, char *argv[])
329 8f16cb3e 2024-01-04 benni struct display dpy;
331 8f16cb3e 2024-01-04 benni memset (&dpy, 0, sizeof (dpy));
332 8f16cb3e 2024-01-04 benni dpy.ncpu = num_cpu ();
333 8f16cb3e 2024-01-04 benni dpy.cpus = calloc (dpy.ncpu, sizeof (struct cpu_usage));
334 8f16cb3e 2024-01-04 benni dpy.fd_apm = open ("/dev/apm", O_RDONLY);
335 8f16cb3e 2024-01-04 benni dpy.delay = 10;
336 8f16cb3e 2024-01-04 benni dpy.running = true;
337 8f16cb3e 2024-01-04 benni find_sensors ();
339 8f16cb3e 2024-01-04 benni setlocale (LC_ALL, "");
340 8f16cb3e 2024-01-04 benni initscr ();
341 8f16cb3e 2024-01-04 benni cbreak ();
342 8f16cb3e 2024-01-04 benni noecho ();
343 8f16cb3e 2024-01-04 benni intrflush (stdscr, FALSE);
344 8f16cb3e 2024-01-04 benni keypad (stdscr, TRUE);
345 8f16cb3e 2024-01-04 benni halfdelay (dpy.delay);
347 8f16cb3e 2024-01-04 benni entries = 0;
348 8f16cb3e 2024-01-04 benni create_win (&dpy);
349 8f16cb3e 2024-01-04 benni update (&dpy);
350 8f16cb3e 2024-01-04 benni draw (&dpy);
352 8f16cb3e 2024-01-04 benni while (1) {
353 8f16cb3e 2024-01-04 benni struct graph *g;
356 8f16cb3e 2024-01-04 benni ch = getch ();
358 8f16cb3e 2024-01-04 benni switch (ch) {
360 8f16cb3e 2024-01-04 benni goto done;
361 8f16cb3e 2024-01-04 benni case KEY_RESIZE:
362 8f16cb3e 2024-01-04 benni create_win (&dpy);
369 8f16cb3e 2024-01-04 benni g = &dpy.g[ch - '1'];
370 8f16cb3e 2024-01-04 benni if (dpy.ngraph == 1 && !g->hide)
372 8f16cb3e 2024-01-04 benni g->hide = !g->hide;
373 8f16cb3e 2024-01-04 benni create_win (&dpy);
376 8f16cb3e 2024-01-04 benni ++dpy.delay;
377 8f16cb3e 2024-01-04 benni halfdelay (dpy.delay);
380 8f16cb3e 2024-01-04 benni if (dpy.delay > 1)
381 8f16cb3e 2024-01-04 benni --dpy.delay;
382 8f16cb3e 2024-01-04 benni halfdelay (dpy.delay);
385 8f16cb3e 2024-01-04 benni dpy.running = !dpy.running;
388 8f16cb3e 2024-01-04 benni if (dpy.running)
389 8f16cb3e 2024-01-04 benni update (&dpy);
393 8f16cb3e 2024-01-04 benni draw (&dpy);
397 8f16cb3e 2024-01-04 benni endwin ();