URI: 
       twinwatch.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       twinwatch.c (9410B)
       ---
            1 /*
            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
            5  */
            6 
            7 #include <u.h>
            8 #include <libc.h>
            9 #include <draw.h>
           10 #include <event.h>
           11 #include <regexp.h>
           12 #include <fmt.h>
           13 #include "../devdraw/x11-inc.h"
           14 
           15 AUTOLIB(X11);
           16 
           17 typedef struct Win Win;
           18 struct Win {
           19         XWindow n;
           20         int dirty;
           21         char *label;
           22         Rectangle r;
           23 };
           24 
           25 XDisplay *dpy;
           26 XWindow root;
           27 Atom net_active_window;
           28 Reprog *exclude = nil;
           29 Win *win;
           30 int nwin;
           31 int mwin;
           32 int onwin;
           33 int rows, cols;
           34 int sortlabels;
           35 int showwmnames;
           36 Font *font;
           37 Image *lightblue;
           38 
           39 XErrorHandler oldxerrorhandler;
           40 
           41 enum {
           42         PAD = 3,
           43         MARGIN = 5
           44 };
           45 
           46 static jmp_buf savebuf;
           47 
           48 int
           49 winwatchxerrorhandler(XDisplay *disp, XErrorEvent *xe)
           50 {
           51         char buf[100];
           52 
           53         XGetErrorText(disp, xe->error_code, buf, 100);
           54         fprint(2, "winwatch: X error %s, request code %d\n",
           55             buf, xe->request_code);
           56         XFlush(disp);
           57         XSync(disp, False);
           58         XSetErrorHandler(oldxerrorhandler);
           59         longjmp(savebuf, 1);
           60         return(0);  /* Not reached */
           61 }
           62 
           63 void*
           64 erealloc(void *v, ulong n)
           65 {
           66         v = realloc(v, n);
           67         if(v==nil)
           68                 sysfatal("out of memory reallocating");
           69         return v;
           70 }
           71 
           72 char*
           73 estrdup(char *s)
           74 {
           75         s = strdup(s);
           76         if(s==nil)
           77                 sysfatal("out of memory allocating");
           78         return(s);
           79 }
           80 
           81 char*
           82 getproperty(XWindow w, Atom a)
           83 {
           84         uchar *p;
           85         int fmt;
           86         Atom type;
           87         ulong n, dummy;
           88         int s;
           89 
           90         n = 100;
           91         p = nil;
           92         oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
           93         s = XGetWindowProperty(dpy, w, a, 0, 100L, 0,
           94             AnyPropertyType, &type, &fmt, &n, &dummy, &p);
           95         XFlush(dpy);
           96         XSync(dpy, False);
           97         XSetErrorHandler(oldxerrorhandler);
           98         if(s!=0){
           99                 XFree(p);
          100                 return(nil);
          101         }
          102 
          103         return((char*)p);
          104 }
          105 
          106 XWindow
          107 findname(XWindow w)
          108 {
          109         int i;
          110         uint nxwin;
          111         XWindow dw1, dw2, *xwin;
          112         char *p;
          113         int s;
          114         Atom net_wm_name;
          115 
          116         p = getproperty(w, XA_WM_NAME);
          117         if(p){
          118                 free(p);
          119                 return(w);
          120         }
          121 
          122         net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", FALSE);
          123         p = getproperty(w, net_wm_name);
          124         if(p){
          125                 free(p);
          126                 return(w);
          127         }
          128 
          129         oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
          130         s = XQueryTree(dpy, w, &dw1, &dw2, &xwin, &nxwin);
          131         XFlush(dpy);
          132         XSync(dpy, False);
          133         XSetErrorHandler(oldxerrorhandler);
          134         if(s == 0) {
          135                 if (xwin != NULL)
          136                         XFree(xwin);
          137                 return 0;
          138         }
          139 
          140         for (i = 0; i < nxwin; i++) {
          141                 w = findname(xwin[i]);
          142                 if (w != 0) {
          143                         XFree(xwin);
          144                         return w;
          145                 }
          146         }
          147         XFree(xwin);
          148 
          149         return 0;
          150 }
          151 
          152 int
          153 wcmp(const void *w1, const void *w2)
          154 {
          155         return *(XWindow *) w1 - *(XWindow *) w2;
          156 }
          157 
          158 /* unicode-aware case-insensitive strcmp,  taken from golang’s gc/subr.c */
          159 
          160 int
          161 _cistrcmp(char *p, char *q)
          162 {
          163         Rune rp, rq;
          164 
          165         while(*p || *q) {
          166                 if(*p == 0)
          167                         return +1;
          168                 if(*q == 0)
          169                         return -1;
          170                 p += chartorune(&rp, p);
          171                 q += chartorune(&rq, q);
          172                 rp = tolowerrune(rp);
          173                 rq = tolowerrune(rq);
          174                 if(rp < rq)
          175                         return -1;
          176                 if(rp > rq)
          177                         return +1;
          178         }
          179         return 0;
          180 }
          181 
          182 int
          183 winlabelcmp(const void *w1, const void *w2)
          184 {
          185         const Win *p1 = (Win *) w1;
          186         const Win *p2 = (Win *) w2;
          187         return _cistrcmp(p1->label, p2->label);
          188 }
          189 
          190 void
          191 refreshwin(void)
          192 {
          193         XWindow dw1, dw2, *xwin;
          194         XClassHint class;
          195         XWindowAttributes attr;
          196         char *label;
          197         char *wmname;
          198         int i, nw;
          199         uint nxwin;
          200         Status s;
          201         Atom net_wm_name;
          202 
          203 
          204         oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
          205         s = XQueryTree(dpy, root, &dw1, &dw2, &xwin, &nxwin);
          206         XFlush(dpy);
          207         XSync(dpy, False);
          208         XSetErrorHandler(oldxerrorhandler);
          209         if(s==0){
          210                 if(xwin!=NULL)
          211                         XFree(xwin);
          212                 return;
          213         }
          214         qsort(xwin, nxwin, sizeof(xwin[0]), wcmp);
          215 
          216         nw = 0;
          217         for(i=0; i<nxwin; i++){
          218                 memset(&attr, 0, sizeof attr);
          219                 xwin[i] = findname(xwin[i]);
          220                 if(xwin[i]==0)
          221                         continue;
          222 
          223                 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
          224                 s = XGetWindowAttributes(dpy, xwin[i], &attr);
          225                 XFlush(dpy);
          226                 XSync(dpy, False);
          227                 XSetErrorHandler(oldxerrorhandler);
          228                 if(s==0)
          229                         continue;
          230                 if (attr.width <= 0 ||
          231                     attr.override_redirect ||
          232                     attr.map_state != IsViewable)
          233                         continue;
          234 
          235                 oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
          236                 s = XGetClassHint(dpy, xwin[i], &class);
          237                 XFlush(dpy);
          238                 XSync(dpy, False);
          239                 XSetErrorHandler(oldxerrorhandler);
          240 
          241                 if(s==0)
          242                         continue;
          243 
          244                 if (exclude!=nil && regexec(exclude, class.res_name, nil, 0)) {
          245                         free(class.res_name);
          246                         free(class.res_class);
          247                         continue;
          248                 }
          249 
          250                 net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", FALSE);
          251                 wmname = getproperty(xwin[i], net_wm_name);
          252 
          253                 if(wmname==nil){
          254                         wmname = getproperty(xwin[i], XA_WM_NAME);
          255                         if(wmname==nil){
          256                                 free(class.res_name);
          257                                 free(class.res_class);
          258                                 continue;
          259                         }
          260                 }
          261 
          262                 label = class.res_name;
          263                 if(showwmnames==1)
          264                         label = wmname;
          265 
          266                 if(nw<nwin && win[nw].n==xwin[i] && strcmp(win[nw].label, label)==0) {
          267                         nw++;
          268                         free(wmname);
          269                         free(class.res_name);
          270                         free(class.res_class);
          271                         continue;
          272                 }
          273 
          274                 if(nw<nwin){
          275                         free(win[nw].label);
          276                         win[nw].label = nil;
          277                 }
          278 
          279                 if(nw>=mwin){
          280                         mwin += 8;
          281                         win = erealloc(win, mwin * sizeof(win[0]));
          282                 }
          283                 win[nw].n = xwin[i];
          284                 win[nw].label = estrdup(label);
          285                 win[nw].dirty = 1;
          286                 win[nw].r = Rect(0, 0, 0, 0);
          287                 free(wmname);
          288                 free(class.res_name);
          289                 free(class.res_class);
          290                 nw++;
          291         }
          292 
          293         oldxerrorhandler = XSetErrorHandler(winwatchxerrorhandler);
          294         XFree(xwin);
          295         XFlush(dpy);
          296         XSync(dpy, False);
          297         XSetErrorHandler(oldxerrorhandler);
          298 
          299         while(nwin>nw)
          300                 free(win[--nwin].label);
          301         nwin = nw;
          302 
          303         if(sortlabels==1)
          304                 qsort(win, nwin, sizeof(struct Win), winlabelcmp);
          305 }
          306 
          307 void
          308 drawnowin(int i)
          309 {
          310         Rectangle r;
          311 
          312         r = Rect(0, 0, (Dx(screen->r) - 2 * MARGIN + PAD) / cols - PAD, font->height);
          313         r = rectaddpt(
          314                 rectaddpt(r,
          315                     Pt(MARGIN + (PAD + Dx(r)) * (i / rows),
          316                          MARGIN + (PAD + Dy(r)) * (i % rows))),
          317                 screen->r.min);
          318         draw(screen, insetrect(r, -1), lightblue, nil, ZP);
          319 }
          320 
          321 void
          322 drawwin(int i)
          323 {
          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);
          329         win[i].dirty = 0;
          330 }
          331 
          332 int
          333 geometry(void)
          334 {
          335         int i, ncols, z;
          336         Rectangle r;
          337 
          338         z = 0;
          339         rows = (Dy(screen->r) - 2 * MARGIN + PAD) / (font->height + PAD);
          340         if(rows*cols<nwin || rows*cols>=nwin*2){
          341                 ncols = 1;
          342                 if(nwin>0)
          343                         ncols = (nwin + rows - 1) / rows;
          344                 if(ncols!=cols){
          345                         cols = ncols;
          346                         z = 1;
          347                 }
          348         }
          349 
          350         r = Rect(0, 0, (Dx(screen->r) - 2 * MARGIN + PAD) / cols - PAD, font->height);
          351         for(i=0; i<nwin; i++)
          352                 win[i].r =
          353                     rectaddpt(
          354                         rectaddpt(r,
          355                             Pt(MARGIN + (PAD + Dx(r)) * (i / rows),
          356                                 MARGIN + (PAD + Dy(r)) * (i % rows))),
          357                         screen->r.min);
          358 
          359         return z;
          360 }
          361 
          362 void
          363 redraw(Image *screen, int all)
          364 {
          365         int i;
          366 
          367         all |= geometry();
          368         if(all)
          369                 draw(screen, screen->r, lightblue, nil, ZP);
          370         for(i=0; i<nwin; i++)
          371                 if(all || win[i].dirty)
          372                         drawwin(i);
          373         if(!all)
          374                 for (; i<onwin; i++)
          375                         drawnowin(i);
          376         onwin = nwin;
          377 }
          378 
          379 void
          380 eresized(int new)
          381 {
          382         if(new && getwindow(display, Refmesg)<0)
          383                 fprint(2, "can't reattach to window");
          384         geometry();
          385         redraw(screen, 1);
          386 }
          387 
          388 
          389 void
          390 selectwin(XWindow win)
          391 {
          392         XEvent ev;
          393         long mask;
          394 
          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;
          403 
          404         XSendEvent(dpy, root, False, mask, &ev);
          405         XMapRaised(dpy, win);
          406         XSync(dpy, False);
          407 }
          408 
          409 void
          410 click(Mouse m)
          411 {
          412         int i, j;
          413 
          414         if(m.buttons==0 || (m.buttons&~4))
          415                 return;
          416 
          417         for(i=0; i<nwin; i++)
          418                 if(ptinrect(m.xy, win[i].r))
          419                         break;
          420         if(i==nwin)
          421                 return;
          422 
          423         do
          424                 m = emouse();
          425         while(m.buttons==4);
          426 
          427         if(m.buttons!=0){
          428                 do
          429                         m = emouse();
          430                 while(m.buttons);
          431                 return;
          432         }
          433         for(j=0; j<nwin; j++)
          434                 if(ptinrect(m.xy, win[j].r))
          435                         break;
          436         if(j==i)
          437                 selectwin(win[i].n);
          438 }
          439 
          440 void
          441 usage(void)
          442 {
          443         fprint(2,
          444             "usage: winwatch [-e exclude] [-W winsize] [-f font] [-n] [-s]\n");
          445         exits("usage");
          446 }
          447 
          448 void
          449 main(int argc, char **argv)
          450 {
          451         char *fontname;
          452         int Etimer;
          453         Event e;
          454 
          455         sortlabels = 0;
          456         showwmnames = 0;
          457         fontname = "/lib/font/bit/lucsans/unicode.8.font";
          458 
          459         ARGBEGIN {
          460         case 'W':
          461                 winsize = EARGF(usage());
          462                 break;
          463         case 'f':
          464                 fontname = EARGF(usage());
          465                 break;
          466         case 'e':
          467                 exclude = regcomp(EARGF(usage()));
          468                 if(exclude==nil)
          469                         sysfatal("Bad regexp");
          470                 break;
          471         case 's':
          472                 sortlabels = 1;
          473                 break;
          474         case 'n':
          475                 showwmnames = 1;
          476                 break;
          477         default:
          478                 usage();
          479         } ARGEND;
          480 
          481         if(argc)
          482             usage();
          483 
          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);
          487 
          488         dpy = XOpenDisplay("");
          489         if(dpy==nil)
          490                 sysfatal("open display: %r");
          491 
          492         root = DefaultRootWindow(dpy);
          493         net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
          494 
          495         initdraw(0, 0, "winwatch");
          496         lightblue = allocimagemix(display, DPalebluegreen, DWhite);
          497         if(lightblue==nil)
          498                 sysfatal("allocimagemix: %r");
          499         font = openfont(display, fontname);
          500         if(font==nil)
          501                 sysfatal("font '%s' not found", fontname);
          502 
          503         /* reentry point upon X server errors */
          504         setjmp(savebuf);
          505 
          506         refreshwin();
          507         redraw(screen, 1);
          508         for(;;){
          509                 switch(eread(Emouse|Ekeyboard|Etimer, &e)){
          510                 case Ekeyboard:
          511                         if(e.kbdc==0x7F || e.kbdc=='q')
          512                                 exits(0);
          513                         break;
          514                 case Emouse:
          515                         if(e.mouse.buttons)
          516                                 click(e.mouse);
          517                         /* fall through  */
          518                 default:                /* Etimer */
          519                         refreshwin();
          520                         redraw(screen, 0);
          521                         break;
          522                 }
          523         }
          524 }