garbeam@5: /* garbeam@5: * (C)opyright MMVI Anselm R. Garbe garbeam@5: * See LICENSE file for license details. garbeam@5: */ garbeam@5: garbeam@10: #include garbeam@51: #include garbeam@5: #include garbeam@5: #include garbeam@32: #include garbeam@5: garbeam@43: #include "dwm.h" garbeam@5: garbeam@51: static void (*arrange)(Arg *) = tiling; garbeam@49: garbeam@51: static Rule rule[] = { garbeam@51: { "Firefox-bin", "Gecko", { [Twww] = "www" } }, garbeam@51: }; garbeam@50: garbeam@49: static Client * garbeam@49: next(Client *c) garbeam@49: { garbeam@50: for(; c && !c->tags[tsel]; c = c->next); garbeam@49: return c; garbeam@49: } garbeam@49: garbeam@50: void garbeam@50: zoom(Arg *arg) garbeam@49: { garbeam@51: Client **l, *old; garbeam@50: garbeam@51: if(!(old = sel)) garbeam@50: return; garbeam@50: garbeam@50: for(l = &clients; *l && *l != sel; l = &(*l)->next); garbeam@50: *l = sel->next; garbeam@50: garbeam@51: old->next = clients; /* pop */ garbeam@51: clients = old; garbeam@51: sel = old; garbeam@50: arrange(NULL); garbeam@50: focus(sel); garbeam@49: } garbeam@31: garbeam@27: void garbeam@49: max(Arg *arg) garbeam@28: { garbeam@50: if(!sel) garbeam@28: return; garbeam@50: sel->x = sx; garbeam@50: sel->y = sy; garbeam@50: sel->w = sw - 2 * sel->border; garbeam@50: sel->h = sh - 2 * sel->border; garbeam@50: craise(sel); garbeam@50: resize(sel); garbeam@29: discard_events(EnterWindowMask); garbeam@28: } garbeam@28: garbeam@46: void garbeam@50: view(Arg *arg) garbeam@50: { garbeam@50: tsel = arg->i; garbeam@50: arrange(NULL); garbeam@50: } garbeam@50: garbeam@50: void garbeam@49: tag(Arg *arg) garbeam@49: { garbeam@50: int i, n; garbeam@50: if(!sel) garbeam@49: return; garbeam@49: garbeam@50: if(arg->i == tsel) { garbeam@50: for(n = i = 0; i < TLast; i++) garbeam@50: if(sel->tags[i]) garbeam@50: n++; garbeam@50: if(n < 2) garbeam@50: return; garbeam@50: } garbeam@49: garbeam@50: if(sel->tags[arg->i]) garbeam@50: sel->tags[arg->i] = NULL; /* toggle tag */ garbeam@49: else garbeam@50: sel->tags[arg->i] = tags[arg->i]; garbeam@49: arrange(NULL); garbeam@49: } garbeam@49: garbeam@50: static void garbeam@50: ban_client(Client *c) garbeam@50: { garbeam@50: XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); garbeam@50: XMoveWindow(dpy, c->title, c->tx + 2 * sw, c->ty); garbeam@50: } garbeam@50: garbeam@49: void garbeam@49: floating(Arg *arg) garbeam@31: { garbeam@31: Client *c; garbeam@31: garbeam@46: arrange = floating; garbeam@50: for(c = clients; c; c = c->next) { garbeam@50: if(c->tags[tsel]) garbeam@50: resize(c); garbeam@50: else garbeam@50: ban_client(c); garbeam@50: } garbeam@50: if(sel && !sel->tags[tsel]) { garbeam@50: if((sel = next(clients))) { garbeam@50: craise(sel); garbeam@50: focus(sel); garbeam@50: } garbeam@50: } garbeam@31: discard_events(EnterWindowMask); garbeam@31: } garbeam@31: garbeam@46: void garbeam@49: tiling(Arg *arg) garbeam@27: { garbeam@27: Client *c; garbeam@51: int n, i, w, h; garbeam@27: garbeam@51: w = sw - mw; garbeam@46: arrange = tiling; garbeam@51: for(n = 0, c = clients; c; c = c->next) garbeam@51: if(c->tags[tsel]) garbeam@51: n++; garbeam@50: garbeam@51: h = (n > 2) ? sh / (n - 2) : sh; garbeam@27: garbeam@51: for(i = 0, c = clients; c; c = c->next) { garbeam@50: if(c->tags[tsel]) { garbeam@51: if(n == 1) { garbeam@51: c->x = sx; garbeam@51: c->y = sy; garbeam@51: c->w = sw; garbeam@51: c->h = sh; garbeam@51: } garbeam@51: else if(i == 1) { garbeam@51: c->x = sx; garbeam@51: c->y = sy; garbeam@51: c->w = mw; garbeam@51: c->h = sh; garbeam@51: } garbeam@51: else { garbeam@51: c->x = sx + mw; garbeam@51: c->y = sy + (i - 2) * h; garbeam@51: c->w = w; garbeam@51: c->h = h; garbeam@51: } garbeam@50: resize(c); garbeam@51: i++; garbeam@50: } garbeam@50: else garbeam@50: ban_client(c); garbeam@50: } garbeam@50: if(sel && !sel->tags[tsel]) { garbeam@50: if((sel = next(clients))) { garbeam@50: craise(sel); garbeam@50: focus(sel); garbeam@27: } garbeam@27: } garbeam@29: discard_events(EnterWindowMask); garbeam@27: } garbeam@27: garbeam@27: void garbeam@49: prevc(Arg *arg) garbeam@27: { garbeam@49: Client *c; garbeam@27: garbeam@50: if(!sel) garbeam@27: return; garbeam@49: garbeam@50: if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) { garbeam@49: craise(c); garbeam@49: focus(c); garbeam@49: } garbeam@27: } garbeam@27: garbeam@27: void garbeam@49: nextc(Arg *arg) garbeam@27: { garbeam@49: Client *c; garbeam@49: garbeam@50: if(!sel) garbeam@49: return; garbeam@49: garbeam@50: if(!(c = next(sel->next))) garbeam@50: c = next(clients); garbeam@49: if(c) { garbeam@49: craise(c); garbeam@50: c->revert = sel; garbeam@49: focus(c); garbeam@49: } garbeam@49: } garbeam@49: garbeam@49: void garbeam@49: ckill(Arg *arg) garbeam@49: { garbeam@50: if(!sel) garbeam@27: return; garbeam@50: if(sel->proto & WM_PROTOCOL_DELWIN) garbeam@50: send_message(sel->win, wm_atom[WMProtocols], wm_atom[WMDelete]); garbeam@27: else garbeam@50: XKillClient(dpy, sel->win); garbeam@27: } garbeam@27: garbeam@26: static void garbeam@26: resize_title(Client *c) garbeam@26: { garbeam@31: int i; garbeam@31: garbeam@31: c->tw = 0; garbeam@31: for(i = 0; i < TLast; i++) garbeam@31: if(c->tags[i]) garbeam@43: c->tw += textw(c->tags[i]) + dc.font.height; garbeam@43: c->tw += textw(c->name) + dc.font.height; garbeam@26: if(c->tw > c->w) garbeam@26: c->tw = c->w + 2; garbeam@26: c->tx = c->x + c->w - c->tw + 2; garbeam@26: c->ty = c->y; garbeam@26: XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th); garbeam@26: } garbeam@18: garbeam@13: void garbeam@13: update_name(Client *c) garbeam@5: { garbeam@5: XTextProperty name; garbeam@5: int n; garbeam@7: char **list = NULL; garbeam@5: garbeam@5: name.nitems = 0; garbeam@5: c->name[0] = 0; garbeam@5: XGetTextProperty(dpy, c->win, &name, net_atom[NetWMName]); garbeam@5: if(!name.nitems) garbeam@5: XGetWMName(dpy, c->win, &name); garbeam@5: if(!name.nitems) garbeam@5: return; garbeam@5: if(name.encoding == XA_STRING) garbeam@5: strncpy(c->name, (char *)name.value, sizeof(c->name)); garbeam@5: else { garbeam@5: if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success garbeam@5: && n > 0 && *list) garbeam@5: { garbeam@5: strncpy(c->name, *list, sizeof(c->name)); garbeam@5: XFreeStringList(list); garbeam@5: } garbeam@5: } garbeam@5: XFree(name.value); garbeam@26: resize_title(c); garbeam@5: } garbeam@5: garbeam@10: void garbeam@20: update_size(Client *c) garbeam@20: { garbeam@20: XSizeHints size; garbeam@20: long msize; garbeam@20: if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) garbeam@20: size.flags = PSize; garbeam@20: c->flags = size.flags; garbeam@21: if(c->flags & PBaseSize) { garbeam@21: c->basew = size.base_width; garbeam@21: c->baseh = size.base_height; garbeam@21: } garbeam@21: else garbeam@21: c->basew = c->baseh = 0; garbeam@21: if(c->flags & PResizeInc) { garbeam@21: c->incw = size.width_inc; garbeam@21: c->inch = size.height_inc; garbeam@21: } garbeam@21: else garbeam@21: c->incw = c->inch = 0; garbeam@21: if(c->flags & PMaxSize) { garbeam@21: c->maxw = size.max_width; garbeam@21: c->maxh = size.max_height; garbeam@21: } garbeam@21: else garbeam@21: c->maxw = c->maxh = 0; garbeam@21: if(c->flags & PMinSize) { garbeam@21: c->minw = size.min_width; garbeam@21: c->minh = size.min_height; garbeam@21: } garbeam@21: else garbeam@21: c->minw = c->minh = 0; garbeam@29: if(c->flags & PWinGravity) garbeam@29: c->grav = size.win_gravity; garbeam@29: else garbeam@29: c->grav = NorthWestGravity; garbeam@20: } garbeam@20: garbeam@20: void garbeam@32: craise(Client *c) garbeam@26: { garbeam@26: XRaiseWindow(dpy, c->win); garbeam@26: XRaiseWindow(dpy, c->title); garbeam@26: } garbeam@26: garbeam@26: void garbeam@26: lower(Client *c) garbeam@26: { garbeam@26: XLowerWindow(dpy, c->title); garbeam@26: XLowerWindow(dpy, c->win); garbeam@26: } garbeam@26: garbeam@26: void garbeam@13: focus(Client *c) garbeam@13: { garbeam@50: if(sel && sel != c) { garbeam@50: XSetWindowBorder(dpy, sel->win, dc.bg); garbeam@50: XMapWindow(dpy, sel->title); garbeam@50: draw_client(sel); garbeam@21: } garbeam@50: sel = c; garbeam@21: XUnmapWindow(dpy, c->title); garbeam@43: XSetWindowBorder(dpy, c->win, dc.fg); garbeam@27: draw_client(c); garbeam@26: XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); garbeam@13: XFlush(dpy); garbeam@45: discard_events(EnterWindowMask); garbeam@13: } garbeam@13: garbeam@51: static void garbeam@51: init_tags(Client *c) garbeam@51: { garbeam@51: XClassHint ch; garbeam@51: static unsigned int len = rule ? sizeof(rule) / sizeof(rule[0]) : 0; garbeam@51: unsigned int i, j; garbeam@51: Bool matched = False; garbeam@51: garbeam@51: if(!len) { garbeam@51: c->tags[tsel] = tags[tsel]; garbeam@51: return; garbeam@51: } garbeam@51: garbeam@51: if(XGetClassHint(dpy, c->win, &ch)) { garbeam@51: if(ch.res_class && ch.res_name) { garbeam@51: fprintf(stderr, "%s:%s\n", ch.res_class, ch.res_name); garbeam@51: for(i = 0; i < len; i++) garbeam@51: if(!strncmp(rule[i].class, ch.res_class, sizeof(rule[i].class)) garbeam@51: && !strncmp(rule[i].instance, ch.res_name, sizeof(rule[i].instance))) garbeam@51: { garbeam@51: fprintf(stderr, "->>>%s:%s\n", ch.res_class, ch.res_name); garbeam@51: for(j = 0; j < TLast; j++) garbeam@51: c->tags[j] = rule[i].tags[j]; garbeam@51: matched = True; garbeam@51: break; garbeam@51: } garbeam@51: } garbeam@51: if(ch.res_class) garbeam@51: XFree(ch.res_class); garbeam@51: if(ch.res_name) garbeam@51: XFree(ch.res_name); garbeam@51: } garbeam@51: garbeam@51: if(!matched) garbeam@51: c->tags[tsel] = tags[tsel]; garbeam@51: } garbeam@51: garbeam@13: void garbeam@10: manage(Window w, XWindowAttributes *wa) garbeam@5: { garbeam@50: Client *c, **l; garbeam@5: XSetWindowAttributes twa; garbeam@5: garbeam@5: c = emallocz(sizeof(Client)); garbeam@5: c->win = w; garbeam@22: c->tx = c->x = wa->x; garbeam@22: c->ty = c->y = wa->y; garbeam@22: c->tw = c->w = wa->width; garbeam@20: c->h = wa->height; garbeam@32: c->th = th; garbeam@29: c->border = 1; garbeam@50: c->proto = win_proto(c->win); garbeam@20: update_size(c); garbeam@26: XSelectInput(dpy, c->win, garbeam@26: StructureNotifyMask | PropertyChangeMask | EnterWindowMask); garbeam@5: XGetTransientForHint(dpy, c->win, &c->trans); garbeam@5: twa.override_redirect = 1; garbeam@5: twa.background_pixmap = ParentRelative; garbeam@23: twa.event_mask = ExposureMask; garbeam@5: garbeam@22: c->title = 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: garbeam@21: update_name(c); garbeam@51: init_tags(c); garbeam@49: garbeam@50: for(l = &clients; *l; l = &(*l)->next); garbeam@50: c->next = *l; /* *l == nil */ garbeam@50: *l = c; garbeam@49: garbeam@43: XSetWindowBorderWidth(dpy, c->win, 1); garbeam@26: XMapRaised(dpy, c->win); garbeam@26: XMapRaised(dpy, c->title); 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@46: arrange(NULL); garbeam@51: if(c->tags[tsel]) garbeam@51: focus(c); garbeam@51: else garbeam@51: ban_client(c); garbeam@5: } garbeam@9: garbeam@18: void garbeam@29: gravitate(Client *c, Bool invert) garbeam@29: { garbeam@29: int dx = 0, dy = 0; garbeam@29: garbeam@29: switch(c->grav) { garbeam@29: case StaticGravity: garbeam@29: case NorthWestGravity: garbeam@29: case NorthGravity: garbeam@29: case NorthEastGravity: garbeam@29: dy = c->border; garbeam@29: break; garbeam@29: case EastGravity: garbeam@29: case CenterGravity: garbeam@29: case WestGravity: garbeam@29: dy = -(c->h / 2) + c->border; garbeam@29: break; garbeam@29: case SouthEastGravity: garbeam@29: case SouthGravity: garbeam@29: case SouthWestGravity: garbeam@29: dy = -c->h; garbeam@29: break; garbeam@29: default: garbeam@29: break; garbeam@29: } garbeam@29: garbeam@29: switch (c->grav) { garbeam@29: case StaticGravity: garbeam@29: case NorthWestGravity: garbeam@29: case WestGravity: garbeam@29: case SouthWestGravity: garbeam@29: dx = c->border; garbeam@29: break; garbeam@29: case NorthGravity: garbeam@29: case CenterGravity: garbeam@29: case SouthGravity: garbeam@29: dx = -(c->w / 2) + c->border; garbeam@29: break; garbeam@29: case NorthEastGravity: garbeam@29: case EastGravity: garbeam@29: case SouthEastGravity: garbeam@29: dx = -(c->w + c->border); garbeam@29: break; garbeam@29: default: garbeam@29: break; garbeam@29: } garbeam@29: garbeam@29: if(invert) { garbeam@29: dx = -dx; garbeam@29: dy = -dy; garbeam@29: } garbeam@29: c->x += dx; garbeam@29: c->y += dy; garbeam@29: } garbeam@29: garbeam@31: garbeam@29: void garbeam@18: resize(Client *c) garbeam@18: { garbeam@18: XConfigureEvent e; garbeam@18: garbeam@31: if(c->incw) garbeam@31: c->w -= (c->w - c->basew) % c->incw; garbeam@31: if(c->inch) garbeam@31: c->h -= (c->h - c->baseh) % c->inch; garbeam@31: if(c->minw && c->w < c->minw) garbeam@31: c->w = c->minw; garbeam@31: if(c->minh && c->h < c->minh) garbeam@31: c->h = c->minh; garbeam@31: if(c->maxw && c->w > c->maxw) garbeam@31: c->w = c->maxw; garbeam@31: if(c->maxh && c->h > c->maxh) garbeam@31: c->h = c->maxh; garbeam@26: resize_title(c); garbeam@20: XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); garbeam@18: e.type = ConfigureNotify; garbeam@18: e.event = c->win; garbeam@18: e.window = c->win; garbeam@20: e.x = c->x; garbeam@20: e.y = c->y; garbeam@20: e.width = c->w; garbeam@20: 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@18: XFlush(dpy); garbeam@18: } garbeam@18: garbeam@10: static int garbeam@33: dummy_error_handler(Display *dsply, XErrorEvent *err) garbeam@10: { garbeam@10: return 0; 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@10: XSetErrorHandler(dummy_error_handler); 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@10: XFlush(dpy); garbeam@10: XSetErrorHandler(error_handler); garbeam@10: XUngrabServer(dpy); garbeam@46: arrange(NULL); garbeam@50: if(sel) garbeam@50: focus(sel); garbeam@10: } garbeam@10: garbeam@23: Client * garbeam@23: gettitle(Window w) garbeam@23: { garbeam@23: Client *c; garbeam@50: for(c = clients; c; c = c->next) garbeam@23: if(c->title == w) garbeam@23: return c; garbeam@23: return NULL; garbeam@23: } garbeam@10: garbeam@9: Client * garbeam@9: getclient(Window w) garbeam@9: { garbeam@9: Client *c; garbeam@50: for(c = clients; c; c = c->next) garbeam@9: if(c->win == w) garbeam@9: return c; garbeam@9: return NULL; garbeam@9: } garbeam@13: garbeam@14: void garbeam@14: draw_client(Client *c) garbeam@14: { garbeam@31: int i; garbeam@50: if(c == sel) garbeam@26: return; garbeam@14: garbeam@34: dc.x = dc.y = 0; garbeam@34: dc.h = c->th; garbeam@14: garbeam@34: dc.w = 0; garbeam@31: for(i = 0; i < TLast; i++) { garbeam@31: if(c->tags[i]) { garbeam@34: dc.x += dc.w; garbeam@43: dc.w = textw(c->tags[i]) + dc.font.height; garbeam@34: draw(True, c->tags[i]); garbeam@31: } garbeam@31: } garbeam@34: dc.x += dc.w; garbeam@43: dc.w = textw(c->name) + dc.font.height; garbeam@34: draw(True, c->name); garbeam@34: XCopyArea(dpy, dc.drawable, c->title, dc.gc, garbeam@22: 0, 0, c->tw, c->th, 0, 0); garbeam@21: XFlush(dpy); garbeam@14: }