garbeam@5: /* garbeam@5: * (C)opyright MMVI 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: garbeam@76: /* static functions */ garbeam@50: garbeam@26: static void arg@372: grabbuttons(Client *c, Bool focus) arg@318: { arg@372: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); arg@372: arg@372: if(focus) { 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@372: arg@318: } arg@318: arg@318: static void garbeam@75: resizetitle(Client *c) garbeam@26: { arg@342: c->tw = textw(c->name); arg@115: if(c->tw > c->w) arg@115: c->tw = c->w + 2; arg@115: c->tx = c->x + c->w - c->tw + 2; arg@115: c->ty = c->y; arg@261: if(isvisible(c)) arg@342: XMoveResizeWindow(dpy, c->twin, c->tx, c->ty, c->tw, c->th); arg@106: else arg@342: XMoveResizeWindow(dpy, c->twin, c->tx + 2 * sw, c->ty, c->tw, c->th); arg@106: garbeam@26: } garbeam@18: garbeam@76: static int garbeam@76: xerrordummy(Display *dsply, XErrorEvent *ee) garbeam@5: { garbeam@76: return 0; garbeam@5: } garbeam@5: garbeam@76: /* extern functions */ garbeam@20: garbeam@20: void garbeam@76: ban(Client *c) garbeam@26: { arg@115: XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); arg@342: XMoveWindow(dpy, c->twin, c->tx + 2 * sw, c->ty); garbeam@26: } garbeam@26: garbeam@26: void garbeam@13: focus(Client *c) garbeam@13: { arg@286: Client *old = sel; arg@286: arg@319: if(!issel) arg@239: return; arg@319: if(!sel) arg@319: sel = c; arg@319: else if(sel != c) { arg@319: if(sel->ismax) arg@319: togglemax(NULL); arg@319: sel = c; arg@372: grabbuttons(old, False); garbeam@74: drawtitle(old); arg@318: } arg@372: grabbuttons(c, True); garbeam@74: drawtitle(c); garbeam@26: XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); garbeam@13: } garbeam@13: garbeam@76: Client * garbeam@76: getclient(Window w) garbeam@76: { 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: garbeam@76: Client * garbeam@76: getctitle(Window w) garbeam@76: { garbeam@76: Client *c; arg@123: garbeam@76: for(c = clients; c; c = c->next) arg@342: if(c->twin == w) garbeam@76: return c; garbeam@76: return NULL; garbeam@76: } garbeam@76: garbeam@76: void garbeam@76: gravitate(Client *c, Bool invert) garbeam@76: { garbeam@76: int dx = 0, dy = 0; garbeam@76: garbeam@76: switch(c->grav) { arg@127: default: arg@127: break; garbeam@76: case StaticGravity: garbeam@76: case NorthWestGravity: garbeam@76: case NorthGravity: garbeam@76: case NorthEastGravity: garbeam@76: dy = c->border; garbeam@76: break; garbeam@76: case EastGravity: garbeam@76: case CenterGravity: garbeam@76: case WestGravity: arg@115: dy = -(c->h / 2) + c->border; garbeam@76: break; garbeam@76: case SouthEastGravity: garbeam@76: case SouthGravity: garbeam@76: case SouthWestGravity: arg@115: dy = -(c->h); garbeam@76: break; garbeam@76: } garbeam@76: garbeam@76: switch (c->grav) { arg@127: default: arg@127: break; garbeam@76: case StaticGravity: garbeam@76: case NorthWestGravity: garbeam@76: case WestGravity: garbeam@76: case SouthWestGravity: garbeam@76: dx = c->border; garbeam@76: break; garbeam@76: case NorthGravity: garbeam@76: case CenterGravity: garbeam@76: case SouthGravity: arg@115: dx = -(c->w / 2) + c->border; garbeam@76: break; garbeam@76: case NorthEastGravity: garbeam@76: case EastGravity: garbeam@76: case SouthEastGravity: arg@115: dx = -(c->w + c->border); garbeam@76: break; garbeam@76: } garbeam@76: garbeam@76: if(invert) { garbeam@76: dx = -dx; garbeam@76: dy = -dy; garbeam@76: } arg@115: c->x += dx; arg@115: c->y += dy; garbeam@76: } garbeam@76: garbeam@76: void garbeam@76: killclient(Arg *arg) garbeam@76: { garbeam@76: if(!sel) garbeam@76: return; arg@157: if(sel->proto & PROTODELWIN) 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 garbeam@10: manage(Window w, XWindowAttributes *wa) garbeam@5: { arg@320: unsigned int i; arg@306: Client *c, *tc; arg@123: Window trans; garbeam@5: XSetWindowAttributes twa; garbeam@5: garbeam@5: c = emallocz(sizeof(Client)); arg@178: c->tags = emallocz(ntags * sizeof(Bool)); garbeam@5: c->win = w; arg@115: c->x = c->tx = wa->x; arg@115: c->y = c->ty = wa->y; arg@115: c->w = c->tw = wa->width; arg@115: c->h = wa->height; arg@115: c->th = bh; arg@100: arg@164: c->border = 0; arg@163: setsize(c); arg@163: arg@315: if(c->x + c->w + 2 > sw) arg@314: c->x = sw - c->w - 2; arg@314: if(c->x < 0) arg@314: c->x = 0; arg@315: if(c->y + c->h + 2 > sh) arg@314: c->y = sh - c->h - 2; arg@163: if(c->h != sh && c->y < bh) arg@314: c->y = bh; arg@104: garbeam@75: c->proto = getproto(c->win); garbeam@26: XSelectInput(dpy, c->win, arg@127: StructureNotifyMask | PropertyChangeMask | EnterWindowMask); garbeam@53: XGetTransientForHint(dpy, c->win, &trans); garbeam@5: twa.override_redirect = 1; garbeam@5: twa.background_pixmap = ParentRelative; arg@161: twa.event_mask = ExposureMask | EnterWindowMask; garbeam@5: arg@342: c->twin = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th, garbeam@20: 0, DefaultDepth(dpy, screen), CopyFromParent, garbeam@5: DefaultVisual(dpy, screen), garbeam@5: CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa); garbeam@31: arg@372: grabbuttons(c, False); arg@320: if((tc = getclient(trans))) /* inherit tags */ arg@320: for(i = 0; i < ntags; i++) arg@320: c->tags[i] = tc->tags[i]; arg@320: else arg@320: settags(c); garbeam@80: if(!c->isfloat) arg@163: c->isfloat = trans arg@163: || (c->maxw && c->minw && arg@164: c->maxw == c->minw && c->maxh == c->minh); arg@378: arg@381: if(clients) arg@381: clients->prev = c; arg@381: c->next = clients; arg@381: clients = c; arg@378: garbeam@95: settitle(c); arg@283: if(isvisible(c)) arg@283: sel = c; arg@283: arrange(NULL); arg@270: XMapWindow(dpy, c->win); arg@342: XMapWindow(dpy, c->twin); arg@261: if(isvisible(c)) garbeam@51: focus(c); garbeam@94: } garbeam@94: garbeam@94: void arg@129: resize(Client *c, Bool sizehints, Corner sticky) garbeam@18: { arg@123: int bottom = c->y + c->h; arg@123: int right = c->x + c->w; arg@163: XWindowChanges wc; garbeam@18: arg@129: if(sizehints) { garbeam@52: if(c->incw) arg@115: c->w -= (c->w - c->basew) % c->incw; garbeam@52: if(c->inch) arg@115: c->h -= (c->h - c->baseh) % c->inch; 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; garbeam@52: } arg@105: if(sticky == TopRight || sticky == BotRight) arg@115: c->x = right - c->w; arg@105: if(sticky == BotLeft || sticky == BotRight) arg@115: c->y = bottom - c->h; arg@106: garbeam@75: resizetitle(c); 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@164: if(c->w == sw && c->h == sh) arg@164: wc.border_width = 0; arg@164: else arg@164: wc.border_width = 1; arg@164: XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); garbeam@79: XSync(dpy, False); garbeam@18: } garbeam@18: garbeam@76: void garbeam@76: setsize(Client *c) garbeam@10: { 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; garbeam@76: if(c->flags & PWinGravity) garbeam@76: c->grav = size.win_gravity; garbeam@76: else garbeam@76: c->grav = NorthWestGravity; garbeam@76: } garbeam@76: garbeam@76: void garbeam@76: settitle(Client *c) garbeam@76: { 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) garbeam@76: 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: { garbeam@76: strncpy(c->name, *list, sizeof(c->name)); garbeam@76: XFreeStringList(list); garbeam@76: } garbeam@76: } garbeam@76: XFree(name.value); garbeam@76: resizetitle(c); garbeam@10: } garbeam@10: garbeam@10: void arg@124: togglemax(Arg *arg) arg@124: { arg@124: int ox, oy, ow, oh; arg@124: XEvent ev; arg@124: arg@124: if(!sel) arg@124: return; arg@124: arg@124: if((sel->ismax = !sel->ismax)) { arg@124: ox = sel->x; arg@124: oy = sel->y; arg@124: ow = sel->w; arg@124: oh = sel->h; arg@124: sel->x = sx; arg@124: sel->y = sy + bh; arg@164: sel->w = sw - 2; arg@164: sel->h = sh - 2 - bh; arg@124: arg@270: restack(); arg@247: resize(sel, arrange == dofloat, TopLeft); arg@124: arg@124: sel->x = ox; arg@124: sel->y = oy; arg@124: sel->w = ow; arg@124: sel->h = oh; arg@124: } arg@124: else arg@124: resize(sel, False, TopLeft); arg@124: while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); arg@124: } arg@124: arg@124: void garbeam@10: unmanage(Client *c) garbeam@10: { arg@372: Client *tc; arg@372: Window trans; garbeam@10: XGrabServer(dpy); garbeam@75: XSetErrorHandler(xerrordummy); garbeam@10: arg@372: XGetTransientForHint(dpy, c->win, &trans); arg@372: garbeam@18: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); arg@342: XDestroyWindow(dpy, c->twin); garbeam@10: arg@378: detach(c); arg@372: if(sel == c) { arg@372: if(trans && (tc = getclient(trans)) && isvisible(tc)) arg@372: sel = tc; arg@372: else arg@372: sel = getnext(clients); arg@372: } arg@178: free(c->tags); garbeam@10: free(c); garbeam@10: garbeam@79: XSync(dpy, False); garbeam@74: XSetErrorHandler(xerror); garbeam@10: XUngrabServer(dpy); garbeam@50: if(sel) garbeam@50: focus(sel); arg@270: arrange(NULL); garbeam@10: }