garbeam@5: /* garbeam@5: * (C)opyright MMVI Anselm R. Garbe garbeam@5: * See LICENSE file for license details. garbeam@5: */ garbeam@5: garbeam@27: #include garbeam@10: #include garbeam@5: #include garbeam@5: #include garbeam@5: garbeam@5: #include "util.h" garbeam@5: #include "wm.h" garbeam@5: garbeam@27: void garbeam@27: arrange(void *aux) garbeam@27: { garbeam@27: Client *c; garbeam@27: int n, cols, rows, gw, gh, i, j; garbeam@27: float rt, fd; garbeam@27: garbeam@27: if(!clients) garbeam@27: return; garbeam@27: for(n = 0, c = clients; c; c = c->next, n++); garbeam@27: rt = sqrt(n); garbeam@27: if(modff(rt, &fd) < 0.5) garbeam@27: rows = floor(rt); garbeam@27: else garbeam@27: rows = ceil(rt); garbeam@27: if(rows * rows < n) garbeam@27: cols = rows + 1; garbeam@27: else garbeam@27: cols = rows; garbeam@27: garbeam@27: gw = (sw - 1) / cols; garbeam@27: gh = (sh - bh - 1) / rows; garbeam@27: garbeam@27: for(i = j = 0, c = clients; c; c = c->next) { garbeam@27: c->x = i * gw; garbeam@27: c->y = j * gh + bh; garbeam@27: c->w = gw; garbeam@27: c->h = gh; garbeam@27: resize(c); garbeam@27: if(++i == cols) { garbeam@27: j++; garbeam@27: i = 0; garbeam@27: } garbeam@27: } garbeam@27: } garbeam@27: garbeam@27: void garbeam@27: sel(void *aux) garbeam@27: { garbeam@27: const char *arg = aux; garbeam@27: Client *c = NULL; garbeam@27: garbeam@27: if(!arg || !stack) garbeam@27: return; garbeam@27: if(!strncmp(arg, "next", 5)) garbeam@27: c = stack->snext ? stack->snext : stack; garbeam@27: else if(!strncmp(arg, "prev", 5)) garbeam@27: for(c = stack; c && c->snext; c = c->snext); garbeam@27: if(!c) garbeam@27: c = stack; garbeam@27: raise(c); garbeam@27: focus(c); garbeam@27: } garbeam@27: garbeam@27: void garbeam@27: kill(void *aux) garbeam@27: { garbeam@27: Client *c = stack; garbeam@27: garbeam@27: if(!c) garbeam@27: return; garbeam@27: if(c->proto & WM_PROTOCOL_DELWIN) garbeam@27: send_message(c->win, wm_atom[WMProtocols], wm_atom[WMDelete]); garbeam@27: else garbeam@27: XKillClient(dpy, c->win); garbeam@27: } garbeam@27: garbeam@26: static void garbeam@26: resize_title(Client *c) garbeam@26: { garbeam@26: c->tw = textw(&brush.font, c->name) + bh; 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@20: } garbeam@20: garbeam@20: void garbeam@26: raise(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@21: Client **l, *old; garbeam@21: garbeam@21: old = stack; garbeam@26: for(l = &stack; *l && *l != c; l = &(*l)->snext); garbeam@13: eassert(*l == c); garbeam@13: *l = c->snext; garbeam@13: c->snext = stack; garbeam@13: stack = c; garbeam@21: if(old && old != c) { garbeam@21: XMapWindow(dpy, old->title); garbeam@21: draw_client(old); garbeam@21: } garbeam@21: XUnmapWindow(dpy, c->title); garbeam@27: draw_client(c); garbeam@26: XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); garbeam@13: XFlush(dpy); garbeam@13: } garbeam@13: garbeam@13: void garbeam@10: manage(Window w, XWindowAttributes *wa) garbeam@5: { garbeam@10: 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@26: if(c->y < bh) garbeam@26: c->ty = c->y += bh; garbeam@22: c->tw = c->w = wa->width; garbeam@20: c->h = wa->height; garbeam@26: c->th = bh; garbeam@20: update_size(c); garbeam@19: XSetWindowBorderWidth(dpy, c->win, 1); garbeam@22: XSetWindowBorder(dpy, c->win, brush.border); 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@21: update_name(c); garbeam@5: garbeam@10: for(l=&clients; *l; l=&(*l)->next); garbeam@10: c->next = *l; /* *l == nil */ garbeam@10: *l = c; garbeam@13: c->snext = stack; garbeam@13: stack = c; 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@21: resize(c); garbeam@13: focus(c); garbeam@5: } garbeam@9: garbeam@18: void garbeam@18: resize(Client *c) garbeam@18: { garbeam@18: XConfigureEvent e; garbeam@18: 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@19: e.border_width = 0; 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@10: dummy_error_handler(Display *dpy, XErrorEvent *error) garbeam@10: { garbeam@10: return 0; garbeam@10: } garbeam@10: garbeam@10: void garbeam@10: unmanage(Client *c) garbeam@10: { garbeam@10: Client **l; garbeam@10: 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@10: for(l=&clients; *l && *l != c; l=&(*l)->next); garbeam@10: eassert(*l == c); garbeam@10: *l = c->next; garbeam@13: for(l=&stack; *l && *l != c; l=&(*l)->snext); garbeam@13: eassert(*l == c); garbeam@13: *l = c->snext; garbeam@10: free(c); garbeam@10: garbeam@10: XFlush(dpy); garbeam@10: XSetErrorHandler(error_handler); garbeam@10: XUngrabServer(dpy); garbeam@14: if(stack) garbeam@14: focus(stack); garbeam@10: } garbeam@10: garbeam@23: Client * garbeam@23: gettitle(Window w) garbeam@23: { garbeam@23: Client *c; garbeam@23: 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@9: 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@26: if(c == stack) { garbeam@21: draw_bar(); garbeam@26: return; garbeam@26: } garbeam@14: garbeam@26: brush.x = brush.y = 0; garbeam@26: brush.w = c->tw; garbeam@26: brush.h = c->th; garbeam@14: garbeam@21: draw(dpy, &brush, True, c->name); garbeam@22: XCopyArea(dpy, brush.drawable, c->title, brush.gc, garbeam@22: 0, 0, c->tw, c->th, 0, 0); garbeam@21: XFlush(dpy); garbeam@14: }