#include "dwm.h" /* global variables */ char broken[] = ""; char stext[256]; int scanner; int screen; int sw, sh; /* X display screen geometry width, height */ int bh, blw = 0; /* bar geometry */ int lrpad; /* sum of left and right padding for text */ int (*xerrorxlib)(Display *, XErrorEvent *); uint numlockmask = 0; void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, [ClientMessage] = clientmessage, [ConfigureRequest] = configurerequest, [ConfigureNotify] = configurenotify, [DestroyNotify] = destroynotify, [EnterNotify] = enternotify, [Expose] = expose, [FocusIn] = focusin, [KeyPress] = keypress, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [MotionNotify] = motionnotify, [PropertyNotify] = propertynotify, [UnmapNotify] = unmapnotify }; xcb_connection_t *xcon; Atom wmatom[WMLast] = {0}, netatom[NetLast] = {0}; int running = 1; Cur *cursor[MouseLast] = {0}; Clr **scheme = nil; Display *dpy = nil; Drw *drw = nil; Monitor *mons = nil, *selmon = nil; Window root = {0}, wmcheckwin = {0}; /* compile-time check if all tags fit into an uint bit array. */ struct NumTags { char limitexceeded[arrlen(tags) > 31 ? -1 : 1]; }; /* function implementations */ void arrange(Monitor *m) { if (m) showhide(m->stack); else for (m = mons; m; m = m->next) showhide(m->stack); if (m) { arrangemon(m); restack(m); } else for (m = mons; m; m = m->next) arrangemon(m); } void arrangemon(Monitor *m) { strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); } void buttonpress(XEvent *e) { uint i, x, click; Arg arg = {0}; Client *c; Monitor *m; XButtonPressedEvent *ev = &e->xbutton; click = ClkRootWin; /* focus monitor if necessary */ if ((m = wintomon(ev->window)) && m != selmon) { unfocus(selmon->sel, 1); selmon = m; focus(nil); } if (ev->window == selmon->barwin) { i = x = 0; do x += TEXTW(tags[i]); while (ev->x >= x && ++i < arrlen(tags)); if (i < arrlen(tags)) { click = ClkTagBar; arg.ui = 1 << i; } else if (ev->x < x + blw) click = ClkLtSymbol; else if (ev->x > selmon->ww - TEXTW(stext)) click = ClkStatusText; else click = ClkWinTitle; } else if ((c = wintoclient(ev->window))) { focus(c); restack(selmon); XAllowEvents(dpy, ReplayPointer, CurrentTime); click = ClkClientWin; } for (i = 0; i < arrlen(buttons); i++) if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); } void checkotherwm(void) { xerrorxlib = XSetErrorHandler(xerrorstart); /* this causes an error if some other window manager is running */ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); XSync(dpy, False); XSetErrorHandler(xerror); XSync(dpy, False); } void cleanup(void) { Arg a = {.ui = ~0}; Layout foo = { "", nil }; Monitor *m; size_t i; view(&a); selmon->lt[selmon->sellt] = &foo; for (m = mons; m; m = m->next) while (m->stack) unmanage(m->stack, 0); XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); for (i = 0; i < MouseLast; i++) drw_cur_free(drw, cursor[i]); for (i = 0; i < arrlen(colors); i++) free(scheme[i]); XDestroyWindow(dpy, wmcheckwin); drw_free(drw); XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); } void cleanupmon(Monitor *mon) { Monitor *m; if (mon == mons) mons = mons->next; else { for (m = mons; m && m->next != mon; m = m->next); m->next = mon->next; } XUnmapWindow(dpy, mon->barwin); XDestroyWindow(dpy, mon->barwin); free(mon); } void clientmessage(XEvent *e) { XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); if (!c) return; if (cme->message_type == netatom[NetWMState]) { if (cme->data.l[1] == netatom[NetWMFullscreen] || cme->data.l[2] == netatom[NetWMFullscreen]) setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); } else if (cme->message_type == netatom[NetActiveWindow]) { if (c != selmon->sel && !c->isurgent) seturgent(c, 1); } } void configurenotify(XEvent *e) { Monitor *m; Client *c; XConfigureEvent *ev = &e->xconfigure; int dirty; /* TODO: updategeom handling sucks, needs to be simplified */ if (ev->window == root) { dirty = (sw != ev->width || sh != ev->height); sw = ev->width; sh = ev->height; if (updategeom() || dirty) { drw_resize(drw, sw, bh); updatebars(); for (m = mons; m; m = m->next) { for (c = m->clients; c; c = c->next) if (c->isfullscreen) resizeclient(c, m->mx, m->my, m->mw, m->mh); XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); } focus(nil); arrange(nil); } } } void configurerequest(XEvent *e) { Client *c; Monitor *m; XConfigureRequestEvent *ev = &e->xconfigurerequest; XWindowChanges wc; if ((c = wintoclient(ev->window))) { if (ev->value_mask & CWBorderWidth) c->bw = ev->border_width; else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { m = c->mon; if (ev->value_mask & CWX) { c->oldx = c->x; c->x = m->mx + ev->x; } if (ev->value_mask & CWY) { c->oldy = c->y; c->y = m->my + ev->y; } if (ev->value_mask & CWWidth) { c->oldw = c->w; c->w = ev->width; } if (ev->value_mask & CWHeight) { c->oldh = c->h; c->h = ev->height; } if ((c->x + c->w) > m->mx + m->mw && c->isfloating) c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ if ((c->y + c->h) > m->my + m->mh && c->isfloating) c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) configure(c); if (ISVISIBLE(c)) XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); } else configure(c); } else { wc.x = ev->x; wc.y = ev->y; wc.width = ev->width; wc.height = ev->height; wc.border_width = ev->border_width; wc.sibling = ev->above; wc.stack_mode = ev->detail; XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); } XSync(dpy, False); } Monitor * createmon(void) { Monitor *m; m = ecalloc(1, sizeof(Monitor)); m->tagset[0] = m->tagset[1] = 1; m->mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; m->topbar = topbar; m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % arrlen(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); return m; } void destroynotify(XEvent *e) { Client *c; XDestroyWindowEvent *ev = &e->xdestroywindow; if ((c = wintoclient(ev->window))) unmanage(c, 1); else if ((c = swallowing(ev->window))) unmanage(c->swallowing, 1); } Monitor * dirtomon(int dir) { Monitor *m = nil; if (dir > 0) { if (!(m = selmon->next)) m = mons; } else if (selmon == mons) for (m = mons; m->next; m = m->next); else for (m = mons; m->next != selmon; m = m->next); return m; } void drawbar(Monitor *m) { int x, w, tw = 0; int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; uint i, occ = 0, urg = 0; Client *c; /* draw status first so it can be overdrawn by tags later */ if(m == selmon) { /* status is only drawn on selected monitor */ drw_setscheme(drw, scheme[SchemeNorm]); tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); } for(c = m->clients; c; c = c->next) { occ |= c->tags; if (c->isurgent) urg |= c->tags; } x = 0; for(i = 0; i < arrlen(tags); i++) { w = TEXTW(tags[i]); drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); if (occ & 1 << i) drw_rect(drw, x + boxs, boxs, boxw, boxw, m == selmon && selmon->sel && selmon->sel->tags & 1 << i, urg & 1 << i); x += w; } w = blw = TEXTW(m->ltsymbol); drw_setscheme(drw, scheme[SchemeNorm]); x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); if((w = m->ww - tw - x) > bh) { if (m->sel) { drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); if (m->sel->isfloating) drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); } else { drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, x, 0, w, bh, 1, 1); } } drw_map(drw, m->barwin, 0, 0, m->ww, bh); } void drawbars(void) { Monitor *m; for (m = mons; m; m = m->next) drawbar(m); } void enternotify(XEvent *e) { Client *c; Monitor *m; XCrossingEvent *ev = &e->xcrossing; if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) return; c = wintoclient(ev->window); m = c ? c->mon : wintomon(ev->window); if (m != selmon) { unfocus(selmon->sel, 1); selmon = m; } else if (!c || c == selmon->sel) return; focus(c); } void expose(XEvent *e) { Monitor *m; XExposeEvent *ev = &e->xexpose; if (ev->count == 0 && (m = wintomon(ev->window))) drawbar(m); } /* there are some broken focus acquiring clients needing extra handling */ void focusin(XEvent *e) { XFocusChangeEvent *ev = &e->xfocus; if (selmon->sel && ev->window != selmon->sel->win) setfocus(selmon->sel); } int getrootptr(int *x, int *y) { int di; uint dui; Window dummy; return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); } long getstate(Window w) { int format; long result = -1; unsigned char *p = nil; unsigned long n, extra; Atom real; if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], &real, &format, &n, &extra, (unsigned char **)&p) != Success) return -1; if (n != 0) result = *p; XFree(p); return result; } int gettextprop(Window w, Atom atom, char *text, uint size) { char **list = nil; int n; XTextProperty name; if (!text || size == 0) return 0; text[0] = '\0'; if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) return 0; if (name.encoding == XA_STRING) strncpy(text, (char *)name.value, size - 1); else { if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { strncpy(text, *list, size - 1); XFreeStringList(list); } } text[size - 1] = '\0'; XFree(name.value); return 1; } void grabkeys(void) { updatenumlockmask(); { uint i, j; uint modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; KeyCode code; XUngrabKey(dpy, AnyKey, AnyModifier, root); for (i = 0; i < arrlen(keys); i++) if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) for (j = 0; j < arrlen(modifiers); j++) XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, True, GrabModeAsync, GrabModeAsync); } } static int isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) { while (n--) if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org && unique[n].width == info->width && unique[n].height == info->height) return 0; return 1; } void keypress(XEvent *e) { uint i; KeySym keysym; XKeyEvent *ev; ev = &e->xkey; keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); for (i = 0; i < arrlen(keys); i++) if (keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].func) keys[i].func(&(keys[i].arg)); } void manage(Window w, XWindowAttributes *wa) { Client *c, *t = nil, *term = nil; Window trans = None; XWindowChanges wc; c = ecalloc(1, sizeof(Client)); c->win = w; c->pid = winpid(w); /* geometry */ c->x = c->oldx = wa->x; c->y = c->oldy = wa->y; c->w = c->oldw = wa->width; c->h = c->oldh = wa->height; c->oldbw = wa->border_width; updatetitle(c); if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { c->mon = t->mon; c->tags = t->tags; } else { c->mon = selmon; applyrules(c); term = termof(c); } if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) c->x = c->mon->mx + c->mon->mw - WIDTH(c); if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) c->y = c->mon->my + c->mon->mh - HEIGHT(c); c->x = MAX(c->x, c->mon->mx); /* only fix client y-offset, if the client center might cover the bar */ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); c->bw = borderpx; selmon->tagset[selmon->seltags] &= ~scratchtag; if(!strcmp(c->name, scratchname)) { c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag; c->isfloating = 1; c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); } wc.border_width = c->bw; XConfigureWindow(dpy, w, CWBorderWidth, &wc); XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); configure(c); /* propagates border_width, if size doesn't change */ updatewindowtype(c); updatesizehints(c); updatewmhints(c); XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); grabbuttons(c, 0); if (!c->isfloating) c->isfloating = c->oldstate = trans != None || c->isfixed; if (c->isfloating) XRaiseWindow(dpy, c->win); /* attach(c); */ attachbottom(c); attachstack(c); XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ setclientstate(c, NormalState); if (c->mon == selmon) unfocus(selmon->sel, 0); c->mon->sel = c; arrange(c->mon); XMapWindow(dpy, c->win); if (term) swallow(term, c); focus(nil); } void mappingnotify(XEvent *e) { XMappingEvent *ev = &e->xmapping; XRefreshKeyboardMapping(ev); if (ev->request == MappingKeyboard) grabkeys(); } void maprequest(XEvent *e) { static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; if (!XGetWindowAttributes(dpy, ev->window, &wa)) return; if (wa.override_redirect) return; if (!wintoclient(ev->window)) manage(ev->window, &wa); } void monocle(Monitor *m) { uint n = 0; Client *c; for (c = m->clients; c; c = c->next) if (ISVISIBLE(c)) n++; if (n > 0) /* override layout symbol */ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); } void motionnotify(XEvent *e) { static Monitor *mon = nil; Monitor *m; XMotionEvent *ev = &e->xmotion; if (ev->window != root) return; if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { unfocus(selmon->sel, 1); selmon = m; focus(nil); } mon = m; } void propertynotify(XEvent *e) { Client *c; Window trans; XPropertyEvent *ev = &e->xproperty; if ((ev->window == root) && (ev->atom == XA_WM_NAME)) updatestatus(); else if (ev->state == PropertyDelete) return; /* ignore */ else if ((c = wintoclient(ev->window))) { switch(ev->atom) { default: break; case XA_WM_TRANSIENT_FOR: if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && (c->isfloating = (wintoclient(trans)) != nil)) arrange(c->mon); break; case XA_WM_NORMAL_HINTS: updatesizehints(c); break; case XA_WM_HINTS: updatewmhints(c); drawbars(); break; } if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { updatetitle(c); if (c == c->mon->sel) drawbar(c->mon); } if (ev->atom == netatom[NetWMWindowType]) updatewindowtype(c); } } Monitor * recttomon(int x, int y, int w, int h) { Monitor *m, *r = selmon; int a, area = 0; for (m = mons; m; m = m->next) if ((a = INTERSECT(x, y, w, h, m)) > area) { area = a; r = m; } return r; } void restack(Monitor *m) { Client *c; XEvent ev; XWindowChanges wc; drawbar(m); if (!m->sel) return; if (m->sel->isfloating || !m->lt[m->sellt]->arrange) XRaiseWindow(dpy, m->sel->win); if (m->lt[m->sellt]->arrange) { wc.stack_mode = Below; wc.sibling = m->barwin; for (c = m->stack; c; c = c->snext) if (!c->isfloating && ISVISIBLE(c)) { XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); wc.sibling = c->win; } } XSync(dpy, False); while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } void run(void) { XEvent ev; /* main event loop */ XSync(dpy, False); while (running && !XNextEvent(dpy, &ev)) if (handler[ev.type]) handler[ev.type](&ev); /* call handler */ } void scan(void) { uint i, num; Window d1, d2, *wins = nil; XWindowAttributes wa; char swin[256]; scanner = 1; if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { for (i = 0; i < num; i++) { if (!XGetWindowAttributes(dpy, wins[i], &wa) || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) continue; if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) manage(wins[i], &wa); else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) manage(wins[i], &wa); } for (i = 0; i < num; i++) { /* now the transients */ if (!XGetWindowAttributes(dpy, wins[i], &wa)) continue; if (XGetTransientForHint(dpy, wins[i], &d1) && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) manage(wins[i], &wa); } if (wins) XFree(wins); } scanner = 0; } void setup(void) { int i; XSetWindowAttributes wa; Atom utf8string; /* clean up any zombies immediately */ sigchld(0); /* init screen */ screen = DefaultScreen(dpy); sw = DisplayWidth(dpy, screen); sh = DisplayHeight(dpy, screen); root = RootWindow(dpy, screen); drw = drw_create(dpy, screen, root, sw, sh); if (!drw_fontset_create(drw, fonts, arrlen(fonts))) fatal("no fonts could be loaded."); lrpad = drw->fonts->h; bh = drw->fonts->h + 2; updategeom(); /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowOpacity] = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); /* init cursors */ cursor[MouseNormal] = drw_cur_create(drw, XC_left_ptr); cursor[MouseResize] = drw_cur_create(drw, XC_sizing); cursor[MouseMove] = drw_cur_create(drw, XC_fleur); /* init appearance */ scheme = ecalloc(arrlen(colors), sizeof(Clr *)); for (i = 0; i < arrlen(colors); i++) scheme[i] = drw_scm_create(drw, colors[i], 3); /* init bars */ updatebars(); updatestatus(); /* supporting window for NetWMCheck */ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, PropModeReplace, (uchar *) &wmcheckwin, 1); XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, PropModeReplace, (uchar *) "dwm", 3); XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, PropModeReplace, (uchar *) &wmcheckwin, 1); /* EWMH support per view */ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, PropModeReplace, (uchar *) netatom, NetLast); XDeleteProperty(dpy, root, netatom[NetClientList]); /* select events */ wa.cursor = cursor[MouseNormal]->cursor; wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask |ButtonPressMask|PointerMotionMask|EnterWindowMask |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); grabkeys(); focus(nil); } void sigchld(int unused) { if (signal(SIGCHLD, sigchld) == SIG_ERR) fatal("can't install SIGCHLD handler:"); while (0 < waitpid(-1, nil, WNOHANG)); } Client * swallowing(Window w) { Client *c; Monitor *m; for (m = mons; m; m = m->next) { for (c = m->clients; c; c = c->next) { if (c->swallowing && c->swallowing->win == w) return c; } } return nil; } void tile(Monitor *m) { uint i, n, h, r, mw, my, ty; Client *c; for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) ; if (n == 0) return; if (n > m->nmaster) mw = m->nmaster ? (m->ww+gapx) * m->mfact : 0; else mw = m->ww - gapx; for (i = 0, my = ty = gapx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) if (i < m->nmaster) { r = MIN(n, m->nmaster) - i; h = (m->wh - my)/r - gapx; resize(c, m->wx + gapx, m->wy + my, mw - (2*c->bw) - gapx, h - (2*c->bw), 0); if (my + HEIGHT(c) + gapx < m->wh) my += HEIGHT(c) + gapx; } else { r = (n-i); h = (m->wh - ty)/r - gapx; resize(c, m->wx + mw + gapx, m->wy + ty, m->ww - mw - (2*c->bw) - (2*gapx), h - (2*c->bw), 0); if (ty + HEIGHT(c) + gapx < m->wh) ty += HEIGHT(c) + gapx; } } void unmapnotify(XEvent *e) { Client *c; XUnmapEvent *ev = &e->xunmap; if ((c = wintoclient(ev->window))) { if (ev->send_event) setclientstate(c, WithdrawnState); else unmanage(c, 0); } } void updatebars(void) { Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, .background_pixmap = ParentRelative, .event_mask = ButtonPressMask|ExposureMask }; XClassHint ch = {"dwm", "dwm"}; for (m = mons; m; m = m->next) { if (m->barwin) continue; m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); XDefineCursor(dpy, m->barwin, cursor[MouseNormal]->cursor); XMapRaised(dpy, m->barwin); XSetClassHint(dpy, m->barwin, &ch); } } void updatebarpos(Monitor *m) { m->wy = m->my; m->wh = m->mh; if (m->showbar) { m->wh -= bh; m->by = m->topbar ? m->wy : m->wy + m->wh; m->wy = m->topbar ? m->wy + bh : m->wy; } else m->by = -bh; } void updateclientlist() { Client *c; Monitor *m; XDeleteProperty(dpy, root, netatom[NetClientList]); for (m = mons; m; m = m->next) for (c = m->clients; c; c = c->next) XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); } int updategeom(void) { int dirty = 0; if (XineramaIsActive(dpy)) { int i, j, n, nn; Client *c; Monitor *m; XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); XineramaScreenInfo *unique = nil; for (n = 0, m = mons; m; m = m->next, n++); /* only consider unique geometries as separate screens */ unique = ecalloc(nn, sizeof(XineramaScreenInfo)); for (i = 0, j = 0; i < nn; i++) if (isuniquegeom(unique, j, &info[i])) memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); XFree(info); nn = j; if (n <= nn) { /* new monitors available */ for (i = 0; i < (nn - n); i++) { for (m = mons; m && m->next; m = m->next); if (m) m->next = createmon(); else mons = createmon(); } for (i = 0, m = mons; i < nn && m; m = m->next, i++) if (i >= n || unique[i].x_org != m->mx || unique[i].y_org != m->my || unique[i].width != m->mw || unique[i].height != m->mh) { dirty = 1; m->num = i; m->mx = m->wx = unique[i].x_org; m->my = m->wy = unique[i].y_org; m->mw = m->ww = unique[i].width; m->mh = m->wh = unique[i].height; updatebarpos(m); } } else { /* less monitors available nn < n */ for (i = nn; i < n; i++) { for (m = mons; m && m->next; m = m->next); while ((c = m->clients)) { dirty = 1; m->clients = c->next; detachstack(c); c->mon = mons; /* attach(c); */ attachbottom(c); attachstack(c); } if (m == selmon) selmon = mons; cleanupmon(m); } } free(unique); } else { /* default monitor setup */ if (!mons) mons = createmon(); if (mons->mw != sw || mons->mh != sh) { dirty = 1; mons->mw = mons->ww = sw; mons->mh = mons->wh = sh; updatebarpos(mons); } } if (dirty) { selmon = mons; selmon = wintomon(root); } return dirty; } void updatenumlockmask(void) { uint i, j; XModifierKeymap *modmap; numlockmask = 0; modmap = XGetModifierMapping(dpy); for (i = 0; i < 8; i++) for (j = 0; j < modmap->max_keypermod; j++) if (modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock)) numlockmask = (1 << i); XFreeModifiermap(modmap); } void updatestatus(void) { if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) strcpy(stext, "dwm-"VERSION); drawbar(selmon); } pid_t winpid(Window w) { pid_t result = 0; xcb_res_client_id_spec_t spec = {0}; spec.client = w; spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; xcb_generic_error_t *e = NULL; xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); if (!r) return (pid_t)0; xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); for (; i.rem; xcb_res_client_id_value_next(&i)) { spec = i.data->spec; if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { uint32_t *t = xcb_res_client_id_value_value(i.data); result = *t; break; } } free(r); if (result == (pid_t)-1) result = 0; return result; } Client * wintoclient(Window w) { Client *c; Monitor *m; for (m = mons; m; m = m->next) for (c = m->clients; c; c = c->next) if (c->win == w) return c; return nil; } Monitor * wintomon(Window w) { int x, y; Client *c; Monitor *m; if (w == root && getrootptr(&x, &y)) return recttomon(x, y, 1, 1); for (m = mons; m; m = m->next) if (w == m->barwin) return m; if ((c = wintoclient(w))) return c->mon; return selmon; } /* There's no way to check accesses to destroyed windows, thus those cases are * ignored (especially on UnmapNotify's). Other types of errors call Xlibs * default error handler, which may call exit. */ int xerror(Display *dpy, XErrorEvent *ee) { if (ee->error_code == BadWindow || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) return 0; fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); return xerrorxlib(dpy, ee); /* may call exit */ } int xerrordummy(Display *dpy, XErrorEvent *ee) { return 0; } /* Startup Error handler to check if another window manager * is already running. */ int xerrorstart(Display *dpy, XErrorEvent *ee) { fatal("dwm: another window manager is already running"); return -1; } int main(int argc, char *argv[]) { if (argc == 2 && !strcmp("-v", argv[1])) fatal("dwm-"VERSION); else if (argc != 1) fatal("usage: dwm [-v]"); if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fputs("warning: no locale support\n", stderr); if (!(dpy = XOpenDisplay(nil))) fatal("dwm: cannot open display"); if (!(xcon = XGetXCBConnection(dpy))) fatal("dwm: cannot get xcb connection"); checkotherwm(); setup(); #ifdef __OpenBSD__ if (pledge("stdio rpath proc exec", nil) == -1) fatal("pledge"); #endif /* __OpenBSD__ */ scan(); run(); cleanup(); XCloseDisplay(dpy); return 0; }