plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

winwatch.c (8283B)


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