// A X11 Netload graph (xload style)
// Run:./ net[interface]

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <X11/Xutil.h>

typedef unsigned long u64;

static int read_bytes(const char *ifc, u64 * rx, u64 * tx)
{
    FILE	   *f = fopen("/proc/net/dev", "r");
    if (!f)
	return -1;
    char line[256];
    fgets(line, sizeof line, f);
    fgets(line, sizeof line, f);

    while (fgets(line, sizeof line, f)) {
	char	       *c = strchr(line, ':');
	if (!c)
	    continue;
	*c = 0;
	char	       *p = line;
	while (*p == ' ' || *p == '\t')
	    p++;

	if (!strcmp(p, ifc)) {
	    unsigned long long r, t;
	    if (sscanf(c + 1, "%llu %*s %*s %*s %*s %*s %*s %*s %llu", &r, &t) == 2) {
		*rx = r;
		*tx = t;
		fclose(f);
		return 0;
	    }
	}
    }
    fclose(f);
    return -1;
}

static unsigned long now_ms()
{
    struct timeval  tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
}

int main(int argc, char **argv)
{
    const char	   *ifc = (argc > 1) ? argv[1] : "enp2s0";
    int w = 300, h = 80;
    int m = 2;

    Display	   *d = XOpenDisplay(NULL);
    if (!d)
	return 1;
    int s = DefaultScreen(d);
    Window root = RootWindow(d, s);

    XSetWindowAttributes a;
    a.background_pixel = WhitePixel(d, s);
    a.event_mask = ExposureMask | StructureNotifyMask;

    Window win = XCreateWindow(
				    d, root, 0, 0, w, h, 0,
				CopyFromParent, InputOutput, CopyFromParent,
				    CWBackPixel | CWEventMask, &a
    );

    XClassHint ch;
    ch.res_name = "net";
    ch.res_class = "Netload";
    XSetClassHint(d, win, &ch);

    XStoreName(d, win, "net");
    XMapWindow(d, win);

    GC		    gc = XCreateGC(d, win, 0, NULL);
    Pixmap buf = XCreatePixmap(d, win, w, h, DefaultDepth(d, s));

    XSetForeground(d, gc, WhitePixel(d, s));
    XFillRectangle(d, buf, gc, 0, 0, w, h);

    u64 rx0, tx0;
    read_bytes(ifc, &rx0, &tx0);
    unsigned long   t0 = now_ms();
    double maxbps = 1.0;

    for (;;) {
	while (XPending(d)) {
	    XEvent e;
	    XNextEvent(d, &e);

	    if (e.type == ConfigureNotify) {
		w = e.xconfigure.width;
		h = e.xconfigure.height;
		XFreePixmap(d, buf);
		buf = XCreatePixmap(d, win, w, h, DefaultDepth(d, s));

		XSetForeground(d, gc, WhitePixel(d, s));
		XFillRectangle(d, buf, gc, 0, 0, w, h);
	    } else if (e.type == Expose) {
		XCopyArea(d, buf, win, gc, 0, 0, w, h, 0, 0);
	    }
	}

	usleep(50000);
	unsigned long	t1 = now_ms();
	if (t1 - t0 < 500)
	    continue;

	u64 rx1, tx1;
	if (read_bytes(ifc, &rx1, &tx1) != 0)
	    continue;

	double bps = (rx1 - rx0 + tx1 - tx0) / ((t1 - t0) / 1000.0);
	if (bps > maxbps)
	    maxbps = bps;

	int gw = w - 2 * m;
	int gh = h - 2 * m;

	if (gw > 1) {
	    XCopyArea(d, buf, buf, gc,
		      m + 1, m, gw - 1, gh,
		      m, m);
	    XSetForeground(d, gc, WhitePixel(d, s));
	    XFillRectangle(d, buf, gc, m + gw - 1, m, 1, gh);
	}

	int bh = (int)(bps / maxbps * gh);
	XSetForeground(d, gc, BlackPixel(d, s));
	XFillRectangle(d, buf, gc, m + gw - 1, m + gh - bh, 1, bh);

	XDrawRectangle(d, buf, gc, m, m, gw, gh);
	XCopyArea(d, buf, win, gc, 0, 0, w, h, 0, 0);
	rx0 = rx1;
	tx0 = tx1;
	t0 = t1;
    }
}
