/* * slightly modified from * https://github.com/fhs/misc/blob/master/cmd/winwatch/winwatch.c * so as to deal with memory leaks and certain X errors */ #include #include #include #include #include #include #include "../devdraw/x11-inc.h" AUTOLIB(X11); typedef struct Win Win; struct Win { XWindow n; int dirty; char *label; Rectangle r; }; XDisplay *dpy; XWindow root; Atom net_active_window; Reprog *exclude = nil; Win *win; int nwin; int mwin; int onwin; int rows, cols; int sortlabels; int showwmnames; Font *font; Image *lightblue; XErrorHandler oldxerrorhandler; enum { PAD = 3, MARGIN = 5 }; static jmp_buf savebuf; int winwatchxerrorhandler(XDisplay *disp, XErrorEvent *xe) { char buf[100]; XGetErrorText(disp, xe->error_code, buf, 100); fprint(2, "winwatch: X error %s, request code %d\n", buf, xe->request_code); XFlush(disp); XSync(disp, False); XSetErrorHandler(oldxerrorhandler); longjmp(savebuf, 1); return(0); /* Not reached */ } void* erealloc(void *v, ulong n) { v = realloc(v, n); if(v==nil) sysfatal("out of memory reallocating"); return v; } char* estrdup(char *s) { s = strdup(s); if(s==nil) sysfatal("out of memory allocating"); return(s); } char* getproperty(XWindow w, Atom a) { uchar *p; int fmt; Atom type; ulong n, dummy; int s; n = 100; p = nil; oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler); s = XGetWindowProperty(dpy, w, a, 0, 100L, 0, AnyPropertyType, &type, &fmt, &n, &dummy, &p); XFlush(dpy); XSync(dpy, False); XSetErrorHandler(oldxerrorhandler); if(s!=0){ XFree(p); return(nil); } return((char*)p); } XWindow findname(XWindow w) { int i; uint nxwin; XWindow dw1, dw2, *xwin; char *p; int s; Atom net_wm_name; p = getproperty(w, XA_WM_NAME); if(p){ free(p); return(w); } net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", FALSE); p = getproperty(w, net_wm_name); if(p){ free(p); return(w); } oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler); s = XQueryTree(dpy, w, &dw1, &dw2, &xwin, &nxwin); XFlush(dpy); XSync(dpy, False); XSetErrorHandler(oldxerrorhandler); if(s == 0) { if (xwin != NULL) XFree(xwin); return 0; } for (i = 0; i < nxwin; i++) { w = findname(xwin[i]); if (w != 0) { XFree(xwin); return w; } } XFree(xwin); return 0; } int wcmp(const void *w1, const void *w2) { return *(XWindow *) w1 - *(XWindow *) w2; } /* unicode-aware case-insensitive strcmp, taken from golang’s gc/subr.c */ int _cistrcmp(char *p, char *q) { Rune rp, rq; while(*p || *q) { if(*p == 0) return +1; if(*q == 0) return -1; p += chartorune(&rp, p); q += chartorune(&rq, q); rp = tolowerrune(rp); rq = tolowerrune(rq); if(rp < rq) return -1; if(rp > rq) return +1; } return 0; } int winlabelcmp(const void *w1, const void *w2) { const Win *p1 = (Win *) w1; const Win *p2 = (Win *) w2; return _cistrcmp(p1->label, p2->label); } void refreshwin(void) { XWindow dw1, dw2, *xwin; XClassHint class; XWindowAttributes attr; char *label; char *wmname; int i, nw; uint nxwin; Status s; Atom net_wm_name; oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler); s = XQueryTree(dpy, root, &dw1, &dw2, &xwin, &nxwin); XFlush(dpy); XSync(dpy, False); XSetErrorHandler(oldxerrorhandler); if(s==0){ if(xwin!=NULL) XFree(xwin); return; } qsort(xwin, nxwin, sizeof(xwin[0]), wcmp); nw = 0; for(i=0; i=mwin){ mwin += 8; win = erealloc(win, mwin * sizeof(win[0])); } win[nw].n = xwin[i]; win[nw].label = estrdup(label); win[nw].dirty = 1; win[nw].r = Rect(0, 0, 0, 0); free(wmname); free(class.res_name); free(class.res_class); nw++; } oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler); XFree(xwin); XFlush(dpy); XSync(dpy, False); XSetErrorHandler(oldxerrorhandler); while(nwin>nw) free(win[--nwin].label); nwin = nw; if(sortlabels==1) qsort(win, nwin, sizeof(struct Win), winlabelcmp); } void drawnowin(int i) { Rectangle r; r = Rect(0, 0, (Dx(screen->r) - 2 * MARGIN + PAD) / cols - PAD, font->height); r = rectaddpt( rectaddpt(r, Pt(MARGIN + (PAD + Dx(r)) * (i / rows), MARGIN + (PAD + Dy(r)) * (i % rows))), screen->r.min); draw(screen, insetrect(r, -1), lightblue, nil, ZP); } void drawwin(int i) { draw(screen, win[i].r, lightblue, nil, ZP); _string(screen, addpt(win[i].r.min, Pt(2, 0)), display->black, ZP, font, win[i].label, nil, strlen(win[i].label), win[i].r, nil, ZP, SoverD); border(screen, win[i].r, 1, display->black, ZP); win[i].dirty = 0; } int geometry(void) { int i, ncols, z; Rectangle r; z = 0; rows = (Dy(screen->r) - 2 * MARGIN + PAD) / (font->height + PAD); if(rows*cols=nwin*2){ ncols = 1; if(nwin>0) ncols = (nwin + rows - 1) / rows; if(ncols!=cols){ cols = ncols; z = 1; } } r = Rect(0, 0, (Dx(screen->r) - 2 * MARGIN + PAD) / cols - PAD, font->height); for(i=0; ir.min); return z; } void redraw(Image *screen, int all) { int i; all |= geometry(); if(all) draw(screen, screen->r, lightblue, nil, ZP); for(i=0; i