Blob


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