2 * slightly modified from
3 * https://github.com/fhs/misc/blob/master/cmd/winwatch/winwatch.c
4 * so as to deal with memory leaks and certain X errors
13 #include "../devdraw/x11-inc.h"
17 typedef struct Win Win;
27 Atom net_active_window;
28 Reprog *exclude = nil;
39 XErrorHandler oldxerrorhandler;
46 static jmp_buf savebuf;
49 winwatchxerrorhandler(XDisplay *disp, XErrorEvent *xe)
53 XGetErrorText(disp, xe->error_code, buf, 100);
54 fprint(2, "winwatch: X error %s, request code %d\n",
55 buf, xe->request_code);
58 XSetErrorHandler(oldxerrorhandler);
60 return(0); /* Not reached */
64 erealloc(void *v, ulong n)
68 sysfatal("out of memory reallocating");
77 sysfatal("out of memory allocating");
82 getproperty(XWindow w, Atom a)
92 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
93 s = XGetWindowProperty(dpy, w, a, 0, 100L, 0,
94 AnyPropertyType, &type, &fmt, &n, &dummy, &p);
97 XSetErrorHandler(oldxerrorhandler);
111 XWindow dw1, dw2, *xwin;
116 p = getproperty(w, XA_WM_NAME);
122 net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", FALSE);
123 p = getproperty(w, net_wm_name);
129 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
130 s = XQueryTree(dpy, w, &dw1, &dw2, &xwin, &nxwin);
133 XSetErrorHandler(oldxerrorhandler);
140 for (i = 0; i < nxwin; i++) {
141 w = findname(xwin[i]);
153 wcmp(const void *w1, const void *w2)
155 return *(XWindow *) w1 - *(XWindow *) w2;
158 /* unicode-aware case-insensitive strcmp, taken from golang’s gc/subr.c */
161 _cistrcmp(char *p, char *q)
170 p += chartorune(&rp, p);
171 q += chartorune(&rq, q);
172 rp = tolowerrune(rp);
173 rq = tolowerrune(rq);
183 winlabelcmp(const void *w1, const void *w2)
185 const Win *p1 = (Win *) w1;
186 const Win *p2 = (Win *) w2;
187 return _cistrcmp(p1->label, p2->label);
193 XWindow dw1, dw2, *xwin;
195 XWindowAttributes attr;
204 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
205 s = XQueryTree(dpy, root, &dw1, &dw2, &xwin, &nxwin);
208 XSetErrorHandler(oldxerrorhandler);
214 qsort(xwin, nxwin, sizeof(xwin[0]), wcmp);
217 for(i=0; i<nxwin; i++){
218 memset(&attr, 0, sizeof attr);
219 xwin[i] = findname(xwin[i]);
223 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
224 s = XGetWindowAttributes(dpy, xwin[i], &attr);
227 XSetErrorHandler(oldxerrorhandler);
230 if (attr.width <= 0 ||
231 attr.override_redirect ||
232 attr.map_state != IsViewable)
235 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
236 s = XGetClassHint(dpy, xwin[i], &class);
239 XSetErrorHandler(oldxerrorhandler);
244 if (exclude!=nil && regexec(exclude, class.res_name, nil, 0)) {
245 free(class.res_name);
246 free(class.res_class);
250 net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", FALSE);
251 wmname = getproperty(xwin[i], net_wm_name);
254 wmname = getproperty(xwin[i], XA_WM_NAME);
256 free(class.res_name);
257 free(class.res_class);
262 label = class.res_name;
266 if(nw<nwin && win[nw].n==xwin[i] && strcmp(win[nw].label, label)==0) {
269 free(class.res_name);
270 free(class.res_class);
281 win = erealloc(win, mwin * sizeof(win[0]));
284 win[nw].label = estrdup(label);
286 win[nw].r = Rect(0, 0, 0, 0);
288 free(class.res_name);
289 free(class.res_class);
293 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
297 XSetErrorHandler(oldxerrorhandler);
300 free(win[--nwin].label);
304 qsort(win, nwin, sizeof(struct Win), winlabelcmp);
312 r = Rect(0, 0, (Dx(screen->r) - 2 * MARGIN + PAD) / cols - PAD, font->height);
315 Pt(MARGIN + (PAD + Dx(r)) * (i / rows),
316 MARGIN + (PAD + Dy(r)) * (i % rows))),
318 draw(screen, insetrect(r, -1), lightblue, nil, ZP);
324 draw(screen, win[i].r, lightblue, nil, ZP);
325 _string(screen, addpt(win[i].r.min, Pt(2, 0)), display->black, ZP,
326 font, win[i].label, nil, strlen(win[i].label),
327 win[i].r, nil, ZP, SoverD);
328 border(screen, win[i].r, 1, display->black, ZP);
339 rows = (Dy(screen->r) - 2 * MARGIN + PAD) / (font->height + PAD);
340 if(rows*cols<nwin || rows*cols>=nwin*2){
343 ncols = (nwin + rows - 1) / rows;
350 r = Rect(0, 0, (Dx(screen->r) - 2 * MARGIN + PAD) / cols - PAD, font->height);
351 for(i=0; i<nwin; i++)
355 Pt(MARGIN + (PAD + Dx(r)) * (i / rows),
356 MARGIN + (PAD + Dy(r)) * (i % rows))),
363 redraw(Image *screen, int all)
369 draw(screen, screen->r, lightblue, nil, ZP);
370 for(i=0; i<nwin; i++)
371 if(all || win[i].dirty)
382 if(new && getwindow(display, Refmesg)<0)
383 fprint(2, "can't reattach to window");
390 selectwin(XWindow win)
395 memset(&ev, 0, sizeof ev);
396 ev.xclient.type = ClientMessage;
397 ev.xclient.serial = 0;
398 ev.xclient.send_event = True;
399 ev.xclient.message_type = net_active_window;
400 ev.xclient.window = win;
401 ev.xclient.format = 32;
402 mask = SubstructureRedirectMask | SubstructureNotifyMask;
404 XSendEvent(dpy, root, False, mask, &ev);
405 XMapRaised(dpy, win);
414 if(m.buttons==0 || (m.buttons&~4))
417 for(i=0; i<nwin; i++)
418 if(ptinrect(m.xy, win[i].r))
433 for(j=0; j<nwin; j++)
434 if(ptinrect(m.xy, win[j].r))
444 "usage: winwatch [-e exclude] [-W winsize] [-f font] [-n] [-s]\n");
449 main(int argc, char **argv)
457 fontname = "/lib/font/bit/lucsans/unicode.8.font";
461 winsize = EARGF(usage());
464 fontname = EARGF(usage());
467 exclude = regcomp(EARGF(usage()));
469 sysfatal("Bad regexp");
484 /* moved up from original winwatch.c for p9p because there can be only one but we want to restart when needed */
485 einit(Emouse | Ekeyboard);
486 Etimer = etimer(0, 1000);
488 dpy = XOpenDisplay("");
490 sysfatal("open display: %r");
492 root = DefaultRootWindow(dpy);
493 net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
495 initdraw(0, 0, "winwatch");
496 lightblue = allocimagemix(display, DPalebluegreen, DWhite);
498 sysfatal("allocimagemix: %r");
499 font = openfont(display, fontname);
501 sysfatal("font '%s' not found", fontname);
503 /* reentry point upon X server errors */
509 switch(eread(Emouse|Ekeyboard|Etimer, &e)){
511 if(e.kbdc==0x7F || e.kbdc=='q')
518 default: /* Etimer */