/* * Window management. */ /* * Copyright (c) 2019 Iris Lightshard, 2005 Russ Cox, 1994-1996 David Hogan * see README for licence details */ #include #include #include #include #include #include #include #include #include #include #include "config.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; XRRMonitorInfo monitor; ScreenInfo* screen; 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; #ifdef ALWAYSDRAW c->is9term = shouldalwaysdraw(c); #else c->is9term = 0; #endif if (isNew) { c->is9term = isterminalwindow(c); 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))) { screen = c->screen; XKillClient(dpy, c->window); rmclient(c); if (current && current->screen == 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); #ifdef AUTOSTICK if (!isautostick(c)) active(c); else setactive(c, 0); #else active(c); #endif setstate(c, NormalState); } if (current && (current != c)) cmapfocus(current); c->init = 1; /* If the window is out of bounds of the screen, try to wrangle it */ if (c->trans) { monitor = monitorinfo[getmonitorbyclient(c)]; } else { #ifdef MONITORFOLLOWSMOUSE monitor = monitorinfo[getmonitorbymouse()]; #else monitor = monitorinfo[getmonitorbyclient(current)]; #endif } wrangle(c, monitor); /* * 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; int 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 (i = 0, lc = label; *lc != '\0'; lc++, i++) { if (i >= 23) { 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); }