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@5: garbeam@10: #include garbeam@5: #include garbeam@5: #include garbeam@32: #include garbeam@5: garbeam@76: /* static functions */ garbeam@50: garbeam@26: static void garbeam@75: resizetitle(Client *c) garbeam@26: { garbeam@31: int i; garbeam@31: garbeam@95: c->bw = 0; garbeam@31: for(i = 0; i < TLast; i++) garbeam@31: if(c->tags[i]) garbeam@95: c->bw += textw(c->tags[i]); garbeam@95: c->bw += textw(c->name); garbeam@95: if(c->bw > *c->w) garbeam@95: c->bw = *c->w + 2; garbeam@95: c->bx = *c->x + *c->w - c->bw + 2; garbeam@95: c->by = *c->y; arg@106: if(c->tags[tsel]) arg@106: XMoveResizeWindow(dpy, c->title, c->bx, c->by, c->bw, c->bh); arg@106: else arg@106: XMoveResizeWindow(dpy, c->title, c->bx + 2 * sw, c->by, c->bw, c->bh); 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: { garbeam@95: XMoveWindow(dpy, c->win, *c->x + 2 * sw, *c->y); garbeam@95: XMoveWindow(dpy, c->title, c->bx + 2 * sw, c->by); garbeam@26: } garbeam@26: garbeam@26: void garbeam@13: focus(Client *c) garbeam@13: { garbeam@52: Client *old = sel; garbeam@63: XEvent ev; garbeam@52: garbeam@50: sel = c; garbeam@52: if(old && old != c) garbeam@74: drawtitle(old); garbeam@74: drawtitle(c); garbeam@26: XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); garbeam@79: XSync(dpy, False); garbeam@63: while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); garbeam@13: } garbeam@13: garbeam@13: void garbeam@76: focusnext(Arg *arg) garbeam@76: { garbeam@76: Client *c; garbeam@76: garbeam@76: if(!sel) garbeam@76: return; garbeam@76: garbeam@93: if(!(c = getnext(sel->next, tsel))) garbeam@93: c = getnext(clients, tsel); garbeam@76: if(c) { garbeam@76: higher(c); garbeam@76: c->revert = sel; garbeam@76: focus(c); garbeam@76: } garbeam@76: } garbeam@76: garbeam@76: void garbeam@76: focusprev(Arg *arg) garbeam@76: { garbeam@76: Client *c; garbeam@76: garbeam@76: if(!sel) garbeam@76: return; garbeam@76: garbeam@76: if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) { garbeam@76: higher(c); garbeam@76: focus(c); garbeam@76: } garbeam@76: } garbeam@76: garbeam@76: Client * garbeam@76: getclient(Window w) garbeam@76: { garbeam@76: Client *c; 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; garbeam@76: for(c = clients; c; c = c->next) garbeam@76: if(c->title == 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) { 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: garbeam@95: dy = -(*c->h / 2) + c->border; garbeam@76: break; garbeam@76: case SouthEastGravity: garbeam@76: case SouthGravity: garbeam@76: case SouthWestGravity: garbeam@95: dy = -(*c->h); garbeam@76: break; garbeam@76: default: garbeam@76: break; garbeam@76: } garbeam@76: garbeam@76: switch (c->grav) { 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: garbeam@95: dx = -(*c->w / 2) + c->border; garbeam@76: break; garbeam@76: case NorthEastGravity: garbeam@76: case EastGravity: garbeam@76: case SouthEastGravity: garbeam@95: dx = -(*c->w + c->border); garbeam@76: break; garbeam@76: default: garbeam@76: break; garbeam@76: } garbeam@76: garbeam@76: if(invert) { garbeam@76: dx = -dx; garbeam@76: dy = -dy; garbeam@76: } garbeam@95: *c->x += dx; garbeam@95: *c->y += dy; garbeam@76: } garbeam@76: garbeam@76: void garbeam@76: higher(Client *c) garbeam@76: { garbeam@76: XRaiseWindow(dpy, c->win); garbeam@76: XRaiseWindow(dpy, c->title); garbeam@76: } garbeam@76: garbeam@76: void garbeam@76: killclient(Arg *arg) garbeam@76: { garbeam@76: if(!sel) garbeam@76: return; garbeam@76: if(sel->proto & WM_PROTOCOL_DELWIN) 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@76: lower(Client *c) garbeam@76: { garbeam@76: XLowerWindow(dpy, c->title); garbeam@76: XLowerWindow(dpy, c->win); garbeam@76: } garbeam@76: garbeam@76: void garbeam@10: manage(Window w, XWindowAttributes *wa) garbeam@5: { arg@100: int diff; garbeam@80: Client *c; garbeam@5: XSetWindowAttributes twa; garbeam@53: Window trans; garbeam@5: garbeam@5: c = emallocz(sizeof(Client)); garbeam@5: c->win = w; garbeam@95: c->bx = c->fx = c->tx = wa->x; garbeam@95: c->by = c->fy = c->ty = wa->y; garbeam@95: c->bw = c->fw = c->tw = wa->width; garbeam@95: c->fh = c->th = wa->height; garbeam@95: c->bh = bh; arg@100: arg@100: diff = sw - c->fw; arg@102: c->fx = random() % (diff ? diff : 1); arg@103: diff = sh - c->fh - bh; arg@102: c->fy = random() % (diff ? diff : 1); arg@100: arg@104: if(c->fy < bh) arg@104: c->by = c->fy = c->ty = bh; arg@104: garbeam@29: c->border = 1; garbeam@75: c->proto = getproto(c->win); garbeam@74: setsize(c); garbeam@26: XSelectInput(dpy, c->win, garbeam@26: StructureNotifyMask | PropertyChangeMask | EnterWindowMask); garbeam@53: XGetTransientForHint(dpy, c->win, &trans); garbeam@5: twa.override_redirect = 1; garbeam@5: twa.background_pixmap = ParentRelative; garbeam@23: twa.event_mask = ExposureMask; garbeam@5: garbeam@95: c->title = XCreateWindow(dpy, root, c->bx, c->by, c->bw, c->bh, garbeam@20: 0, DefaultDepth(dpy, screen), CopyFromParent, garbeam@5: DefaultVisual(dpy, screen), garbeam@5: CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa); garbeam@31: garbeam@75: settags(c); garbeam@49: garbeam@80: c->next = clients; garbeam@80: clients = c; garbeam@49: garbeam@80: XGrabButton(dpy, Button1, ControlMask, c->win, False, ButtonPressMask, garbeam@80: GrabModeAsync, GrabModeSync, None, None); garbeam@19: XGrabButton(dpy, Button1, Mod1Mask, c->win, False, ButtonPressMask, garbeam@19: GrabModeAsync, GrabModeSync, None, None); garbeam@19: XGrabButton(dpy, Button2, Mod1Mask, c->win, False, ButtonPressMask, garbeam@19: GrabModeAsync, GrabModeSync, None, None); garbeam@19: XGrabButton(dpy, Button3, Mod1Mask, c->win, False, ButtonPressMask, garbeam@18: GrabModeAsync, GrabModeSync, None, None); garbeam@53: garbeam@80: if(!c->isfloat) garbeam@80: c->isfloat = trans garbeam@53: || ((c->maxw == c->minw) && (c->maxh == c->minh)); garbeam@53: garbeam@95: setgeom(c); garbeam@95: settitle(c); garbeam@95: garbeam@46: arrange(NULL); garbeam@95: garbeam@60: /* mapping the window now prevents flicker */ garbeam@60: if(c->tags[tsel]) { garbeam@60: XMapRaised(dpy, c->win); garbeam@60: XMapRaised(dpy, c->title); garbeam@51: focus(c); garbeam@60: } garbeam@60: else { garbeam@60: XMapRaised(dpy, c->win); garbeam@60: XMapRaised(dpy, c->title); garbeam@60: } garbeam@5: } garbeam@9: garbeam@18: void garbeam@76: maximize(Arg *arg) garbeam@29: { garbeam@76: if(!sel) garbeam@76: return; garbeam@95: *sel->x = sx; garbeam@95: *sel->y = sy + bh; garbeam@95: *sel->w = sw - 2 * sel->border; garbeam@95: *sel->h = sh - 2 * sel->border - bh; garbeam@76: higher(sel); arg@99: resize(sel, False, TopLeft); garbeam@29: } garbeam@29: garbeam@29: void garbeam@94: pop(Client *c) garbeam@94: { garbeam@94: Client **l; garbeam@94: for(l = &clients; *l && *l != c; l = &(*l)->next); garbeam@94: *l = c->next; garbeam@94: garbeam@94: c->next = clients; /* pop */ garbeam@94: clients = c; garbeam@94: arrange(NULL); garbeam@94: } garbeam@94: garbeam@94: void arg@99: resize(Client *c, Bool inc, Corner sticky) garbeam@18: { garbeam@18: XConfigureEvent e; arg@99: int right = *c->x + *c->w; arg@99: int bottom = *c->y + *c->h; garbeam@18: garbeam@52: if(inc) { garbeam@52: if(c->incw) garbeam@95: *c->w -= (*c->w - c->basew) % c->incw; garbeam@52: if(c->inch) garbeam@95: *c->h -= (*c->h - c->baseh) % c->inch; garbeam@52: } garbeam@95: if(*c->x > sw) /* might happen on restart */ garbeam@95: *c->x = sw - *c->w; garbeam@95: if(*c->y > sh) garbeam@95: *c->y = sh - *c->h; garbeam@95: if(c->minw && *c->w < c->minw) garbeam@95: *c->w = c->minw; garbeam@95: if(c->minh && *c->h < c->minh) garbeam@95: *c->h = c->minh; garbeam@95: if(c->maxw && *c->w > c->maxw) garbeam@95: *c->w = c->maxw; garbeam@95: if(c->maxh && *c->h > c->maxh) garbeam@95: *c->h = c->maxh; arg@105: if(sticky == TopRight || sticky == BotRight) arg@99: *c->x = right - *c->w; arg@105: if(sticky == BotLeft || sticky == BotRight) arg@99: *c->y = bottom - *c->h; arg@106: garbeam@75: resizetitle(c); garbeam@55: XSetWindowBorderWidth(dpy, c->win, 1); garbeam@95: XMoveResizeWindow(dpy, c->win, *c->x, *c->y, *c->w, *c->h); arg@106: garbeam@18: e.type = ConfigureNotify; garbeam@18: e.event = c->win; garbeam@18: e.window = c->win; garbeam@95: e.x = *c->x; garbeam@95: e.y = *c->y; garbeam@95: e.width = *c->w; garbeam@95: e.height = *c->h; garbeam@29: e.border_width = c->border; garbeam@18: e.above = None; garbeam@18: e.override_redirect = False; garbeam@18: XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e); garbeam@79: XSync(dpy, False); garbeam@18: } garbeam@18: garbeam@76: void garbeam@95: setgeom(Client *c) garbeam@95: { garbeam@95: if((arrange == dotile) && !c->isfloat) { garbeam@95: c->x = &c->tx; garbeam@95: c->y = &c->ty; garbeam@95: c->w = &c->tw; garbeam@95: c->h = &c->th; garbeam@95: } garbeam@95: else { garbeam@95: c->x = &c->fx; garbeam@95: c->y = &c->fy; garbeam@95: c->w = &c->fw; garbeam@95: c->h = &c->fh; garbeam@95: } garbeam@95: } garbeam@95: garbeam@95: void garbeam@76: setsize(Client *c) garbeam@10: { garbeam@76: XSizeHints size; garbeam@76: long msize; 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: { garbeam@76: XTextProperty name; garbeam@76: int n; garbeam@76: char **list = NULL; 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 garbeam@10: unmanage(Client *c) garbeam@10: { garbeam@50: Client **l; garbeam@50: garbeam@10: XGrabServer(dpy); garbeam@75: XSetErrorHandler(xerrordummy); garbeam@10: garbeam@18: XUngrabButton(dpy, AnyButton, AnyModifier, c->win); garbeam@10: XDestroyWindow(dpy, c->title); garbeam@10: garbeam@50: for(l = &clients; *l && *l != c; l = &(*l)->next); garbeam@50: *l = c->next; garbeam@50: for(l = &clients; *l; l = &(*l)->next) garbeam@50: if((*l)->revert == c) garbeam@50: (*l)->revert = NULL; garbeam@50: if(sel == c) garbeam@50: sel = sel->revert ? sel->revert : clients; garbeam@49: garbeam@10: free(c); garbeam@10: garbeam@79: XSync(dpy, False); garbeam@74: XSetErrorHandler(xerror); garbeam@10: XUngrabServer(dpy); garbeam@46: arrange(NULL); garbeam@50: if(sel) garbeam@50: focus(sel); garbeam@10: } garbeam@10: garbeam@75: void garbeam@75: zoom(Arg *arg) garbeam@75: { garbeam@94: Client *c; garbeam@75: garbeam@75: if(!sel) garbeam@75: return; garbeam@75: garbeam@93: if(sel == getnext(clients, tsel) && sel->next) { garbeam@93: if((c = getnext(sel->next, tsel))) garbeam@75: sel = c; garbeam@75: } garbeam@75: garbeam@94: pop(sel); garbeam@75: focus(sel); garbeam@75: }