summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0xhenrique <[email protected]>2026-05-04 22:52:00 +0100
committer0xhenrique <[email protected]>2026-05-04 22:52:00 +0100
commit0c286f5b98c5845f2338082115cb48fd90ca01d5 (patch)
treeab7756dbe56233659f237657ce57e817a3a6e60f
parent20cd046137f465acaf9a5dafeebcd8a6d0c92cb8 (diff)
add filesHEADmaster
-rw-r--r--Makefile21
-rw-r--r--back.xpm118
-rwxr-xr-xwmgpumonbin0 -> 27320 bytes
-rw-r--r--wmgpumon.c337
4 files changed, 476 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a0441c5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+PROG = wmgpumon
+CC = gcc
+CFLAGS = -Wall -O2 $(shell pkg-config --cflags xft)
+LIBS = $(shell pkg-config --libs xft) -lX11 -lXext
+
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+
+$(PROG): wmgpumon.c
+ $(CC) $(CFLAGS) wmgpumon.c -o $(PROG) $(LIBS)
+
+install: $(PROG)
+ install -m 755 $(PROG) $(BINDIR)
+
+uninstall:
+ rm -f $(BINDIR)/$(PROG)
+
+clean:
+ rm -f $(PROG)
+
+.PHONY: install uninstall clean
diff --git a/back.xpm b/back.xpm
new file mode 100644
index 0000000..f8f02b9
--- /dev/null
+++ b/back.xpm
@@ -0,0 +1,118 @@
+/* XPM */
+static char *back_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 64 64 47 1",
+/* colors */
+". c #010103",
+"# c #818183",
+"a c #414143",
+"b c #c1c1c3",
+"c c #a1a1a3",
+"d c #616163",
+"e c #919193",
+"f c #515153",
+"g c #b1b1b3",
+"h c #717173",
+"i c #29292b",
+"j c #fdfdfb",
+"k c #cdcdcb",
+"l c #353533",
+"m c #89898b",
+"n c #49494b",
+"o c #a9a9ab",
+"p c #69696b",
+"q c #99999b",
+"r c #59595b",
+"s c #b9b9bb",
+"t c #79797b",
+"u c #bdbdbb",
+"v c #c9c9cb",
+"w c #3d3d3b",
+"x c #313133",
+"y c #7d7d7b",
+"z c #d1d1d3",
+"A c #39393b",
+"B c #858583",
+"C c #454543",
+"D c #c5c5c3",
+"E c #a5a5a3",
+"F c #656563",
+"G c #959593",
+"H c #555553",
+"I c #b5b5b3",
+"J c #757573",
+"K c #8d8d8b",
+"L c #4d4d4b",
+"M c #adadab",
+"N c #6d6d6b",
+"O c #9d9d9b",
+"P c #5d5d5b",
+"Q c #009f00",
+"R c #00a600",
+"S c #00b600",
+/* pixels */
+"qqqqqqqqqqqqqqqOOOOOOccccccEEEEEEEooooooMMMMMMggggggIIIIIIIIsssN",
+"OOOOOOOOOOOOOOOqOOOccOOcccEcEEEEEoEEoooMoMMgggMgggIIgIIIssubbDGC",
+"qqxxxxxxxxxxxlAAAwwwaaaCnnLLLffffHrHrPPddddFpppNNNNhhJJJttyyy#AC",
+"qqxxxlllllllAAAAwwaaaCCnnnLLfffHHrHPPPdddFpppNNNhhJJhJtttyy#BBwC",
+"OqllxlllllAAwwwaaaCCCnnnLLfffHrrrPrPdddFpFpppNNhhhhtJttyy##BBBaC",
+"OOxll.....................................................iBBman",
+"OOxll.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijmmman",
+"Oqxll.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijmmKCC",
+"OOxxl.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijKmKCn",
+"OOxll.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijKKKnn",
+"OOxxl.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijKeenn",
+"OclAA.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijeeeCL",
+"OOxAA.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijeeGnL",
+"OOAAw.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijeGGnn",
+"ccAAw.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijeGGnL",
+"ccAww.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijqGqLL",
+"ccAwa.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijGGGLL",
+"EEwwa.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijqqOnL",
+"Ecwaa.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijGOOLf",
+"EEaaC.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijOqOLL",
+"EEwaC.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijOOcff",
+"ooaCC.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijOccLf",
+"EoaCn.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijOcEff",
+"ooCnn.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijEccff",
+"ooCnL.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijcEEfH",
+"oMnLL.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijEEEHf",
+"MMnLL.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijEEEHH",
+"MMLLH.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijEoMfH",
+"MMLLf.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijooMHH",
+"ggffH.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijMMMfH",
+"MgffH.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijoMgHH",
+"gMfHr.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijgMgrr",
+"ggHHr.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijgggHr",
+"ggHHr.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijgggrr",
+"IgHrP.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijIIIHr",
+"gIrrP.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijIIIrr",
+"IIrPd.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijsIsrr",
+"IIPPd.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijIssrP",
+"IIPdd.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijusurP",
+"ssddF.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijsuuPP",
+"ssPdF.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijuuurP",
+"ssFFp.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijuubPP",
+"usFFp.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijbDDPd",
+"suFpp.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijbDDdP",
+"uuFpN.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijbDvdd",
+"uupNN.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijvvvdd",
+"bbppN.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijDvvdd",
+"uuNhh.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijvkzFF",
+"buNNh.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijkkkdd",
+"bDNhh.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijkzzdF",
+"bbhhJ.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijzkzdd",
+"bbNJJ.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijkzkdF",
+"DDhJJ.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijkzzdd",
+"bDJJt.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijzkkdF",
+"bDJJt.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijzzzdd",
+"DDJtt.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijkkkdF",
+"DDtyy.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijzzzdd",
+"Dvty#.QRSiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijzkkdF",
+"vvy##ijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjzzzdd",
+"vvy#BBmmmmmKeeGGGGGGGqqOOccEoMMMgggMggIIIsubbbDDvkkkvvvvkkkkkkdF",
+"vk#BBBmmKKeeGGGGGGOOOcEccEEEMMMMgIIIssuuuubDvvkkkzkzzzzzzzzzzzdd",
+"kk##mmmKKKKeeGGGqOqOOccEEEoMoMMgggIIsssuuDDDDvvkzkzkkkkkkkkkkkdF",
+"vGxxllllAAAAAAwAwawaaaaCCCCnnnnLLLfLfffHfHHrrPPPPPPPPddddddddddd",
+"GxlxlllllAAAAwwwwwaaaCCCCCnCnnLnLLLLffffHHHrrrPrPPPddPPPPddddddF"
+};
diff --git a/wmgpumon b/wmgpumon
new file mode 100755
index 0000000..757f713
--- /dev/null
+++ b/wmgpumon
Binary files differ
diff --git a/wmgpumon.c b/wmgpumon.c
new file mode 100644
index 0000000..8af1ce8
--- /dev/null
+++ b/wmgpumon.c
@@ -0,0 +1,337 @@
+/* wmgpumon.c — NVIDIA GPU monitor dockapp for Window Maker
+ *
+ * Usage: wmgpumon [-display <disp>] [-interval <sec>]
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/select.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/Xft/Xft.h>
+
+/* ── content area ────────────────────────────────────────── */
+#define WIN_SIZE 64
+#define CONTENT_X 4
+#define CONTENT_Y 6
+#define CONTENT_W 56
+#define CONTENT_H 52
+#define N_ROWS 4
+#define ROW_H (CONTENT_H / N_ROWS) /* 13 px */
+
+/* ── gpu data ────────────────────────────────────────────── */
+typedef struct {
+ int util;
+ int mem_pct;
+ int temp;
+ float power;
+ float power_limit;
+ int valid;
+} GpuStats;
+
+static GpuStats gpu;
+
+/* ── x11 state ───────────────────────────────────────────── */
+static Display *dpy;
+static int scr;
+static Window win, iconwin;
+static GC gc;
+static Pixmap buf;
+static Atom tile_atom;
+
+static XftFont *font;
+static XftDraw *xdraw;
+static int fnt_asc;
+static int fnt_h;
+
+/* ── colour palette ──────────────────────────────────────── */
+typedef struct { const char *hex; XftColor xft; } Color;
+
+static Color co_fg = { "#e0e0e0" };
+static Color co_dim = { "#888899" };
+static Color co_warn = { "#e3b341" };
+static Color co_crit = { "#ff5555" };
+
+/* ── nvidia-smi query ────────────────────────────────────── */
+#define NVML_CMD \
+ "nvidia-smi --query-gpu=" \
+ "utilization.gpu,temperature.gpu,power.draw," \
+ "memory.used,memory.total,power.limit " \
+ "--format=csv,noheader,nounits 2>/dev/null"
+
+static void fetch(void)
+{
+ FILE *fp = popen(NVML_CMD, "r");
+ if (!fp) { gpu.valid = 0; return; }
+
+ int u, t, mu, mt;
+ float pw, pl;
+
+ if (fscanf(fp, "%d, %d, %f, %d, %d, %f",
+ &u, &t, &pw, &mu, &mt, &pl) == 6) {
+ gpu.util = u < 0 ? 0 : u > 100 ? 100 : u;
+ gpu.temp = t;
+ gpu.power = pw < 0.0f ? 0.0f : pw;
+ gpu.power_limit = pl > 0.0f ? pl : 300.0f;
+ gpu.mem_pct = mt > 0 ? mu * 100 / mt : 0;
+ if (gpu.mem_pct > 100) gpu.mem_pct = 100;
+ gpu.valid = 1;
+ } else {
+ gpu.valid = 0;
+ }
+ pclose(fp);
+}
+
+/* ── helpers ─────────────────────────────────────────────── */
+static void xft_alloc(Color *co)
+{
+ XftColorAllocName(dpy, DefaultVisual(dpy, scr),
+ DefaultColormap(dpy, scr), co->hex, &co->xft);
+}
+
+static int row_y(int row)
+{
+ int center = CONTENT_Y + row * ROW_H + ROW_H / 2;
+ return center + fnt_asc - fnt_h / 2;
+}
+
+/* Read _WINDOWMAKER_ICON_TILE from the root window. */
+static Pixmap get_wm_tile(void)
+{
+ if (tile_atom == None) return None;
+
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = NULL;
+
+ if (XGetWindowProperty(dpy, RootWindow(dpy, scr), tile_atom,
+ 0, 1, False, XA_PIXMAP,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &data) != Success || !data)
+ return None;
+
+ Pixmap tile = *(Pixmap *)data;
+ XFree(data);
+ return tile;
+}
+
+/* ── drawing ─────────────────────────────────────────────── */
+static void draw_row(int row, const char *label,
+ const char *value, XftColor *vcol)
+{
+ int y = row_y(row);
+
+ XftDrawStringUtf8(xdraw, &co_dim.xft, font,
+ CONTENT_X, y,
+ (const FcChar8 *)label, strlen(label));
+
+ XGlyphInfo ext;
+ XftTextExtentsUtf8(dpy, font,
+ (const FcChar8 *)value, strlen(value), &ext);
+ int vx = CONTENT_X + CONTENT_W - ext.xOff;
+ if (vx < CONTENT_X) vx = CONTENT_X;
+ XftDrawStringUtf8(xdraw, vcol, font,
+ vx, y, (const FcChar8 *)value, strlen(value));
+}
+
+static void paint(void)
+{
+ char s[16];
+
+ /* background: WM dock tile if available, else solid black */
+ Pixmap tile = get_wm_tile();
+ if (tile != None) {
+ XCopyArea(dpy, tile, buf, gc, 0, 0, WIN_SIZE, WIN_SIZE, 0, 0);
+ } else {
+ XSetForeground(dpy, gc, BlackPixel(dpy, scr));
+ XFillRectangle(dpy, buf, gc, 0, 0, WIN_SIZE, WIN_SIZE);
+ }
+
+ if (!gpu.valid) {
+ XftDrawStringUtf8(xdraw, &co_dim.xft, font,
+ CONTENT_X, row_y(1),
+ (const FcChar8 *)"no GPU data", 11);
+ goto flush;
+ }
+
+ snprintf(s, sizeof(s), "%3d%%", gpu.util);
+ draw_row(0, "UTIL", s, &co_fg.xft);
+
+ snprintf(s, sizeof(s), "%3d%%", gpu.mem_pct);
+ draw_row(1, "VRAM", s, &co_fg.xft);
+
+ snprintf(s, sizeof(s), "%3dC", gpu.temp);
+ {
+ XftColor *tc = gpu.temp >= 90 ? &co_crit.xft :
+ gpu.temp >= 75 ? &co_warn.xft : &co_fg.xft;
+ draw_row(2, "TEMP", s, tc);
+ }
+
+ snprintf(s, sizeof(s), "%.0fW", gpu.power);
+ draw_row(3, "WATT", s, &co_fg.xft);
+
+flush:
+ XCopyArea(dpy, buf, win, gc, 0, 0, WIN_SIZE, WIN_SIZE, 0, 0);
+ XCopyArea(dpy, buf, iconwin, gc, 0, 0, WIN_SIZE, WIN_SIZE, 0, 0);
+ XSync(dpy, False);
+}
+
+/* ── window + pixmap setup ───────────────────────────────── */
+static void make_window(int argc, char **argv)
+{
+ Window root = RootWindow(dpy, scr);
+ int depth = DefaultDepth(dpy, scr);
+ Visual *vis = DefaultVisual(dpy, scr);
+
+ tile_atom = XInternAtom(dpy, "_WINDOWMAKER_ICON_TILE", True);
+
+ /* subscribe to tile updates on the root window */
+ XSelectInput(dpy, root, PropertyChangeMask);
+
+ buf = XCreatePixmap(dpy, root, WIN_SIZE, WIN_SIZE, depth);
+
+ XSetWindowAttributes a;
+ a.background_pixel = BlackPixel(dpy, scr);
+ a.border_pixel = BlackPixel(dpy, scr);
+ a.event_mask = ExposureMask | StructureNotifyMask;
+ unsigned long mask = CWBackPixel | CWBorderPixel | CWEventMask;
+
+ win = XCreateWindow(dpy, root, 0, 0, WIN_SIZE, WIN_SIZE, 0,
+ depth, InputOutput, vis, mask, &a);
+ iconwin = XCreateWindow(dpy, root, 0, 0, WIN_SIZE, WIN_SIZE, 0,
+ depth, InputOutput, vis, mask, &a);
+
+ XWMHints *wm = XAllocWMHints();
+ wm->flags = StateHint | IconWindowHint | WindowGroupHint;
+ wm->initial_state = WithdrawnState;
+ wm->icon_window = iconwin;
+ wm->window_group = win;
+ XSetWMHints(dpy, win, wm);
+ XFree(wm);
+
+ XSizeHints *sz = XAllocSizeHints();
+ sz->flags = PMinSize | PMaxSize | PBaseSize;
+ sz->min_width = sz->max_width = sz->base_width = WIN_SIZE;
+ sz->min_height = sz->max_height = sz->base_height = WIN_SIZE;
+ XSetWMNormalHints(dpy, win, sz);
+ XFree(sz);
+
+ XStoreName(dpy, win, "wmgpumon");
+ XSetIconName(dpy, win, "wmgpumon");
+
+ XClassHint *ch = XAllocClassHint();
+ ch->res_name = "wmgpumon";
+ ch->res_class = "WMGpumon";
+ XSetClassHint(dpy, win, ch);
+ XFree(ch);
+
+ Atom wm_del = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(dpy, win, &wm_del, 1);
+
+ XGCValues gcv;
+ gcv.graphics_exposures = False;
+ gcv.function = GXcopy;
+ gc = XCreateGC(dpy, buf, GCGraphicsExposures | GCFunction, &gcv);
+
+ XSetCommand(dpy, win, argv, argc);
+ XMapWindow(dpy, win);
+}
+
+/* ── entry point ─────────────────────────────────────────── */
+int main(int argc, char **argv)
+{
+ const char *dpyname = NULL;
+ int interval = 2;
+
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-display") && i+1 < argc) dpyname = argv[++i];
+ if (!strcmp(argv[i], "-interval") && i+1 < argc) interval = atoi(argv[++i]);
+ if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
+ puts("usage: wmgpumon [-display <disp>] [-interval <sec>]");
+ return 0;
+ }
+ }
+ if (interval < 1) interval = 1;
+
+ dpy = XOpenDisplay(dpyname);
+ if (!dpy) { fputs("wmgpumon: cannot open display\n", stderr); return 1; }
+ scr = DefaultScreen(dpy);
+
+ xft_alloc(&co_fg);
+ xft_alloc(&co_dim);
+ xft_alloc(&co_warn);
+ xft_alloc(&co_crit);
+
+ make_window(argc, argv);
+
+ static const char *font_names[] = {
+ "UnifontExMono:size=10:antialias=false",
+ "UnifontExMono:size=9:antialias=false",
+ "monospace:size=10:antialias=false",
+ "monospace:size=10",
+ NULL
+ };
+ for (int i = 0; font_names[i]; i++) {
+ font = XftFontOpenName(dpy, scr, font_names[i]);
+ if (font) break;
+ }
+ if (!font) { fputs("wmgpumon: could not load any font\n", stderr); return 1; }
+ fnt_asc = font->ascent;
+ fnt_h = font->ascent + font->descent;
+
+ xdraw = XftDrawCreate(dpy, buf,
+ DefaultVisual(dpy, scr),
+ DefaultColormap(dpy, scr));
+
+ fetch();
+ paint();
+
+ int xfd = XConnectionNumber(dpy);
+ time_t last = time(NULL);
+
+ for (;;) {
+ fd_set fds;
+ struct timeval tv;
+ time_t now = time(NULL);
+ long wait = interval - (long)(now - last);
+
+ FD_ZERO(&fds);
+ FD_SET(xfd, &fds);
+ tv.tv_sec = wait > 0 ? wait : 0;
+ tv.tv_usec = 0;
+
+ select(xfd + 1, &fds, NULL, NULL, &tv);
+
+ while (XPending(dpy)) {
+ XEvent ev;
+ XNextEvent(dpy, &ev);
+ if (ev.type == Expose) paint();
+ if (ev.type == DestroyNotify) goto done;
+ if (ev.type == ClientMessage) goto done;
+ if (ev.type == PropertyNotify &&
+ ev.xproperty.atom == tile_atom)
+ paint();
+ }
+
+ now = time(NULL);
+ if (now - last >= interval) {
+ fetch();
+ paint();
+ last = now;
+ }
+ }
+done:
+ XftDrawDestroy(xdraw);
+ XftFontClose(dpy, font);
+ XFreeGC(dpy, gc);
+ XFreePixmap(dpy, buf);
+ XDestroyWindow(dpy, iconwin);
+ XDestroyWindow(dpy, win);
+ XCloseDisplay(dpy);
+ return 0;
+}