arg@644: /* (C)opyright MMVI-MMVII Anselm R. Garbe garbeam@5: * See LICENSE file for license details. garbeam@5: */ garbeam@76: #include "dwm.h" garbeam@10: #include garbeam@5: #include garbeam@5: #include garbeam@32: #include garbeam@5: arg@730: /* static */ garbeam@50: garbeam@26: static void arg@461: detachstack(Client *c) { arg@446: Client **tc; arg@446: for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); arg@446: *tc = c->snext; arg@446: } arg@446: arg@446: static void arg@461: grabbuttons(Client *c, Bool focused) { arg@372: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); arg@372: arg@452: if(focused) { arg@372: XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: arg@372: XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: arg@372: XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@372: } arg@372: else arg@372: XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, arg@372: GrabModeAsync, GrabModeSync, None, None); arg@318: } arg@318: arg@725: static void arg@725: setclientstate(Client *c, long state) { arg@725: long data[] = {state, None}; arg@725: XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, arg@725: PropModeReplace, (unsigned char *)data, 2); arg@725: } arg@725: garbeam@76: static int arg@461: xerrordummy(Display *dsply, XErrorEvent *ee) { garbeam@76: return 0; garbeam@5: } garbeam@5: arg@730: /* extern */ garbeam@20: garbeam@20: void arg@491: configure(Client *c) { arg@491: XEvent synev; arg@491: arg@491: synev.type = ConfigureNotify; arg@491: synev.xconfigure.display = dpy; arg@491: synev.xconfigure.event = c->win; arg@491: synev.xconfigure.window = c->win; arg@491: synev.xconfigure.x = c->x; arg@491: synev.xconfigure.y = c->y; arg@491: synev.xconfigure.width = c->w; arg@491: synev.xconfigure.height = c->h; arg@491: synev.xconfigure.border_width = c->border; arg@491: synev.xconfigure.above = None; arg@491: XSendEvent(dpy, c->win, True, NoEventMask, &synev); arg@491: } arg@491: arg@491: void arg@461: focus(Client *c) { arg@709: if(c && !isvisible(c)) arg@239: return; arg@712: if(sel && sel != c) { arg@712: grabbuttons(sel, False); arg@712: XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); arg@318: } arg@400: if(c) { arg@711: detachstack(c); arg@711: c->snext = stack; arg@711: stack = c; arg@711: grabbuttons(c, True); arg@714: } arg@714: sel = c; arg@714: drawstatus(); arg@716: if(!selscreen) arg@714: return; arg@715: if(c) { arg@711: XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); arg@711: XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); arg@400: } arg@712: else arg@400: XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); garbeam@13: } garbeam@13: garbeam@76: Client * arg@461: getclient(Window w) { garbeam@76: Client *c; arg@123: garbeam@76: for(c = clients; c; c = c->next) garbeam@76: if(c->win == w) garbeam@76: return c; garbeam@76: return NULL; garbeam@76: } garbeam@76: arg@734: Bool arg@734: isprotodel(Client *c) { arg@734: int i, n; arg@734: Atom *protocols; arg@734: Bool ret = False; arg@734: arg@734: if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { arg@734: for(i = 0; !ret && i < n; i++) arg@734: if(protocols[i] == wmatom[WMDelete]) arg@734: ret = True; arg@734: XFree(protocols); arg@734: } arg@734: return ret; arg@734: } arg@734: garbeam@76: void arg@461: killclient(Arg *arg) { garbeam@76: if(!sel) garbeam@76: return; arg@734: if(isprotodel(sel)) garbeam@77: sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); garbeam@76: else garbeam@76: XKillClient(dpy, sel->win); garbeam@76: } garbeam@76: garbeam@76: void arg@461: manage(Window w, XWindowAttributes *wa) { arg@431: Client *c; arg@123: Window trans; garbeam@5: garbeam@5: c = emallocz(sizeof(Client)); arg@178: c->tags = emallocz(ntags * sizeof(Bool)); garbeam@5: c->win = w; arg@687: c->x = wa->x; arg@687: c->y = wa->y; arg@687: c->w = wa->width; arg@115: c->h = wa->height; arg@708: if(c->w == sw && c->h == sh) { arg@708: c->border = 0; arg@708: c->x = sx; arg@708: c->y = sy; arg@708: } arg@708: else { arg@708: c->border = BORDERPX; arg@718: if(c->x + c->w + 2 * c->border > wax + waw) arg@718: c->x = wax + waw - c->w - 2 * c->border; arg@718: if(c->y + c->h + 2 * c->border > way + wah) arg@718: c->y = way + wah - c->h - 2 * c->border; arg@708: if(c->x < wax) arg@708: c->x = wax; arg@708: if(c->y < way) arg@708: c->y = way; arg@708: } arg@639: updatesizehints(c); garbeam@26: XSelectInput(dpy, c->win, arg@127: StructureNotifyMask | PropertyChangeMask | EnterWindowMask); garbeam@53: XGetTransientForHint(dpy, c->win, &trans); arg@372: grabbuttons(c, False); arg@715: XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]); arg@500: updatetitle(c); arg@431: settags(c, getclient(trans)); garbeam@80: if(!c->isfloat) arg@549: c->isfloat = trans || c->isfixed; arg@381: if(clients) arg@381: clients->prev = c; arg@381: c->next = clients; arg@446: c->snext = stack; arg@446: stack = clients = c; arg@687: XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); arg@270: XMapWindow(dpy, c->win); arg@725: setclientstate(c, NormalState); arg@261: if(isvisible(c)) garbeam@51: focus(c); arg@533: arrange(); garbeam@94: } garbeam@94: garbeam@94: void arg@708: resize(Client *c, Bool sizehints) { arg@733: float actual, dx, dy, max, min; arg@163: XWindowChanges wc; garbeam@18: arg@721: if(c->w <= 0 || c->h <= 0) arg@721: return; arg@129: if(sizehints) { arg@129: if(c->minw && c->w < c->minw) arg@129: c->w = c->minw; arg@129: if(c->minh && c->h < c->minh) arg@129: c->h = c->minh; arg@129: if(c->maxw && c->w > c->maxw) arg@129: c->w = c->maxw; arg@129: if(c->maxh && c->h > c->maxh) arg@129: c->h = c->maxh; arg@731: /* inspired by algorithm from fluxbox */ arg@731: if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) { arg@731: dx = (float)(c->w - c->basew); arg@731: dy = (float)(c->h - c->baseh); arg@731: min = (float)(c->minax) / (float)(c->minay); arg@731: max = (float)(c->maxax) / (float)(c->maxay); arg@731: actual = dx / dy; arg@731: if(max > 0 && min > 0 && actual > 0) { arg@731: if(actual < min) { arg@732: dy = (dx * min + dy) / (min * min + 1); arg@732: dx = dy * min; arg@731: c->w = (int)dx + c->basew; arg@731: c->h = (int)dy + c->baseh; arg@731: } arg@731: else if(actual > max) { arg@732: dy = (dx * min + dy) / (max * max + 1); arg@732: dx = dy * min; arg@731: c->w = (int)dx + c->basew; arg@731: c->h = (int)dy + c->baseh; arg@731: } arg@731: } arg@731: } arg@731: if(c->incw) arg@731: c->w -= (c->w - c->basew) % c->incw; arg@731: if(c->inch) arg@731: c->h -= (c->h - c->baseh) % c->inch; garbeam@52: } arg@708: if(c->w == sw && c->h == sh) arg@708: c->border = 0; arg@708: else arg@708: c->border = BORDERPX; arg@465: /* offscreen appearance fixes */ arg@718: if(c->x > sw) arg@718: c->x = sw - c->w - 2 * c->border; arg@718: if(c->y > sh) arg@718: c->y = sh - c->h - 2 * c->border; arg@708: if(c->x + c->w + 2 * c->border < sx) arg@517: c->x = sx; arg@708: if(c->y + c->h + 2 * c->border < sy) arg@708: c->y = sy; arg@163: wc.x = c->x; arg@163: wc.y = c->y; arg@163: wc.width = c->w; arg@163: wc.height = c->h; arg@708: wc.border_width = c->border; arg@452: XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); arg@630: configure(c); garbeam@79: XSync(dpy, False); garbeam@18: } garbeam@18: garbeam@76: void arg@639: updatesizehints(Client *c) { arg@123: long msize; garbeam@76: XSizeHints size; arg@123: garbeam@76: if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) garbeam@76: size.flags = PSize; garbeam@76: c->flags = size.flags; garbeam@76: if(c->flags & PBaseSize) { garbeam@76: c->basew = size.base_width; garbeam@76: c->baseh = size.base_height; garbeam@76: } garbeam@76: else garbeam@76: c->basew = c->baseh = 0; garbeam@76: if(c->flags & PResizeInc) { garbeam@76: c->incw = size.width_inc; garbeam@76: c->inch = size.height_inc; garbeam@76: } garbeam@76: else garbeam@76: c->incw = c->inch = 0; garbeam@76: if(c->flags & PMaxSize) { garbeam@76: c->maxw = size.max_width; garbeam@76: c->maxh = size.max_height; garbeam@76: } garbeam@76: else garbeam@76: c->maxw = c->maxh = 0; garbeam@76: if(c->flags & PMinSize) { garbeam@76: c->minw = size.min_width; garbeam@76: c->minh = size.min_height; garbeam@76: } garbeam@76: else garbeam@76: c->minw = c->minh = 0; arg@731: if(c->flags & PAspect) { arg@731: c->minax = size.min_aspect.x; arg@731: c->minay = size.min_aspect.y; arg@731: c->maxax = size.max_aspect.x; arg@731: c->maxay = size.max_aspect.y; arg@731: } arg@731: else arg@731: c->minax = c->minay = c->maxax = c->maxay = 0; arg@550: c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && arg@550: c->maxw == c->minw && c->maxh == c->minh); garbeam@76: } garbeam@76: garbeam@76: void arg@461: updatetitle(Client *c) { arg@123: char **list = NULL; arg@377: int n; garbeam@76: XTextProperty name; garbeam@76: garbeam@76: name.nitems = 0; garbeam@76: c->name[0] = 0; garbeam@77: XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); garbeam@76: if(!name.nitems) garbeam@76: XGetWMName(dpy, c->win, &name); garbeam@76: if(!name.nitems) garbeam@76: return; garbeam@76: if(name.encoding == XA_STRING) arg@581: strncpy(c->name, (char *)name.value, sizeof c->name); garbeam@76: else { garbeam@76: if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success garbeam@76: && n > 0 && *list) garbeam@76: { arg@581: strncpy(c->name, *list, sizeof c->name); garbeam@76: XFreeStringList(list); garbeam@76: } garbeam@76: } garbeam@76: XFree(name.value); garbeam@10: } garbeam@10: garbeam@10: void arg@461: unmanage(Client *c) { arg@450: Client *nc; arg@450: arg@472: /* The server grab construct avoids race conditions. */ garbeam@10: XGrabServer(dpy); garbeam@75: XSetErrorHandler(xerrordummy); arg@400: detach(c); arg@448: detachstack(c); arg@400: if(sel == c) { arg@450: for(nc = stack; nc && !isvisible(nc); nc = nc->snext); arg@450: focus(nc); arg@400: } garbeam@18: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); arg@725: setclientstate(c, WithdrawnState); arg@178: free(c->tags); garbeam@10: free(c); garbeam@79: XSync(dpy, False); garbeam@74: XSetErrorHandler(xerror); garbeam@10: XUngrabServer(dpy); arg@533: arrange(); garbeam@10: }