commit - /dev/null
commit + 8f16cb3ee7a8516f90d4d7109d26d49f0cf9255d
blob - /dev/null
blob + 58c2e41aa282594470482a9727a5ac2f119db777 (mode 644)
--- /dev/null
+++ Makefile
+CFLAGS = -Wall -g -Og
+LIBS = -lutil -lncurses
+
+all: apmtop
+
+run: apmtop
+ ./apmtop
+
+clean:
+ rm -f apmtop *.core
+
+apmtop: apmtop.c
+ ${CC} -o $@ apmtop.c ${CFLAGS} ${LIBS}
blob - /dev/null
blob + 56149b80cc8e9511fe07dc44643972135aa79857 (mode 644)
--- /dev/null
+++ apmtop.c
+#include <sys/types.h>
+#include <machine/apmvar.h>
+#include <sys/signal.h>
+#include <sys/sched.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <sys/sensors.h>
+#include <stdbool.h>
+#include <ncurses.h>
+#include <locale.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <err.h>
+
+#define MAXSENSORS 16
+#define LOGLEN 512
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+static int power_sensors[MAXSENSORS];
+static int cputemp_sensor = -1;
+static int num_power_sensors = 0;
+static int entries = 0;
+
+enum {
+ G_CPUUSAGE,
+ G_CPUSPEED,
+ G_CPUTEMP,
+ G_BATTERY,
+ G_POWER,
+ NGRAPH,
+};
+
+struct graph {
+ WINDOW *win;
+ int log[LOGLEN];
+ bool hide;
+};
+
+struct display {
+ struct graph g[NGRAPH];
+
+ struct cpu_usage *cpus;
+ int fd_apm, ncpu;
+ int ngraph;
+ int width;
+ int delay;
+ bool running;
+};
+
+struct cpu_usage {
+ struct cpustats new;
+ struct cpustats old;
+ struct cpustats diffs;
+};
+
+static void
+find_sensors (void)
+{
+ for (int i = 0; i < MAXSENSORS; ++i) {
+ const int mib[3] = { CTL_HW, HW_SENSORS, i };
+ struct sensordev dev;
+ size_t sdlen = sizeof dev;
+
+ if (sysctl (mib, 3, &dev, &sdlen, NULL, 0) == -1)
+ continue;
+
+ if (dev.maxnumt[SENSOR_WATTS] > 0) {
+ power_sensors[num_power_sensors++] = i;
+ }
+
+ if (dev.maxnumt[SENSOR_TEMP] > 0
+ && memcmp (dev.xname, "cpu", 3) == 0
+ && cputemp_sensor == -1) {
+ cputemp_sensor = i;
+ }
+ }
+}
+
+static int
+num_cpu (void)
+{
+ const int mib[] = { CTL_HW, HW_NCPU };
+ int ncpu = 0;
+ size_t size = sizeof ncpu;
+
+ if (sysctl (mib, 2, &ncpu, &size, NULL, 0) < 0)
+ return 1;
+
+ return ncpu;
+}
+
+static int64_t
+percentages (struct cpustats *out, struct cpu_usage *cpu)
+{
+ int64_t total_change = 0;
+
+ for (int i = 0; i < 6; ++i) {
+ int64_t change = cpu->new.cs_time[i] - cpu->old.cs_time[i];
+ if (change < 0)
+ change = INT64_MAX - cpu->old.cs_time[i] - cpu->new.cs_time[i];
+
+ cpu->diffs.cs_time[i] = change;
+ total_change += change;
+ cpu->old.cs_time[i] = cpu->new.cs_time[i];
+ }
+
+ if (total_change == 0)
+ total_change = 1;
+
+ for (int i = 0; i < 6; ++i) {
+ out->cs_time[i] = (cpu->diffs.cs_time[i] * 1000 + total_change / 2) / total_change;
+ }
+
+ return total_change;
+}
+
+static int
+cpuspeed (void)
+{
+ const int mib[2] = { CTL_HW, HW_CPUSPEED };
+ int speed;
+ size_t len = sizeof speed;
+
+ if (sysctl (mib, 2, &speed, &len, NULL, 0) == -1)
+ return -1;
+
+ return speed;
+}
+
+static int
+cputemp (void)
+{
+ const int mib[5] = { CTL_HW, HW_SENSORS, cputemp_sensor, SENSOR_TEMP, 0 };
+ struct sensor sensor;
+ size_t len = sizeof sensor;
+ int ret;
+
+ ret = sysctl (mib, 5, &sensor, &len, NULL, 0);
+
+ return (ret == -1 || sensor.status & SENSOR_FINVALID) ? -1 : sensor.value / 10000000;
+}
+
+static int
+cpu (int ncpu, struct cpu_usage *cpus)
+{
+ uint64_t sum = 0;
+
+ for (int i = 0; i < ncpu; ++i) {
+ const int mib[] = { CTL_KERN, KERN_CPUSTATS, i };
+ struct cpustats tmp;
+ size_t size = sizeof tmp;
+
+ if (sysctl (mib, 3, &cpus[i].new, &size, NULL, 0) < 0)
+ continue;
+
+ percentages (&tmp, &cpus[i]);
+ sum += 1000 - tmp.cs_time[5];
+ }
+
+ return sum / ncpu / 10;
+}
+
+static int
+bat (int fd)
+{
+ struct apm_power_info info;
+
+ if (fd < 0 || ioctl (fd, APM_IOC_GETPOWER, &info) != 0)
+ return 0;
+
+ return info.battery_life;
+}
+
+static int
+power (void)
+{
+ int64_t total = 0;
+
+ for (int i = 0; i < num_power_sensors; ++i) {
+ const int mib[5] = { CTL_HW, HW_SENSORS, power_sensors[i], SENSOR_WATTS, 0 };
+ struct sensor sensor;
+ size_t slen = sizeof sensor;
+
+ if (sysctl (mib, 5, &sensor, &slen, NULL, 0) == -1)
+ continue;
+
+ if (sensor.flags & SENSOR_FINVALID)
+ continue;
+
+ total += sensor.value;
+ }
+
+ return total / 1000;
+}
+
+static void
+create_win (struct display *dpy)
+{
+ int j = 0, w, h, wh;
+
+ dpy->ngraph = 0;
+ for (int i = 0; i < NGRAPH; ++i) {
+ if (!dpy->g[i].hide)
+ ++dpy->ngraph;
+ }
+
+ getmaxyx (stdscr, h, w);
+ dpy->width = MIN (w - 2, LOGLEN);
+ --h;
+ wh = h / dpy->ngraph;
+
+ if (entries > dpy->width) {
+ const int diff = entries - dpy->width;
+ for (int i = 0; i < NGRAPH; ++i) {
+ struct graph *g = &dpy->g[i];
+ memmove (g->log, g->log + diff, dpy->width * sizeof (g->log[0]));
+ }
+ entries = dpy->width;
+ }
+
+ for (int i = 0; i < NGRAPH; ++i) {
+ struct graph *g = &dpy->g[i];
+ if (g->win != NULL)
+ delwin (g->win);
+ g->win = g->hide ? NULL : newwin (wh, w, 1 + wh * j++, 0);
+ }
+}
+
+static void
+draw_graph (struct graph *g, const char *fmt, int min, int max)
+{
+ int w, h;
+
+ if (g->hide || g->win == NULL)
+ return;
+
+ getmaxyx (g->win, h, w);
+ w -= 2;
+ h -= 3;
+
+ wclear (g->win);
+
+ for (int i = 0; i < entries; ++i) {
+ int v = g->log[i];
+ if (v < min)
+ min = v;
+ if (v > max)
+ max = v;
+ }
+
+ if (min < max) {
+ for (int i = 0; i < entries; ++i) {
+ int v = g->log[i];
+ int y = (int)((float)(v - min) / (max - min) * h);
+ mvwaddch (g->win, h - y + 1, i + 1, 'X');
+ }
+ }
+
+ box (g->win, 0, 0);
+
+ mvwprintw (g->win, 0, 3, fmt, g->log[entries - 1]);
+ wprintw (g->win, " (min: %d/max: %d)", min, max);
+
+}
+
+static void
+draw (struct display *dpy)
+{
+ clear ();
+
+ draw_graph (&dpy->g[G_CPUUSAGE], "CPU: %d%%", 0, 100);
+ draw_graph (&dpy->g[G_CPUTEMP], "CPU.: %d C", 0, 100);
+ draw_graph (&dpy->g[G_CPUSPEED], "CPU: %d MHz", INT_MAX, INT_MIN);
+ draw_graph (&dpy->g[G_BATTERY], "BAT: %d%%", 0, 100);
+ draw_graph (&dpy->g[G_POWER], "PWR: %dmW", INT_MAX, INT_MIN);
+
+ mvprintw (0, 0, "Panels: ");
+ for (int i = 0; i < NGRAPH; ++i) {
+ addch (dpy->g[i].hide ? ' ' : ('1' + i));
+ }
+
+ printw (" %c delay=%d", dpy->running ? 'R' : 'S', dpy->delay);
+
+ refresh ();
+ for (int i = 0; i < NGRAPH; ++i)
+ wnoutrefresh (dpy->g[i].win);
+ doupdate ();
+ move (0, 0);
+
+}
+
+static void
+update_graph (int w, struct graph *g, int value)
+{
+ if (entries == w) {
+ memmove (g->log, g->log + 1, (LOGLEN - 1) * sizeof (g->log[0]));
+ g->log[entries - 1] = value;
+ } else {
+ g->log[entries] = value;
+ }
+ fprintf (stderr, "entries=%d, w=%d, value=%d\n", entries, w, value);
+}
+
+static void
+update (struct display *dpy)
+{
+ int w = dpy->width;
+
+ update_graph (w, &dpy->g[G_CPUUSAGE], cpu (dpy->ncpu, dpy->cpus));
+ update_graph (w, &dpy->g[G_CPUTEMP], cputemp ());
+ update_graph (w, &dpy->g[G_CPUSPEED], cpuspeed ());
+ update_graph (w, &dpy->g[G_BATTERY], bat (dpy->fd_apm));
+ update_graph (w, &dpy->g[G_POWER], power ());
+
+ entries++;
+ if (entries > w)
+ entries = w;
+}
+
+int
+main (int argc, char *argv[])
+{
+ struct display dpy;
+
+ memset (&dpy, 0, sizeof (dpy));
+ dpy.ncpu = num_cpu ();
+ dpy.cpus = calloc (dpy.ncpu, sizeof (struct cpu_usage));
+ dpy.fd_apm = open ("/dev/apm", O_RDONLY);
+ dpy.delay = 10;
+ dpy.running = true;
+ find_sensors ();
+
+ setlocale (LC_ALL, "");
+ initscr ();
+ cbreak ();
+ noecho ();
+ intrflush (stdscr, FALSE);
+ keypad (stdscr, TRUE);
+ halfdelay (dpy.delay);
+
+ entries = 0;
+ create_win (&dpy);
+ update (&dpy);
+ draw (&dpy);
+
+ while (1) {
+ struct graph *g;
+ int ch;
+
+ ch = getch ();
+
+ switch (ch) {
+ case 'q':
+ goto done;
+ case KEY_RESIZE:
+ create_win (&dpy);
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ g = &dpy.g[ch - '1'];
+ if (dpy.ngraph == 1 && !g->hide)
+ break;
+ g->hide = !g->hide;
+ create_win (&dpy);
+ break;
+ case '+':
+ ++dpy.delay;
+ halfdelay (dpy.delay);
+ break;
+ case '-':
+ if (dpy.delay > 1)
+ --dpy.delay;
+ halfdelay (dpy.delay);
+ break;
+ case ' ':
+ dpy.running = !dpy.running;
+ break;
+ case ERR:
+ if (dpy.running)
+ update (&dpy);
+ break;
+ }
+
+ draw (&dpy);
+ }
+
+done:
+ endwin ();
+ return 0;
+}