1 /*
2  * Window management.
3  */
4 
5 /* Copyright (c) 1994-1996 David Hogan, see README for licence details */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <inttypes.h>
9 #include <X11/X.h>
10 #include <X11/Xos.h>
11 #include <X11/Xlib.h>
12 #include <X11/Xutil.h>
13 #include <X11/Xatom.h>
14 #include <X11/extensions/shape.h>
15 #include "dat.h"
16 #include "fns.h"
17 
18 int isNew;
19 
20 int
manage(Client * c,int mapped)21 manage(Client *c, int mapped)
22 {
23 	int fixsize, dohide, doreshape, state;
24 	long msize;
25 	XClassHint class;
26 	XWMHints *hints;
27 	XSetWindowAttributes attrs;
28 
29 	trace("manage", c, 0);
30 	XSelectInput(dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask | FocusChangeMask | KeyPressMask);
31 
32 	/* Get loads of hints */
33 
34 	if(XGetClassHint(dpy, c->window, &class) != 0){	/* ``Success'' */
35 		c->instance = class.res_name;
36 		c->class = class.res_class;
37 		c->is9term = 0;
38 		if(isNew){
39 			c->is9term = strstr(c->class, "term") || strstr(c->class, "Term");
40 			isNew = 0;
41 		}
42 	}
43 	else {
44 		c->instance = 0;
45 		c->class = 0;
46 		c->is9term = 0;
47 	}
48 	c->iconname = getprop(c->window, XA_WM_ICON_NAME);
49 	c->name = getprop(c->window, XA_WM_NAME);
50 	setlabel(c);
51 
52 	hints = XGetWMHints(dpy, c->window);
53 	if(XGetWMNormalHints(dpy, c->window, &c->size, &msize) == 0 || c->size.flags == 0)
54 		c->size.flags = PSize;		/* not specified - punt */
55 
56 	getcmaps(c);
57 	getproto(c);
58 	gettrans(c);
59 	if(c->is9term)
60 		c->hold = getiprop(c->window, _rio_hold_mode);
61 
62 	/* Figure out what to do with the window from hints */
63 
64 	if(!getstate(c->window, &state))
65 		state = hints ? hints->initial_state : NormalState;
66 	dohide = (state == IconicState);
67 
68 	fixsize = 0;
69 	if((c->size.flags & (USSize|PSize)))
70 		fixsize = 1;
71 	if((c->size.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize) && c->size.min_width == c->size.max_width && c->size.min_height == c->size.max_height)
72 		fixsize = 1;
73 	doreshape = !mapped;
74 	if(fixsize){
75 		if(c->size.flags & USPosition)
76 			doreshape = 0;
77 		if(dohide && (c->size.flags & PPosition))
78 			doreshape = 0;
79 		if(c->trans != None)
80 			doreshape = 0;
81 	}
82 	if(c->is9term)
83 		fixsize = 0;
84 	if(c->size.flags & PBaseSize){
85 		c->min_dx = c->size.base_width;
86 		c->min_dy = c->size.base_height;
87 	}
88 	else if(c->size.flags & PMinSize){
89 		c->min_dx = c->size.min_width;
90 		c->min_dy = c->size.min_height;
91 	}
92 	else if(c->is9term){
93 		c->min_dx = 100;
94 		c->min_dy = 50;
95 	}
96 	else
97 		c->min_dx = c->min_dy = 0;
98 
99 	if(hints)
100 		XFree(hints);
101 
102 	/* Now do it!!! */
103 
104 	if(doreshape){
105 		if(0) fprintf(stderr, "in doreshape is9term=%d fixsize=%d, x=%d, y=%d, min_dx=%d, min_dy=%d, dx=%d, dy=%d\n",
106 				c->is9term, fixsize, c->x, c->y, c->min_dx, c->min_dy, c->dx, c->dy);
107 		if(current && current->screen == c->screen)
108 			cmapnofocus(c->screen);
109 		if(!c->is9term && c->x==0 && c->y==0){
110 			static int nwin;
111 
112 			c->x = 20*nwin+BORDER;
113 			c->y = 20*nwin+BORDER;
114 			nwin++;
115 			nwin %= 10;
116 		}
117 
118 		if(c->is9term && !(fixsize ? drag(c, Button3) : sweep(c, Button3))){
119 			XKillClient(dpy, c->window);
120 			rmclient(c);
121 			if(current && current->screen == c->screen)
122 				cmapfocus(current);
123 			return 0;
124 		}
125 	}
126 
127 	attrs.border_pixel =  c->screen->black;
128 	attrs.background_pixel =  c->screen->white;
129 	attrs.colormap = c->screen->def_cmap;
130 	c->parent = XCreateWindow(dpy, c->screen->root,
131 			c->x - BORDER, c->y - BORDER,
132 			c->dx + 2*BORDER, c->dy + 2*BORDER,
133 			0,
134 			c->screen->depth,
135 			CopyFromParent,
136 			c->screen->vis,
137 			CWBackPixel | CWBorderPixel | CWColormap,
138 			&attrs);
139 
140 	XSelectInput(dpy, c->parent, SubstructureRedirectMask | SubstructureNotifyMask|ButtonPressMask| PointerMotionMask|LeaveWindowMask|KeyPressMask);
141 	if(mapped)
142 		c->reparenting = 1;
143 	if(doreshape && !fixsize)
144 		XResizeWindow(dpy, c->window, c->dx, c->dy);
145 	XSetWindowBorderWidth(dpy, c->window, 0);
146 
147 	/*
148 	  * To have something more than only a big white or black border
149 	  * XXX should replace this by a pattern in the white or black
150 	  * such that we can see the border also if all our
151 	  * windows are black and/or white
152 	  * (black (or white)  border around black (or white) window
153 	  *  is not very helpful.
154 	  */
155 	if(c->screen->depth <= 8){
156 		XSetWindowBorderWidth(dpy, c->parent, 1);
157 	}
158 
159 	XReparentWindow(dpy, c->window, c->parent, BORDER, BORDER);
160 #ifdef	SHAPE
161 	if(shape){
162 		XShapeSelectInput(dpy, c->window, ShapeNotifyMask);
163 		ignore_badwindow = 1;		/* magic */
164 		setshape(c);
165 		ignore_badwindow = 0;
166 	}
167 #endif
168 	XAddToSaveSet(dpy, c->window);
169 	if(dohide)
170 		hide(c);
171 	else {
172 		XMapWindow(dpy, c->window);
173 		XMapWindow(dpy, c->parent);
174 		XUnmapWindow(dpy, c->screen->sweepwin);
175 		if(nostalgia || doreshape)
176 			active(c);
177 		else if(c->trans != None && current && current->window == c->trans)
178 			active(c);
179 		else
180 			setactive(c, 0);
181 		setstate(c, NormalState);
182 	}
183 	if(current && (current != c))
184 		cmapfocus(current);
185 	c->init = 1;
186 
187 	/*
188 	 * If we swept the window, let's send a resize event to the
189 	 * guy who just got resized.  It's not clear whether the apps
190 	 * should notice their new size via other means.  Try as I might,
191 	 * I can't find a way to have them notice during initdraw, so
192 	 * I solve the problem this way instead.		-rsc
193 	 */
194 	if(c->is9term)
195 		sendconfig(c);
196 	return 1;
197 }
198 
199 void
scanwins(ScreenInfo * s)200 scanwins(ScreenInfo *s)
201 {
202 	unsigned int i, nwins;
203 	Client *c;
204 	Window dw1, dw2, *wins;
205 	XWindowAttributes attr;
206 
207 	XQueryTree(dpy, s->root, &dw1, &dw2, &wins, &nwins);
208 	for(i = 0; i < nwins; i++){
209 		XGetWindowAttributes(dpy, wins[i], &attr);
210 		if(attr.override_redirect || wins[i] == s->menuwin)
211 			continue;
212 		c = getclient(wins[i], 1);
213 		if(c != 0 && c->window == wins[i] && !c->init){
214 			c->x = attr.x;
215 			c->y = attr.y;
216 			c->dx = attr.width;
217 			c->dy = attr.height;
218 			c->border = attr.border_width;
219 			c->screen = s;
220 			c->parent = s->root;
221 			if(attr.map_state == IsViewable)
222 				manage(c, 1);
223 		}
224 	}
225 	XFree((void *) wins);	/* cast is to shut stoopid compiler up */
226 }
227 
228 void
gettrans(Client * c)229 gettrans(Client *c)
230 {
231 	Window trans;
232 
233 	trans = None;
234 	if(XGetTransientForHint(dpy, c->window, &trans) != 0)
235 		c->trans = trans;
236 	else
237 		c->trans = None;
238 }
239 
240 void
withdraw(Client * c)241 withdraw(Client *c)
242 {
243 	XUnmapWindow(dpy, c->parent);
244 	XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y);
245 	XRemoveFromSaveSet(dpy, c->window);
246 	setstate(c, WithdrawnState);
247 
248 	/* flush any errors */
249 	ignore_badwindow = 1;
250 	XSync(dpy, False);
251 	ignore_badwindow = 0;
252 }
253 
254 static void
installcmap(ScreenInfo * s,Colormap cmap)255 installcmap(ScreenInfo *s, Colormap cmap)
256 {
257 	if(cmap == None)
258 		XInstallColormap(dpy, s->def_cmap);
259 	else
260 		XInstallColormap(dpy, cmap);
261 }
262 
263 void
cmapfocus(Client * c)264 cmapfocus(Client *c)
265 {
266 	int i, found;
267 	Client *cc;
268 
269 	if(c == 0)
270 		return;
271 	else if(c->ncmapwins != 0){
272 		found = 0;
273 		for(i = c->ncmapwins-1; i >= 0; i--){
274 			installcmap(c->screen, c->wmcmaps[i]);
275 			if(c->cmapwins[i] == c->window)
276 				found++;
277 		}
278 		if(!found)
279 			installcmap(c->screen, c->cmap);
280 	}
281 	else if(c->trans != None && (cc = getclient(c->trans, 0)) != 0 && cc->ncmapwins != 0)
282 		cmapfocus(cc);
283 	else
284 		installcmap(c->screen, c->cmap);
285 }
286 
287 void
cmapnofocus(ScreenInfo * s)288 cmapnofocus(ScreenInfo *s)
289 {
290 	installcmap(s, None);
291 }
292 
293 void
getcmaps(Client * c)294 getcmaps(Client *c)
295 {
296 	int n, i;
297 	Window *cw;
298 	XWindowAttributes attr;
299 
300 	if(!c->init){
301 		ignore_badwindow = 1;
302 		XGetWindowAttributes(dpy, c->window, &attr);
303 		c->cmap = attr.colormap;
304 		XSync(dpy, False);
305 		ignore_badwindow = 0;
306 	}
307 
308 	n = _getprop(c->window, wm_colormaps, XA_WINDOW, 100L, (void*)&cw);
309 	if(c->ncmapwins != 0){
310 		XFree((char *)c->cmapwins);
311 		free((char *)c->wmcmaps);
312 	}
313 	if(n <= 0){
314 		c->ncmapwins = 0;
315 		return;
316 	}
317 
318 	c->ncmapwins = n;
319 	c->cmapwins = cw;
320 
321 	c->wmcmaps = (Colormap*)malloc(n*sizeof(Colormap));
322 	for(i = 0; i < n; i++){
323 		if(cw[i] == c->window)
324 			c->wmcmaps[i] = c->cmap;
325 		else {
326 			/* flush any errors (e.g., caused by mozilla tabs) */
327 			ignore_badwindow = 1;
328 			XSelectInput(dpy, cw[i], ColormapChangeMask);
329 			XGetWindowAttributes(dpy, cw[i], &attr);
330 			c->wmcmaps[i] = attr.colormap;
331 			XSync(dpy, False);
332 			ignore_badwindow = 0;
333 		}
334 	}
335 }
336 
337 void
setlabel(Client * c)338 setlabel(Client *c)
339 {
340 	char *label, *p;
341 
342 	if(c->iconname != 0)
343 		label = c->iconname;
344 	else if(c->name != 0)
345 		label = c->name;
346 	else if(c->instance != 0)
347 		label = c->instance;
348 	else if(c->class != 0)
349 		label = c->class;
350 	else
351 		label = "no label";
352 	if((p = index(label, ':')) != 0)
353 		*p = '\0';
354 	c->label = label;
355 }
356 
357 #ifdef	SHAPE
358 void
setshape(Client * c)359 setshape(Client *c)
360 {
361 	int n, order;
362 	XRectangle *rect;
363 
364 	/* don't try to add a border if the window is non-rectangular */
365 	rect = XShapeGetRectangles(dpy, c->window, ShapeBounding, &n, &order);
366 	if(n > 1)
367 		XShapeCombineShape(dpy, c->parent, ShapeBounding, BORDER, BORDER,
368 			c->window, ShapeBounding, ShapeSet);
369 	XFree((void*)rect);
370 }
371 #endif
372 
373 int
_getprop(Window w,Atom a,Atom type,long len,unsigned char ** p)374 _getprop(Window w, Atom a, Atom type, long len, unsigned char **p)
375 {
376 	Atom real_type;
377 	int format;
378 	unsigned long n, extra;
379 	int status;
380 
381 	status = XGetWindowProperty(dpy, w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p);
382 	if(status != Success || *p == 0)
383 		return -1;
384 	if(n == 0)
385 		XFree((void*) *p);
386 	/* could check real_type, format, extra here... */
387 	return n;
388 }
389 
390 char *
getprop(Window w,Atom a)391 getprop(Window w, Atom a)
392 {
393 	unsigned char *p;
394 
395 	if(_getprop(w, a, XA_STRING, 100L, &p) <= 0)
396 		return 0;
397 	return (char *)p;
398 }
399 
400 int
get1prop(Window w,Atom a,Atom type)401 get1prop(Window w, Atom a, Atom type)
402 {
403 	char **p, *x;
404 
405 	if(_getprop(w, a, type, 1L, (void*)&p) <= 0)
406 		return 0;
407 	x = *p;
408 	XFree((void*) p);
409 	return (int)(uintptr_t)x;
410 }
411 
412 Window
getwprop(Window w,Atom a)413 getwprop(Window w, Atom a)
414 {
415 	return get1prop(w, a, XA_WINDOW);
416 }
417 
418 int
getiprop(Window w,Atom a)419 getiprop(Window w, Atom a)
420 {
421 	return get1prop(w, a, XA_INTEGER);
422 }
423 
424 void
setstate(Client * c,int state)425 setstate(Client *c, int state)
426 {
427 	long data[2];
428 
429 	data[0] = (long) state;
430 	data[1] = (long) None;
431 
432 	c->state = state;
433 	XChangeProperty(dpy, c->window, wm_state, wm_state, 32,
434 		PropModeReplace, (unsigned char *)data, 2);
435 }
436 
437 int
getstate(Window w,int * state)438 getstate(Window w, int *state)
439 {
440 	long *p = 0;
441 
442 	if(_getprop(w, wm_state, wm_state, 2L, (void*)&p) <= 0)
443 		return 0;
444 
445 	*state = (int) *p;
446 	XFree((char *) p);
447 	return 1;
448 }
449 
450 void
getproto(Client * c)451 getproto(Client *c)
452 {
453 	Atom *p;
454 	int i;
455 	long n;
456 	Window w;
457 
458 	w = c->window;
459 	c->proto = 0;
460 	if((n = _getprop(w, wm_protocols, XA_ATOM, 20L, (void*)&p)) <= 0)
461 		return;
462 
463 	for(i = 0; i < n; i++)
464 		if(p[i] == wm_delete)
465 			c->proto |= Pdelete;
466 		else if(p[i] == wm_take_focus)
467 			c->proto |= Ptakefocus;
468 		else if(p[i] == wm_lose_focus)
469 			c->proto |= Plosefocus;
470 
471 	XFree((char *) p);
472 }
473