paulo@0: /* (C)opyright MMVI-MMVII Anselm R. Garbe paulo@0: * See LICENSE file for license details. paulo@0: */ paulo@0: #include "dwm.h" paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: paulo@0: /* static */ paulo@0: paulo@0: static void paulo@0: attachstack(Client *c) { paulo@0: c->snext = stack; paulo@0: stack = c; paulo@0: } paulo@0: paulo@0: static void paulo@0: detachstack(Client *c) { paulo@0: Client **tc; paulo@0: paulo@0: for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); paulo@0: *tc = c->snext; paulo@0: } paulo@0: paulo@0: static void paulo@0: grabbuttons(Client *c, Bool focused) { paulo@0: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); paulo@0: paulo@0: if(focused) { paulo@0: XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: paulo@0: XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: paulo@0: XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: } paulo@0: else paulo@0: XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, paulo@0: GrabModeAsync, GrabModeSync, None, None); paulo@0: } paulo@0: paulo@0: static Bool paulo@0: isprotodel(Client *c) { paulo@0: int i, n; paulo@0: Atom *protocols; paulo@0: Bool ret = False; paulo@0: paulo@0: if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { paulo@0: for(i = 0; !ret && i < n; i++) paulo@0: if(protocols[i] == wmatom[WMDelete]) paulo@0: ret = True; paulo@0: XFree(protocols); paulo@0: } paulo@0: return ret; paulo@0: } paulo@0: paulo@0: static void paulo@0: setclientstate(Client *c, long state) { paulo@0: long data[] = {state, None}; paulo@0: paulo@0: XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, paulo@0: PropModeReplace, (unsigned char *)data, 2); paulo@0: } paulo@0: paulo@0: static void paulo@0: togglemax(Client *c) { paulo@0: XEvent ev; paulo@0: paulo@0: if(c->isfixed) paulo@0: return; paulo@0: if((c->ismax = !c->ismax)) { paulo@0: c->rx = c->x; paulo@0: c->ry = c->y; paulo@0: c->rw = c->w; paulo@0: c->rh = c->h; paulo@0: resize(c, wax, way, waw - 2 * BORDERPX, wah - 2 * BORDERPX, True); paulo@0: } paulo@0: else paulo@0: resize(c, c->rx, c->ry, c->rw, c->rh, True); paulo@0: while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); paulo@0: } paulo@0: paulo@0: static int paulo@0: xerrordummy(Display *dsply, XErrorEvent *ee) { paulo@0: return 0; paulo@0: } paulo@0: paulo@0: /* extern */ paulo@0: paulo@0: void paulo@0: attach(Client *c) { paulo@0: if(clients) paulo@0: clients->prev = c; paulo@0: c->next = clients; paulo@0: clients = c; paulo@0: } paulo@0: paulo@0: void paulo@0: configure(Client *c) { paulo@0: XConfigureEvent ce; paulo@0: paulo@0: ce.type = ConfigureNotify; paulo@0: ce.display = dpy; paulo@0: ce.event = c->win; paulo@0: ce.window = c->win; paulo@0: ce.x = c->x; paulo@0: ce.y = c->y; paulo@0: ce.width = c->w; paulo@0: ce.height = c->h; paulo@0: ce.border_width = c->border; paulo@0: ce.above = None; paulo@0: ce.override_redirect = False; paulo@0: XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); paulo@0: } paulo@0: paulo@0: void paulo@0: detach(Client *c) { paulo@0: if(c->prev) paulo@0: c->prev->next = c->next; paulo@0: if(c->next) paulo@0: c->next->prev = c->prev; paulo@0: if(c == clients) paulo@0: clients = c->next; paulo@0: c->next = c->prev = NULL; paulo@0: } paulo@0: paulo@0: void paulo@0: focus(Client *c) { paulo@0: if(c && !isvisible(c)) paulo@0: return; paulo@0: if(sel && sel != c) { paulo@0: grabbuttons(sel, False); paulo@0: XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); paulo@0: } paulo@0: if(c) { paulo@0: detachstack(c); paulo@0: attachstack(c); paulo@0: grabbuttons(c, True); paulo@0: } paulo@0: sel = c; paulo@0: drawstatus(); paulo@0: if(!selscreen) paulo@0: return; paulo@0: if(c) { paulo@0: XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); paulo@0: XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); paulo@0: } paulo@0: else paulo@0: XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); paulo@0: } paulo@0: paulo@0: void paulo@0: killclient(Arg *arg) { paulo@0: if(!sel) paulo@0: return; paulo@0: if(isprotodel(sel)) paulo@0: sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); paulo@0: else paulo@0: XKillClient(dpy, sel->win); paulo@0: } paulo@0: paulo@0: void paulo@0: manage(Window w, XWindowAttributes *wa) { paulo@0: Client *c, *t; paulo@0: Window trans; paulo@0: XWindowChanges wc; paulo@0: paulo@0: c = emallocz(sizeof(Client)); paulo@0: c->tags = emallocz(ntags * sizeof(Bool)); paulo@0: c->win = w; paulo@0: c->x = wa->x; paulo@0: c->y = wa->y; paulo@0: c->w = wa->width; paulo@0: c->h = wa->height; paulo@0: if(c->w == sw && c->h == sh) { paulo@0: c->border = 0; paulo@0: c->x = sx; paulo@0: c->y = sy; paulo@0: } paulo@0: else { paulo@0: c->border = BORDERPX; paulo@0: if(c->x > wax + waw && c-> w < waw) paulo@0: c->x = wax + waw - c->w - 2 * c->border; paulo@0: if(c->y > way + wah && c-> h < wah) paulo@0: c->y = way + wah - c->h - 2 * c->border; paulo@0: if(c->x < wax && c->w < waw) paulo@0: c->x = wax; paulo@0: if(c->y < way && c->h < wah) paulo@0: c->y = way; paulo@0: } paulo@0: XSelectInput(dpy, w, paulo@0: StructureNotifyMask | PropertyChangeMask | EnterWindowMask); paulo@0: XGetTransientForHint(dpy, w, &trans); paulo@0: grabbuttons(c, False); paulo@0: wc.border_width = c->border; paulo@0: XConfigureWindow(dpy, w, CWBorderWidth, &wc); paulo@0: XSetWindowBorder(dpy, w, dc.norm[ColBorder]); paulo@0: configure(c); /* propagates border_width, if size doesn't change */ paulo@0: updatetitle(c); paulo@0: for(t = clients; t && t->win != trans; t = t->next); paulo@0: settags(c, t); paulo@0: if(!c->isversatile) paulo@0: c->isversatile = (t != NULL) || c->isfixed; paulo@0: attach(c); paulo@0: attachstack(c); paulo@0: c->isbanned = True; paulo@0: XMoveWindow(dpy, w, c->x + 2 * sw, c->y); paulo@0: XMapWindow(dpy, w); paulo@0: setclientstate(c, NormalState); paulo@0: if(isvisible(c)) paulo@0: focus(c); paulo@0: updatesizehints(c); paulo@0: lt->arrange(); paulo@0: } paulo@0: paulo@0: void paulo@0: resize(Client *c, int x, int y, int w, int h, Bool sizehints) { paulo@0: float actual, dx, dy, max, min; paulo@0: XWindowChanges wc; paulo@0: paulo@0: if(w <= 0 || h <= 0) paulo@0: return; paulo@0: if(sizehints) { paulo@0: if(c->minw && w < c->minw) paulo@0: w = c->minw; paulo@0: if(c->minh && h < c->minh) paulo@0: h = c->minh; paulo@0: if(c->maxw && w > c->maxw) paulo@0: w = c->maxw; paulo@0: if(c->maxh && h > c->maxh) paulo@0: h = c->maxh; paulo@0: /* inspired by algorithm from fluxbox */ paulo@0: if(c->minay > 0 && c->maxay && (h - c->baseh) > 0) { paulo@0: dx = (float)(w - c->basew); paulo@0: dy = (float)(h - c->baseh); paulo@0: min = (float)(c->minax) / (float)(c->minay); paulo@0: max = (float)(c->maxax) / (float)(c->maxay); paulo@0: actual = dx / dy; paulo@0: if(max > 0 && min > 0 && actual > 0) { paulo@0: if(actual < min) { paulo@0: dy = (dx * min + dy) / (min * min + 1); paulo@0: dx = dy * min; paulo@0: w = (int)dx + c->basew; paulo@0: h = (int)dy + c->baseh; paulo@0: } paulo@0: else if(actual > max) { paulo@0: dy = (dx * min + dy) / (max * max + 1); paulo@0: dx = dy * min; paulo@0: w = (int)dx + c->basew; paulo@0: h = (int)dy + c->baseh; paulo@0: } paulo@0: } paulo@0: } paulo@0: if(c->incw) paulo@0: w -= (w - c->basew) % c->incw; paulo@0: if(c->inch) paulo@0: h -= (h - c->baseh) % c->inch; paulo@0: } paulo@0: if(w == sw && h == sh) paulo@0: c->border = 0; paulo@0: else paulo@0: c->border = BORDERPX; paulo@0: /* offscreen appearance fixes */ paulo@0: if(x > sw) paulo@0: x = sw - w - 2 * c->border; paulo@0: if(y > sh) paulo@0: y = sh - h - 2 * c->border; paulo@0: if(x + w + 2 * c->border < sx) paulo@0: x = sx; paulo@0: if(y + h + 2 * c->border < sy) paulo@0: y = sy; paulo@0: /* if(c->x != x || c->y != y || c->w != w || c->h != h) { */ paulo@0: c->x = wc.x = x; paulo@0: c->y = wc.y = y; paulo@0: c->w = wc.width = w; paulo@0: c->h = wc.height = h; paulo@0: wc.border_width = c->border; paulo@0: XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); paulo@0: configure(c); paulo@0: XSync(dpy, False); paulo@0: /* } */ paulo@0: } paulo@0: paulo@0: void paulo@0: toggleversatile(Arg *arg) { paulo@0: if(!sel || lt->arrange == versatile) paulo@0: return; paulo@0: sel->isversatile = !sel->isversatile; paulo@0: lt->arrange(); paulo@0: } paulo@0: paulo@0: void paulo@0: updatesizehints(Client *c) { paulo@0: long msize; paulo@0: XSizeHints size; paulo@0: paulo@0: if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) paulo@0: size.flags = PSize; paulo@0: c->flags = size.flags; paulo@0: if(c->flags & PBaseSize) { paulo@0: c->basew = size.base_width; paulo@0: c->baseh = size.base_height; paulo@0: } paulo@0: else paulo@0: c->basew = c->baseh = 0; paulo@0: if(c->flags & PResizeInc) { paulo@0: c->incw = size.width_inc; paulo@0: c->inch = size.height_inc; paulo@0: } paulo@0: else paulo@0: c->incw = c->inch = 0; paulo@0: if(c->flags & PMaxSize) { paulo@0: c->maxw = size.max_width; paulo@0: c->maxh = size.max_height; paulo@0: } paulo@0: else paulo@0: c->maxw = c->maxh = 0; paulo@0: if(c->flags & PMinSize) { paulo@0: c->minw = size.min_width; paulo@0: c->minh = size.min_height; paulo@0: } paulo@0: else paulo@0: c->minw = c->minh = 0; paulo@0: if(c->flags & PAspect) { paulo@0: c->minax = size.min_aspect.x; paulo@0: c->minay = size.min_aspect.y; paulo@0: c->maxax = size.max_aspect.x; paulo@0: c->maxay = size.max_aspect.y; paulo@0: } paulo@0: else paulo@0: c->minax = c->minay = c->maxax = c->maxay = 0; paulo@0: c->isfixed = (c->maxw && c->minw && c->maxh && c->minh paulo@0: && c->maxw == c->minw && c->maxh == c->minh); paulo@0: } paulo@0: paulo@0: void paulo@0: updatetitle(Client *c) { paulo@0: char **list = NULL; paulo@0: int n; paulo@0: XTextProperty name; paulo@0: paulo@0: name.nitems = 0; paulo@0: c->name[0] = 0; paulo@0: XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); paulo@0: if(!name.nitems) paulo@0: XGetWMName(dpy, c->win, &name); paulo@0: if(!name.nitems) paulo@0: return; paulo@0: if(name.encoding == XA_STRING) paulo@0: strncpy(c->name, (char *)name.value, sizeof c->name); paulo@0: else { paulo@0: if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success paulo@0: && n > 0 && *list) paulo@0: { paulo@0: strncpy(c->name, *list, sizeof c->name); paulo@0: XFreeStringList(list); paulo@0: } paulo@0: } paulo@0: XFree(name.value); paulo@0: } paulo@0: paulo@0: void paulo@0: unmanage(Client *c) { paulo@0: Client *nc; paulo@0: paulo@0: /* The server grab construct avoids race conditions. */ paulo@0: XGrabServer(dpy); paulo@0: XSetErrorHandler(xerrordummy); paulo@0: detach(c); paulo@0: detachstack(c); paulo@0: if(sel == c) { paulo@0: for(nc = stack; nc && !isvisible(nc); nc = nc->snext); paulo@0: focus(nc); paulo@0: } paulo@0: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); paulo@0: setclientstate(c, WithdrawnState); paulo@0: free(c->tags); paulo@0: free(c); paulo@0: XSync(dpy, False); paulo@0: XSetErrorHandler(xerror); paulo@0: XUngrabServer(dpy); paulo@0: lt->arrange(); paulo@0: } paulo@0: paulo@0: void paulo@0: zoom(Arg *arg) { paulo@0: unsigned int n; paulo@0: Client *c; paulo@0: paulo@0: if(!sel) paulo@0: return; paulo@0: if(sel->isversatile || (lt->arrange == versatile)) { paulo@0: togglemax(sel); paulo@0: return; paulo@0: } paulo@0: for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) paulo@0: n++; paulo@0: if((c = sel) == nexttiled(clients)) paulo@0: if(!(c = nexttiled(c->next))) paulo@0: return; paulo@0: detach(c); paulo@0: attach(c); paulo@0: focus(c); paulo@0: lt->arrange(); paulo@0: } paulo@0: paulo@0: Client * paulo@0: prevtiled(Client *c) { paulo@0: for(; c && (c->isversatile || !isvisible(c)); c = c->prev); paulo@0: return c; paulo@0: } paulo@0: paulo@0: void paulo@0: pushup(Arg *arg) { paulo@0: Client *c; paulo@0: paulo@0: if(!sel || sel->isversatile) paulo@0: return; paulo@0: if((c = prevtiled(sel->prev))) { paulo@0: /* attach before c */ paulo@0: detach(sel); paulo@0: sel->next = c; paulo@0: sel->prev = c->prev; paulo@0: c->prev = sel; paulo@0: if(sel->prev) paulo@0: sel->prev->next = sel; paulo@0: else paulo@0: clients = sel; paulo@0: } else { paulo@0: /* move to the end */ paulo@0: for(c = sel; c->next; c = c->next); paulo@0: detach(sel); paulo@0: sel->prev = c; paulo@0: c->next = sel; paulo@0: } paulo@0: focus(sel); paulo@0: lt->arrange(); paulo@0: } paulo@0: paulo@0: void paulo@0: pushdown(Arg *arg) { paulo@0: Client *c; paulo@0: paulo@0: if(!sel || sel->isversatile) paulo@0: return; paulo@0: if((c = nexttiled(sel->next))) { paulo@0: /* attach after c */ paulo@0: detach(sel); paulo@0: sel->prev = c; paulo@0: sel->next = c->next; paulo@0: c->next = sel; paulo@0: if(sel->next) paulo@0: sel->next->prev = sel; paulo@0: } else { paulo@0: /* move to the front */ paulo@0: detach(sel); paulo@0: attach(sel); paulo@0: } paulo@0: focus(sel); paulo@0: lt->arrange(); paulo@0: } paulo@0: paulo@0: void paulo@0: moveresize(Arg *arg) { paulo@0: int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh; paulo@0: char xAbs, yAbs, wAbs, hAbs; paulo@0: int mx, my, dx, dy, nmx, nmy; paulo@0: unsigned int dui; paulo@0: Window dummy; paulo@0: paulo@0: if (lt->arrange != versatile) paulo@0: if (!sel || !sel->isversatile || !arg) paulo@0: return; paulo@0: if(sscanf(arg->cmd, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8) paulo@0: return; paulo@0: nx = xAbs == 'X' ? x : sel->x + x; paulo@0: ny = yAbs == 'Y' ? y : sel->y + y; paulo@0: nw = wAbs == 'W' ? w : sel->w + w; paulo@0: nh = hAbs == 'H' ? h : sel->h + h; paulo@0: paulo@0: ox = sel->x; paulo@0: oy = sel->y; paulo@0: ow = sel->w; paulo@0: oh = sel->h; paulo@0: paulo@0: Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &mx, &my, &dx, &dy, &dui); paulo@0: resize(sel, nx, ny, nw, nh, True); paulo@0: if (xqp && ox <= mx && (ox + ow) >= mx && oy <= my && (oy + oh) >= my) paulo@0: { paulo@0: nmx = mx-ox+sel->w-ow-1 < 0 ? 0 : mx-ox+sel->w-ow-1; paulo@0: nmy = my-oy+sel->h-oh-1 < 0 ? 0 : my-oy+sel->h-oh-1; paulo@0: XWarpPointer(dpy, None, sel->win, 0, 0, 0, 0, nmx, nmy); paulo@0: } paulo@0: } paulo@0: