Commit Diff


commit - /dev/null
commit + 8f16cb3ee7a8516f90d4d7109d26d49f0cf9255d
blob - /dev/null
blob + 58c2e41aa282594470482a9727a5ac2f119db777 (mode 644)
--- /dev/null
+++ Makefile
@@ -0,0 +1,13 @@
+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
@@ -0,0 +1,399 @@
+#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;
+}