diff options
Diffstat (limited to 'src/cmd/rio')
-rw-r--r-- | src/cmd/rio/mkfile | 2 | ||||
-rw-r--r-- | src/cmd/rio/winwatch.c | 524 | ||||
-rw-r--r-- | src/cmd/rio/xshove.c | 7 |
3 files changed, 531 insertions, 2 deletions
diff --git a/src/cmd/rio/mkfile b/src/cmd/rio/mkfile index 8b8ea46a..20202e22 100644 --- a/src/cmd/rio/mkfile +++ b/src/cmd/rio/mkfile @@ -16,7 +16,7 @@ RIOFILES=\ CFLAGS=$CFLAGS -DDEBUG HFILES=dat.h fns.h -TARG=rio xshove +TARG=rio winwatch xshove # need to add lib64 when it exists (on x86-64), but # Darwin complains about the nonexistant directory diff --git a/src/cmd/rio/winwatch.c b/src/cmd/rio/winwatch.c new file mode 100644 index 00000000..1a93e78c --- /dev/null +++ b/src/cmd/rio/winwatch.c @@ -0,0 +1,524 @@ +/* + * 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 <u.h> +#include <libc.h> +#include <draw.h> +#include <event.h> +#include <regexp.h> +#include <fmt.h> +#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<nxwin; i++){ + memset(&attr, 0, sizeof attr); + xwin[i] = findname(xwin[i]); + if(xwin[i]==0) + continue; + + oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler); + s = XGetWindowAttributes(dpy, xwin[i], &attr); + XFlush(dpy); + XSync(dpy, False); + XSetErrorHandler(oldxerrorhandler); + if(s==0) + continue; + if (attr.width <= 0 || + attr.override_redirect || + attr.map_state != IsViewable) + continue; + + oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler); + s = XGetClassHint(dpy, xwin[i], &class); + XFlush(dpy); + XSync(dpy, False); + XSetErrorHandler(oldxerrorhandler); + + if(s==0) + continue; + + if (exclude!=nil && regexec(exclude, class.res_name, nil, 0)) { + free(class.res_name); + free(class.res_class); + continue; + } + + net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", FALSE); + wmname = getproperty(xwin[i], net_wm_name); + + if(wmname==nil){ + wmname = getproperty(xwin[i], XA_WM_NAME); + if(wmname==nil){ + free(class.res_name); + free(class.res_class); + continue; + } + } + + label = class.res_name; + if(showwmnames==1) + label = wmname; + + if(nw<nwin && win[nw].n==xwin[i] && strcmp(win[nw].label, label)==0) { + nw++; + free(wmname); + free(class.res_name); + free(class.res_class); + continue; + } + + if(nw<nwin){ + free(win[nw].label); + win[nw].label = nil; + } + + if(nw>=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 || 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; i<nwin; i++) + win[i].r = + rectaddpt( + rectaddpt(r, + Pt(MARGIN + (PAD + Dx(r)) * (i / rows), + MARGIN + (PAD + Dy(r)) * (i % rows))), + screen->r.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<nwin; i++) + if(all || win[i].dirty) + drawwin(i); + if(!all) + for (; i<onwin; i++) + drawnowin(i); + onwin = nwin; +} + +void +eresized(int new) +{ + if(new && getwindow(display, Refmesg)<0) + fprint(2, "can't reattach to window"); + geometry(); + redraw(screen, 1); +} + + +void +selectwin(XWindow win) +{ + XEvent ev; + long mask; + + memset(&ev, 0, sizeof ev); + ev.xclient.type = ClientMessage; + ev.xclient.serial = 0; + ev.xclient.send_event = True; + ev.xclient.message_type = net_active_window; + ev.xclient.window = win; + ev.xclient.format = 32; + mask = SubstructureRedirectMask | SubstructureNotifyMask; + + XSendEvent(dpy, root, False, mask, &ev); + XMapRaised(dpy, win); + XSync(dpy, False); +} + +void +click(Mouse m) +{ + int i, j; + + if(m.buttons==0 || (m.buttons&~4)) + return; + + for(i=0; i<nwin; i++) + if(ptinrect(m.xy, win[i].r)) + break; + if(i==nwin) + return; + + do + m = emouse(); + while(m.buttons==4); + + if(m.buttons!=0){ + do + m = emouse(); + while(m.buttons); + return; + } + for(j=0; j<nwin; j++) + if(ptinrect(m.xy, win[j].r)) + break; + if(j==i) + selectwin(win[i].n); +} + +void +usage(void) +{ + fprint(2, + "usage: winwatch [-e exclude] [-W winsize] [-f font] [-n] [-s]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + char *fontname; + int Etimer; + Event e; + + sortlabels = 0; + showwmnames = 0; + fontname = "/lib/font/bit/lucsans/unicode.8.font"; + + ARGBEGIN { + case 'W': + winsize = EARGF(usage()); + break; + case 'f': + fontname = EARGF(usage()); + break; + case 'e': + exclude = regcomp(EARGF(usage())); + if(exclude==nil) + sysfatal("Bad regexp"); + break; + case 's': + sortlabels = 1; + break; + case 'n': + showwmnames = 1; + break; + default: + usage(); + } ARGEND; + + if(argc) + usage(); + + /* moved up from original winwatch.c for p9p because there can be only one but we want to restart when needed */ + einit(Emouse | Ekeyboard); + Etimer = etimer(0, 1000); + + dpy = XOpenDisplay(""); + if(dpy==nil) + sysfatal("open display: %r"); + + root = DefaultRootWindow(dpy); + net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + + initdraw(0, 0, "winwatch"); + lightblue = allocimagemix(display, DPalebluegreen, DWhite); + if(lightblue==nil) + sysfatal("allocimagemix: %r"); + font = openfont(display, fontname); + if(font==nil) + sysfatal("font '%s' not found", fontname); + + /* reentry point upon X server errors */ + setjmp(savebuf); + + refreshwin(); + redraw(screen, 1); + for(;;){ + switch(eread(Emouse|Ekeyboard|Etimer, &e)){ + case Ekeyboard: + if(e.kbdc==0x7F || e.kbdc=='q') + exits(0); + break; + case Emouse: + if(e.mouse.buttons) + click(e.mouse); + /* fall through */ + default: /* Etimer */ + refreshwin(); + redraw(screen, 0); + break; + } + } +} diff --git a/src/cmd/rio/xshove.c b/src/cmd/rio/xshove.c index e235874e..7358987e 100644 --- a/src/cmd/rio/xshove.c +++ b/src/cmd/rio/xshove.c @@ -27,6 +27,7 @@ struct Win int y; int dx; int dy; + char *idstr; char *class; char *instance; char *name; @@ -143,6 +144,9 @@ getinfo(void) if(attr.width <= 0 || attr.override_redirect || attr.map_state != IsViewable) continue; ww->xw = xwin[i]; + char idstr[9]; + snprint(idstr, sizeof(idstr), "%08x", (uint)ww->xw); + ww->idstr = strdup(idstr); ww->x = attr.x; ww->y = attr.y; ww->dx = attr.width; @@ -196,7 +200,8 @@ shove(char *name, char *geom) for(i=0; i<nw; i++){ Win *ww = &w[i]; if(ww->instance && strstr(ww->instance, name) - || ww->class && strstr(ww->class, name)){ + || ww->class && strstr(ww->class, name) + || ww->idstr && strstr(ww->idstr, name)){ int value_mask; XWindowChanges e; |