plan9port/src/cmd/rio/winwatch.c
Dan Cross 6510a2d353 winwatch: Plan 9-ify.
This is new code, and custom to plan9port.  Make it
conform more closely to plan9 style.

Signed-off-by: Dan Cross <cross@gajendra.net>
2020-01-15 09:58:24 -05:00

525 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 golangs 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;
}
}
}