Blob


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