|  | /* | 
|  | * Window management. | 
|  | */ | 
|  |  | 
|  | /* Copyright (c) 1994-1996 David Hogan, see README for licence details */ | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <inttypes.h> | 
|  | #include <X11/X.h> | 
|  | #include <X11/Xos.h> | 
|  | #include <X11/Xlib.h> | 
|  | #include <X11/Xutil.h> | 
|  | #include <X11/Xatom.h> | 
|  | #include <X11/extensions/shape.h> | 
|  | #include "dat.h" | 
|  | #include "fns.h" | 
|  |  | 
|  | int isNew; | 
|  |  | 
|  | int | 
|  | manage(Client *c, int mapped) | 
|  | { | 
|  | int fixsize, dohide, doreshape, state; | 
|  | long msize; | 
|  | XClassHint class; | 
|  | XWMHints *hints; | 
|  | XSetWindowAttributes attrs; | 
|  |  | 
|  | trace("manage", c, 0); | 
|  | XSelectInput(dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask | FocusChangeMask | KeyPressMask); | 
|  |  | 
|  | /* Get loads of hints */ | 
|  |  | 
|  | if(XGetClassHint(dpy, c->window, &class) != 0){	/* ``Success'' */ | 
|  | c->instance = class.res_name; | 
|  | c->class = class.res_class; | 
|  | c->is9term = 0; | 
|  | if(isNew){ | 
|  | c->is9term = strstr(c->class, "term") || strstr(c->class, "Term"); | 
|  | isNew = 0; | 
|  | } | 
|  | } | 
|  | else { | 
|  | c->instance = 0; | 
|  | c->class = 0; | 
|  | c->is9term = 0; | 
|  | } | 
|  | c->iconname = getprop(c->window, XA_WM_ICON_NAME); | 
|  | c->name = getprop(c->window, XA_WM_NAME); | 
|  | setlabel(c); | 
|  |  | 
|  | hints = XGetWMHints(dpy, c->window); | 
|  | if(XGetWMNormalHints(dpy, c->window, &c->size, &msize) == 0 || c->size.flags == 0) | 
|  | c->size.flags = PSize;		/* not specified - punt */ | 
|  |  | 
|  | getcmaps(c); | 
|  | getproto(c); | 
|  | gettrans(c); | 
|  | if(c->is9term) | 
|  | c->hold = getiprop(c->window, _rio_hold_mode); | 
|  |  | 
|  | /* Figure out what to do with the window from hints */ | 
|  |  | 
|  | if(!getstate(c->window, &state)) | 
|  | state = hints ? hints->initial_state : NormalState; | 
|  | dohide = (state == IconicState); | 
|  |  | 
|  | fixsize = 0; | 
|  | if((c->size.flags & (USSize|PSize))) | 
|  | fixsize = 1; | 
|  | if((c->size.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize) && c->size.min_width == c->size.max_width && c->size.min_height == c->size.max_height) | 
|  | fixsize = 1; | 
|  | doreshape = !mapped; | 
|  | if(fixsize){ | 
|  | if(c->size.flags & USPosition) | 
|  | doreshape = 0; | 
|  | if(dohide && (c->size.flags & PPosition)) | 
|  | doreshape = 0; | 
|  | if(c->trans != None) | 
|  | doreshape = 0; | 
|  | } | 
|  | if(c->is9term) | 
|  | fixsize = 0; | 
|  | if(c->size.flags & PBaseSize){ | 
|  | c->min_dx = c->size.base_width; | 
|  | c->min_dy = c->size.base_height; | 
|  | } | 
|  | else if(c->size.flags & PMinSize){ | 
|  | c->min_dx = c->size.min_width; | 
|  | c->min_dy = c->size.min_height; | 
|  | } | 
|  | else if(c->is9term){ | 
|  | c->min_dx = 100; | 
|  | c->min_dy = 50; | 
|  | } | 
|  | else | 
|  | c->min_dx = c->min_dy = 0; | 
|  |  | 
|  | if(hints) | 
|  | XFree(hints); | 
|  |  | 
|  | /* Now do it!!! */ | 
|  |  | 
|  | if(doreshape){ | 
|  | 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", | 
|  | c->is9term, fixsize, c->x, c->y, c->min_dx, c->min_dy, c->dx, c->dy); | 
|  | if(current && current->screen == c->screen) | 
|  | cmapnofocus(c->screen); | 
|  | if(!c->is9term && c->x==0 && c->y==0){ | 
|  | static int nwin; | 
|  |  | 
|  | c->x = 20*nwin+BORDER; | 
|  | c->y = 20*nwin+BORDER; | 
|  | nwin++; | 
|  | nwin %= 10; | 
|  | } | 
|  |  | 
|  | if(c->is9term && !(fixsize ? drag(c, Button3) : sweep(c, Button3))){ | 
|  | XKillClient(dpy, c->window); | 
|  | rmclient(c); | 
|  | if(current && current->screen == c->screen) | 
|  | cmapfocus(current); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | attrs.border_pixel =  c->screen->black; | 
|  | attrs.background_pixel =  c->screen->white; | 
|  | attrs.colormap = c->screen->def_cmap; | 
|  | c->parent = XCreateWindow(dpy, c->screen->root, | 
|  | c->x - BORDER, c->y - BORDER, | 
|  | c->dx + 2*BORDER, c->dy + 2*BORDER, | 
|  | 0, | 
|  | c->screen->depth, | 
|  | CopyFromParent, | 
|  | c->screen->vis, | 
|  | CWBackPixel | CWBorderPixel | CWColormap, | 
|  | &attrs); | 
|  |  | 
|  | XSelectInput(dpy, c->parent, SubstructureRedirectMask | SubstructureNotifyMask|ButtonPressMask| PointerMotionMask|LeaveWindowMask|KeyPressMask); | 
|  | if(mapped) | 
|  | c->reparenting = 1; | 
|  | if(doreshape && !fixsize) | 
|  | XResizeWindow(dpy, c->window, c->dx, c->dy); | 
|  | XSetWindowBorderWidth(dpy, c->window, 0); | 
|  |  | 
|  | /* | 
|  | * To have something more than only a big white or black border | 
|  | * XXX should replace this by a pattern in the white or black | 
|  | * such that we can see the border also if all our | 
|  | * windows are black and/or white | 
|  | * (black (or white)  border around black (or white) window | 
|  | *  is not very helpful. | 
|  | */ | 
|  | if(c->screen->depth <= 8){ | 
|  | XSetWindowBorderWidth(dpy, c->parent, 1); | 
|  | } | 
|  |  | 
|  | XReparentWindow(dpy, c->window, c->parent, BORDER, BORDER); | 
|  | #ifdef	SHAPE | 
|  | if(shape){ | 
|  | XShapeSelectInput(dpy, c->window, ShapeNotifyMask); | 
|  | ignore_badwindow = 1;		/* magic */ | 
|  | setshape(c); | 
|  | ignore_badwindow = 0; | 
|  | } | 
|  | #endif | 
|  | XAddToSaveSet(dpy, c->window); | 
|  | if(dohide) | 
|  | hide(c); | 
|  | else { | 
|  | XMapWindow(dpy, c->window); | 
|  | XMapWindow(dpy, c->parent); | 
|  | XUnmapWindow(dpy, c->screen->sweepwin); | 
|  | if(nostalgia || doreshape) | 
|  | active(c); | 
|  | else if(c->trans != None && current && current->window == c->trans) | 
|  | active(c); | 
|  | else | 
|  | setactive(c, 0); | 
|  | setstate(c, NormalState); | 
|  | } | 
|  | if(current && (current != c)) | 
|  | cmapfocus(current); | 
|  | c->init = 1; | 
|  |  | 
|  | /* | 
|  | * If we swept the window, let's send a resize event to the | 
|  | * guy who just got resized.  It's not clear whether the apps | 
|  | * should notice their new size via other means.  Try as I might, | 
|  | * I can't find a way to have them notice during initdraw, so | 
|  | * I solve the problem this way instead.		-rsc | 
|  | */ | 
|  | if(c->is9term) | 
|  | sendconfig(c); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | scanwins(ScreenInfo *s) | 
|  | { | 
|  | unsigned int i, nwins; | 
|  | Client *c; | 
|  | Window dw1, dw2, *wins; | 
|  | XWindowAttributes attr; | 
|  |  | 
|  | XQueryTree(dpy, s->root, &dw1, &dw2, &wins, &nwins); | 
|  | for(i = 0; i < nwins; i++){ | 
|  | XGetWindowAttributes(dpy, wins[i], &attr); | 
|  | if(attr.override_redirect || wins[i] == s->menuwin) | 
|  | continue; | 
|  | c = getclient(wins[i], 1); | 
|  | if(c != 0 && c->window == wins[i] && !c->init){ | 
|  | c->x = attr.x; | 
|  | c->y = attr.y; | 
|  | c->dx = attr.width; | 
|  | c->dy = attr.height; | 
|  | c->border = attr.border_width; | 
|  | c->screen = s; | 
|  | c->parent = s->root; | 
|  | if(attr.map_state == IsViewable) | 
|  | manage(c, 1); | 
|  | } | 
|  | } | 
|  | XFree((void *) wins);	/* cast is to shut stoopid compiler up */ | 
|  | } | 
|  |  | 
|  | void | 
|  | gettrans(Client *c) | 
|  | { | 
|  | Window trans; | 
|  |  | 
|  | trans = None; | 
|  | if(XGetTransientForHint(dpy, c->window, &trans) != 0) | 
|  | c->trans = trans; | 
|  | else | 
|  | c->trans = None; | 
|  | } | 
|  |  | 
|  | void | 
|  | withdraw(Client *c) | 
|  | { | 
|  | XUnmapWindow(dpy, c->parent); | 
|  | XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y); | 
|  | XRemoveFromSaveSet(dpy, c->window); | 
|  | setstate(c, WithdrawnState); | 
|  |  | 
|  | /* flush any errors */ | 
|  | ignore_badwindow = 1; | 
|  | XSync(dpy, False); | 
|  | ignore_badwindow = 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | installcmap(ScreenInfo *s, Colormap cmap) | 
|  | { | 
|  | if(cmap == None) | 
|  | XInstallColormap(dpy, s->def_cmap); | 
|  | else | 
|  | XInstallColormap(dpy, cmap); | 
|  | } | 
|  |  | 
|  | void | 
|  | cmapfocus(Client *c) | 
|  | { | 
|  | int i, found; | 
|  | Client *cc; | 
|  |  | 
|  | if(c == 0) | 
|  | return; | 
|  | else if(c->ncmapwins != 0){ | 
|  | found = 0; | 
|  | for(i = c->ncmapwins-1; i >= 0; i--){ | 
|  | installcmap(c->screen, c->wmcmaps[i]); | 
|  | if(c->cmapwins[i] == c->window) | 
|  | found++; | 
|  | } | 
|  | if(!found) | 
|  | installcmap(c->screen, c->cmap); | 
|  | } | 
|  | else if(c->trans != None && (cc = getclient(c->trans, 0)) != 0 && cc->ncmapwins != 0) | 
|  | cmapfocus(cc); | 
|  | else | 
|  | installcmap(c->screen, c->cmap); | 
|  | } | 
|  |  | 
|  | void | 
|  | cmapnofocus(ScreenInfo *s) | 
|  | { | 
|  | installcmap(s, None); | 
|  | } | 
|  |  | 
|  | void | 
|  | getcmaps(Client *c) | 
|  | { | 
|  | int n, i; | 
|  | Window *cw; | 
|  | XWindowAttributes attr; | 
|  |  | 
|  | if(!c->init){ | 
|  | ignore_badwindow = 1; | 
|  | XGetWindowAttributes(dpy, c->window, &attr); | 
|  | c->cmap = attr.colormap; | 
|  | XSync(dpy, False); | 
|  | ignore_badwindow = 0; | 
|  | } | 
|  |  | 
|  | n = _getprop(c->window, wm_colormaps, XA_WINDOW, 100L, (void*)&cw); | 
|  | if(c->ncmapwins != 0){ | 
|  | XFree((char *)c->cmapwins); | 
|  | free((char *)c->wmcmaps); | 
|  | } | 
|  | if(n <= 0){ | 
|  | c->ncmapwins = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | c->ncmapwins = n; | 
|  | c->cmapwins = cw; | 
|  |  | 
|  | c->wmcmaps = (Colormap*)malloc(n*sizeof(Colormap)); | 
|  | for(i = 0; i < n; i++){ | 
|  | if(cw[i] == c->window) | 
|  | c->wmcmaps[i] = c->cmap; | 
|  | else { | 
|  | /* flush any errors (e.g., caused by mozilla tabs) */ | 
|  | ignore_badwindow = 1; | 
|  | XSelectInput(dpy, cw[i], ColormapChangeMask); | 
|  | XGetWindowAttributes(dpy, cw[i], &attr); | 
|  | c->wmcmaps[i] = attr.colormap; | 
|  | XSync(dpy, False); | 
|  | ignore_badwindow = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | setlabel(Client *c) | 
|  | { | 
|  | char *label, *p; | 
|  |  | 
|  | if(c->iconname != 0) | 
|  | label = c->iconname; | 
|  | else if(c->name != 0) | 
|  | label = c->name; | 
|  | else if(c->instance != 0) | 
|  | label = c->instance; | 
|  | else if(c->class != 0) | 
|  | label = c->class; | 
|  | else | 
|  | label = "no label"; | 
|  | if((p = index(label, ':')) != 0) | 
|  | *p = '\0'; | 
|  | c->label = label; | 
|  | } | 
|  |  | 
|  | #ifdef	SHAPE | 
|  | void | 
|  | setshape(Client *c) | 
|  | { | 
|  | int n, order; | 
|  | XRectangle *rect; | 
|  |  | 
|  | /* don't try to add a border if the window is non-rectangular */ | 
|  | rect = XShapeGetRectangles(dpy, c->window, ShapeBounding, &n, &order); | 
|  | if(n > 1) | 
|  | XShapeCombineShape(dpy, c->parent, ShapeBounding, BORDER, BORDER, | 
|  | c->window, ShapeBounding, ShapeSet); | 
|  | XFree((void*)rect); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | int | 
|  | _getprop(Window w, Atom a, Atom type, long len, unsigned char **p) | 
|  | { | 
|  | Atom real_type; | 
|  | int format; | 
|  | unsigned long n, extra; | 
|  | int status; | 
|  |  | 
|  | status = XGetWindowProperty(dpy, w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p); | 
|  | if(status != Success || *p == 0) | 
|  | return -1; | 
|  | if(n == 0) | 
|  | XFree((void*) *p); | 
|  | /* could check real_type, format, extra here... */ | 
|  | return n; | 
|  | } | 
|  |  | 
|  | char * | 
|  | getprop(Window w, Atom a) | 
|  | { | 
|  | unsigned char *p; | 
|  |  | 
|  | if(_getprop(w, a, XA_STRING, 100L, &p) <= 0) | 
|  | return 0; | 
|  | return (char *)p; | 
|  | } | 
|  |  | 
|  | int | 
|  | get1prop(Window w, Atom a, Atom type) | 
|  | { | 
|  | char **p, *x; | 
|  |  | 
|  | if(_getprop(w, a, type, 1L, (void*)&p) <= 0) | 
|  | return 0; | 
|  | x = *p; | 
|  | XFree((void*) p); | 
|  | return (int)(uintptr_t)x; | 
|  | } | 
|  |  | 
|  | Window | 
|  | getwprop(Window w, Atom a) | 
|  | { | 
|  | return get1prop(w, a, XA_WINDOW); | 
|  | } | 
|  |  | 
|  | int | 
|  | getiprop(Window w, Atom a) | 
|  | { | 
|  | return get1prop(w, a, XA_INTEGER); | 
|  | } | 
|  |  | 
|  | void | 
|  | setstate(Client *c, int state) | 
|  | { | 
|  | long data[2]; | 
|  |  | 
|  | data[0] = (long) state; | 
|  | data[1] = (long) None; | 
|  |  | 
|  | c->state = state; | 
|  | XChangeProperty(dpy, c->window, wm_state, wm_state, 32, | 
|  | PropModeReplace, (unsigned char *)data, 2); | 
|  | } | 
|  |  | 
|  | int | 
|  | getstate(Window w, int *state) | 
|  | { | 
|  | long *p = 0; | 
|  |  | 
|  | if(_getprop(w, wm_state, wm_state, 2L, (void*)&p) <= 0) | 
|  | return 0; | 
|  |  | 
|  | *state = (int) *p; | 
|  | XFree((char *) p); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | getproto(Client *c) | 
|  | { | 
|  | Atom *p; | 
|  | int i; | 
|  | long n; | 
|  | Window w; | 
|  |  | 
|  | w = c->window; | 
|  | c->proto = 0; | 
|  | if((n = _getprop(w, wm_protocols, XA_ATOM, 20L, (void*)&p)) <= 0) | 
|  | return; | 
|  |  | 
|  | for(i = 0; i < n; i++) | 
|  | if(p[i] == wm_delete) | 
|  | c->proto |= Pdelete; | 
|  | else if(p[i] == wm_take_focus) | 
|  | c->proto |= Ptakefocus; | 
|  | else if(p[i] == wm_lose_focus) | 
|  | c->proto |= Plosefocus; | 
|  |  | 
|  | XFree((char *) p); | 
|  | } |