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 <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"
18 int isNew;
20 int
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;
29 trace("manage", c, 0);
30 XSelectInput(dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask | FocusChangeMask | KeyPressMask);
32 /* Get loads of hints */
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);
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 */
56 getcmaps(c);
57 getproto(c);
58 gettrans(c);
59 if(c->is9term)
60 c->hold = getiprop(c->window, _rio_hold_mode);
62 /* Figure out what to do with the window from hints */
64 if(!getstate(c->window, &state))
65 state = hints ? hints->initial_state : NormalState;
66 dohide = (state == IconicState);
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;
99 if(hints)
100 XFree(hints);
102 /* Now do it!!! */
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;
112 c->x = 20*nwin+BORDER;
113 c->y = 20*nwin+BORDER;
114 nwin++;
115 nwin %= 10;
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;
125 } else
126 gravitate(c, 0);
129 attrs.border_pixel = c->screen->black;
130 attrs.background_pixel = c->screen->white;
131 attrs.colormap = c->screen->def_cmap;
132 c->parent = XCreateWindow(dpy, c->screen->root,
133 c->x - BORDER, c->y - BORDER,
134 c->dx + 2*BORDER, c->dy + 2*BORDER,
135 0,
136 c->screen->depth,
137 CopyFromParent,
138 c->screen->vis,
139 CWBackPixel | CWBorderPixel | CWColormap,
140 &attrs);
142 XSelectInput(dpy, c->parent, SubstructureRedirectMask | SubstructureNotifyMask|ButtonPressMask| PointerMotionMask|LeaveWindowMask|KeyPressMask);
143 if(mapped)
144 c->reparenting = 1;
145 if(doreshape && !fixsize)
146 XResizeWindow(dpy, c->window, c->dx, c->dy);
147 XSetWindowBorderWidth(dpy, c->window, 0);
149 /*
150 * To have something more than only a big white or black border
151 * XXX should replace this by a pattern in the white or black
152 * such that we can see the border also if all our
153 * windows are black and/or white
154 * (black (or white) border around black (or white) window
155 * is not very helpful.
156 */
157 if(c->screen->depth <= 8){
158 XSetWindowBorderWidth(dpy, c->parent, 1);
161 XReparentWindow(dpy, c->window, c->parent, BORDER, BORDER);
162 #ifdef SHAPE
163 if(shape){
164 XShapeSelectInput(dpy, c->window, ShapeNotifyMask);
165 ignore_badwindow = 1; /* magic */
166 setshape(c);
167 ignore_badwindow = 0;
169 #endif
170 XAddToSaveSet(dpy, c->window);
171 if(dohide)
172 hide(c);
173 else {
174 XMapWindow(dpy, c->window);
175 XMapWindow(dpy, c->parent);
176 XUnmapWindow(dpy, c->screen->sweepwin);
177 if(nostalgia || doreshape)
178 active(c);
179 else if(c->trans != None && current && current->window == c->trans)
180 active(c);
181 else
182 setactive(c, 0);
183 setstate(c, NormalState);
185 if(current && (current != c))
186 cmapfocus(current);
187 c->init = 1;
189 /*
190 * If we swept the window, let's send a resize event to the
191 * guy who just got resized. It's not clear whether the apps
192 * should notice their new size via other means. Try as I might,
193 * I can't find a way to have them notice during initdraw, so
194 * I solve the problem this way instead. -rsc
195 */
196 if(c->is9term)
197 sendconfig(c);
198 return 1;
201 void
202 scanwins(ScreenInfo *s)
204 unsigned int i, nwins;
205 Client *c;
206 Window dw1, dw2, *wins;
207 XWindowAttributes attr;
209 XQueryTree(dpy, s->root, &dw1, &dw2, &wins, &nwins);
210 for(i = 0; i < nwins; i++){
211 XGetWindowAttributes(dpy, wins[i], &attr);
212 if(attr.override_redirect || wins[i] == s->menuwin)
213 continue;
214 c = getclient(wins[i], 1);
215 if(c != 0 && c->window == wins[i] && !c->init){
216 c->x = attr.x;
217 c->y = attr.y;
218 c->dx = attr.width;
219 c->dy = attr.height;
220 c->border = attr.border_width;
221 c->screen = s;
222 c->parent = s->root;
223 if(attr.map_state == IsViewable)
224 manage(c, 1);
227 XFree((void *) wins); /* cast is to shut stoopid compiler up */
230 void
231 gettrans(Client *c)
233 Window trans;
235 trans = None;
236 if(XGetTransientForHint(dpy, c->window, &trans) != 0)
237 c->trans = trans;
238 else
239 c->trans = None;
242 void
243 withdraw(Client *c)
245 XUnmapWindow(dpy, c->parent);
246 gravitate(c, 1);
247 XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y);
248 gravitate(c, 0);
249 XRemoveFromSaveSet(dpy, c->window);
250 setstate(c, WithdrawnState);
252 /* flush any errors */
253 ignore_badwindow = 1;
254 XSync(dpy, False);
255 ignore_badwindow = 0;
258 void
259 gravitate(Client *c, int invert)
261 int gravity, dx, dy, delta;
263 gravity = NorthWestGravity;
264 if(c->size.flags & PWinGravity)
265 gravity = c->size.win_gravity;
267 delta = c->border-BORDER;
268 switch (gravity){
269 case NorthWestGravity:
270 dx = 0;
271 dy = 0;
272 break;
273 case NorthGravity:
274 dx = delta;
275 dy = 0;
276 break;
277 case NorthEastGravity:
278 dx = 2*delta;
279 dy = 0;
280 break;
281 case WestGravity:
282 dx = 0;
283 dy = delta;
284 break;
285 case CenterGravity:
286 case StaticGravity:
287 dx = delta;
288 dy = delta;
289 break;
290 case EastGravity:
291 dx = 2*delta;
292 dy = delta;
293 break;
294 case SouthWestGravity:
295 dx = 0;
296 dy = 2*delta;
297 break;
298 case SouthGravity:
299 dx = delta;
300 dy = 2*delta;
301 break;
302 case SouthEastGravity:
303 dx = 2*delta;
304 dy = 2*delta;
305 break;
306 default:
307 fprintf(stderr, "rio: bad window gravity %d for 0x%x\n", gravity, (int)c->window);
308 return;
310 dx += BORDER;
311 dy += BORDER;
312 if(invert){
313 dx = -dx;
314 dy = -dy;
316 c->x += dx;
317 c->y += dy;
320 static void
321 installcmap(ScreenInfo *s, Colormap cmap)
323 if(cmap == None)
324 XInstallColormap(dpy, s->def_cmap);
325 else
326 XInstallColormap(dpy, cmap);
329 void
330 cmapfocus(Client *c)
332 int i, found;
333 Client *cc;
335 if(c == 0)
336 return;
337 else if(c->ncmapwins != 0){
338 found = 0;
339 for(i = c->ncmapwins-1; i >= 0; i--){
340 installcmap(c->screen, c->wmcmaps[i]);
341 if(c->cmapwins[i] == c->window)
342 found++;
344 if(!found)
345 installcmap(c->screen, c->cmap);
347 else if(c->trans != None && (cc = getclient(c->trans, 0)) != 0 && cc->ncmapwins != 0)
348 cmapfocus(cc);
349 else
350 installcmap(c->screen, c->cmap);
353 void
354 cmapnofocus(ScreenInfo *s)
356 installcmap(s, None);
359 void
360 getcmaps(Client *c)
362 int n, i;
363 Window *cw;
364 XWindowAttributes attr;
366 if(!c->init){
367 ignore_badwindow = 1;
368 XGetWindowAttributes(dpy, c->window, &attr);
369 c->cmap = attr.colormap;
370 XSync(dpy, False);
371 ignore_badwindow = 0;
374 n = _getprop(c->window, wm_colormaps, XA_WINDOW, 100L, (void*)&cw);
375 if(c->ncmapwins != 0){
376 XFree((char *)c->cmapwins);
377 free((char *)c->wmcmaps);
379 if(n <= 0){
380 c->ncmapwins = 0;
381 return;
384 c->ncmapwins = n;
385 c->cmapwins = cw;
387 c->wmcmaps = (Colormap*)malloc(n*sizeof(Colormap));
388 for(i = 0; i < n; i++){
389 if(cw[i] == c->window)
390 c->wmcmaps[i] = c->cmap;
391 else {
392 /* flush any errors (e.g., caused by mozilla tabs) */
393 ignore_badwindow = 1;
394 XSelectInput(dpy, cw[i], ColormapChangeMask);
395 XGetWindowAttributes(dpy, cw[i], &attr);
396 c->wmcmaps[i] = attr.colormap;
397 XSync(dpy, False);
398 ignore_badwindow = 0;
403 void
404 setlabel(Client *c)
406 char *label, *p;
408 if(c->iconname != 0)
409 label = c->iconname;
410 else if(c->name != 0)
411 label = c->name;
412 else if(c->instance != 0)
413 label = c->instance;
414 else if(c->class != 0)
415 label = c->class;
416 else
417 label = "no label";
418 if((p = index(label, ':')) != 0)
419 *p = '\0';
420 c->label = label;
423 #ifdef SHAPE
424 void
425 setshape(Client *c)
427 int n, order;
428 XRectangle *rect;
430 /* don't try to add a border if the window is non-rectangular */
431 rect = XShapeGetRectangles(dpy, c->window, ShapeBounding, &n, &order);
432 if(n > 1)
433 XShapeCombineShape(dpy, c->parent, ShapeBounding, BORDER, BORDER,
434 c->window, ShapeBounding, ShapeSet);
435 XFree((void*)rect);
437 #endif
439 int
440 _getprop(Window w, Atom a, Atom type, long len, unsigned char **p)
442 Atom real_type;
443 int format;
444 unsigned long n, extra;
445 int status;
447 status = XGetWindowProperty(dpy, w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p);
448 if(status != Success || *p == 0)
449 return -1;
450 if(n == 0)
451 XFree((void*) *p);
452 /* could check real_type, format, extra here... */
453 return n;
456 char *
457 getprop(Window w, Atom a)
459 unsigned char *p;
461 if(_getprop(w, a, XA_STRING, 100L, &p) <= 0)
462 return 0;
463 return (char *)p;
466 int
467 get1prop(Window w, Atom a, Atom type)
469 char **p, *x;
471 if(_getprop(w, a, type, 1L, (void*)&p) <= 0)
472 return 0;
473 x = *p;
474 XFree((void*) p);
475 return (int)(uintptr_t)x;
478 Window
479 getwprop(Window w, Atom a)
481 return get1prop(w, a, XA_WINDOW);
484 int
485 getiprop(Window w, Atom a)
487 return get1prop(w, a, XA_INTEGER);
490 void
491 setstate(Client *c, int state)
493 long data[2];
495 data[0] = (long) state;
496 data[1] = (long) None;
498 c->state = state;
499 XChangeProperty(dpy, c->window, wm_state, wm_state, 32,
500 PropModeReplace, (unsigned char *)data, 2);
503 int
504 getstate(Window w, int *state)
506 long *p = 0;
508 if(_getprop(w, wm_state, wm_state, 2L, (void*)&p) <= 0)
509 return 0;
511 *state = (int) *p;
512 XFree((char *) p);
513 return 1;
516 void
517 getproto(Client *c)
519 Atom *p;
520 int i;
521 long n;
522 Window w;
524 w = c->window;
525 c->proto = 0;
526 if((n = _getprop(w, wm_protocols, XA_ATOM, 20L, (void*)&p)) <= 0)
527 return;
529 for(i = 0; i < n; i++)
530 if(p[i] == wm_delete)
531 c->proto |= Pdelete;
532 else if(p[i] == wm_take_focus)
533 c->proto |= Ptakefocus;
534 else if(p[i] == wm_lose_focus)
535 c->proto |= Plosefocus;
537 XFree((char *) p);