/* * Window management. */ /* * Copyright (c) 2019 Derek Stevens, 2005 Russ Cox, 1994-1996 David Hogan * see README for licence details */ #include #include #include #include #include #include #include #include #include #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") || strstr (c->class, "urxvt") || strstr (c->class, "URxvt"); 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, *lc, i; 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'; for (lc = label, i = 0; lc++, i++; *lc != 0){ if (i >= 24) { label[22] = '~'; label[23] = '\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); }