# HG changeset patch # User paulo@localhost # Date 1237789595 25200 # Node ID 7024076fa9483facf80f50c60267187f98f179ca initial add diff -r 000000000000 -r 7024076fa948 LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,22 @@ +MIT/X Consortium License + +(C)opyright MMVI-MMVII Anselm R. Garbe +(C)opyright MMVI-MMVII Sander van Dijk + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff -r 000000000000 -r 7024076fa948 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,61 @@ +# dwm - dynamic window manager +# (C)opyright MMVI-MMVII Anselm R. Garbe + +include config.mk + +SRC = client.c draw.c event.c layout.c main.c tag.c util.c +OBJ = ${SRC:.c=.o} + +all: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: dwm.h config.h config.mk + +config.h: + @echo creating $@ from config.default.h + @cp config.default.h $@ + +dwm: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + @strip $@ + +clean: + @echo cleaning + @rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p dwm-${VERSION} + @cp -R LICENSE Makefile README config.*.h config.mk \ + dwm.1 dwm.h ${SRC} dwm-${VERSION} + @tar -cf dwm-${VERSION}.tar dwm-${VERSION} + @gzip dwm-${VERSION}.tar + @rm -rf dwm-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f dwm ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/dwm + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall diff -r 000000000000 -r 7024076fa948 README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,48 @@ +dwm - dynamic window manager +============================ +dwm is an extremely fast, small, and dynamic window manager for X. + + +Requirements +------------ +In order to build dwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dwm is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dwm (if +necessary as root): + + make clean install + + +Running dwm +----------- +Add the following line to your .xinitrc to start dwm using startx: + + exec dwm + +In order to connect dwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec dwm + +(This will start dwm on display :1 of the host foo.bar.) + +In order to display status info in the bar, you can do something +like this in your .xinitrc: + + while true + do + echo `date` `uptime | sed 's/.*,//'` + sleep 1 + done | dwm + + +Configuration +------------- +The configuration of dwm is done by creating a custom config.h +and (re)compiling the source code. diff -r 000000000000 -r 7024076fa948 client.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,517 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ +#include "dwm.h" +#include +#include +#include +#include +#include + +/* static */ + +static void +attachstack(Client *c) { + c->snext = stack; + stack = c; +} + +static void +detachstack(Client *c) { + Client **tc; + + for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); + *tc = c->snext; +} + +static void +grabbuttons(Client *c, Bool focused) { + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + + if(focused) { + XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } + else + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); +} + +static Bool +isprotodel(Client *c) { + int i, n; + Atom *protocols; + Bool ret = False; + + if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { + for(i = 0; !ret && i < n; i++) + if(protocols[i] == wmatom[WMDelete]) + ret = True; + XFree(protocols); + } + return ret; +} + +static void +setclientstate(Client *c, long state) { + long data[] = {state, None}; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +static void +togglemax(Client *c) { + XEvent ev; + + if(c->isfixed) + return; + if((c->ismax = !c->ismax)) { + c->rx = c->x; + c->ry = c->y; + c->rw = c->w; + c->rh = c->h; + resize(c, wax, way, waw - 2 * BORDERPX, wah - 2 * BORDERPX, True); + } + else + resize(c, c->rx, c->ry, c->rw, c->rh, True); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static int +xerrordummy(Display *dsply, XErrorEvent *ee) { + return 0; +} + +/* extern */ + +void +attach(Client *c) { + if(clients) + clients->prev = c; + c->next = clients; + clients = c; +} + +void +configure(Client *c) { + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->border; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +detach(Client *c) { + if(c->prev) + c->prev->next = c->next; + if(c->next) + c->next->prev = c->prev; + if(c == clients) + clients = c->next; + c->next = c->prev = NULL; +} + +void +focus(Client *c) { + if(c && !isvisible(c)) + return; + if(sel && sel != c) { + grabbuttons(sel, False); + XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); + } + if(c) { + detachstack(c); + attachstack(c); + grabbuttons(c, True); + } + sel = c; + drawstatus(); + if(!selscreen) + return; + if(c) { + XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + } + else + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +} + +void +killclient(Arg *arg) { + if(!sel) + return; + if(isprotodel(sel)) + sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); + else + XKillClient(dpy, sel->win); +} + +void +manage(Window w, XWindowAttributes *wa) { + Client *c, *t; + Window trans; + XWindowChanges wc; + + c = emallocz(sizeof(Client)); + c->tags = emallocz(ntags * sizeof(Bool)); + c->win = w; + c->x = wa->x; + c->y = wa->y; + c->w = wa->width; + c->h = wa->height; + if(c->w == sw && c->h == sh) { + c->border = 0; + c->x = sx; + c->y = sy; + } + else { + c->border = BORDERPX; + if(c->x > wax + waw && c-> w < waw) + c->x = wax + waw - c->w - 2 * c->border; + if(c->y > way + wah && c-> h < wah) + c->y = way + wah - c->h - 2 * c->border; + if(c->x < wax && c->w < waw) + c->x = wax; + if(c->y < way && c->h < wah) + c->y = way; + } + XSelectInput(dpy, w, + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); + XGetTransientForHint(dpy, w, &trans); + grabbuttons(c, False); + wc.border_width = c->border; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, dc.norm[ColBorder]); + configure(c); /* propagates border_width, if size doesn't change */ + updatetitle(c); + for(t = clients; t && t->win != trans; t = t->next); + settags(c, t); + if(!c->isversatile) + c->isversatile = (t != NULL) || c->isfixed; + attach(c); + attachstack(c); + c->isbanned = True; + XMoveWindow(dpy, w, c->x + 2 * sw, c->y); + XMapWindow(dpy, w); + setclientstate(c, NormalState); + if(isvisible(c)) + focus(c); + updatesizehints(c); + lt->arrange(); +} + +void +resize(Client *c, int x, int y, int w, int h, Bool sizehints) { + float actual, dx, dy, max, min; + XWindowChanges wc; + + if(w <= 0 || h <= 0) + return; + if(sizehints) { + if(c->minw && w < c->minw) + w = c->minw; + if(c->minh && h < c->minh) + h = c->minh; + if(c->maxw && w > c->maxw) + w = c->maxw; + if(c->maxh && h > c->maxh) + h = c->maxh; + /* inspired by algorithm from fluxbox */ + if(c->minay > 0 && c->maxay && (h - c->baseh) > 0) { + dx = (float)(w - c->basew); + dy = (float)(h - c->baseh); + min = (float)(c->minax) / (float)(c->minay); + max = (float)(c->maxax) / (float)(c->maxay); + actual = dx / dy; + if(max > 0 && min > 0 && actual > 0) { + if(actual < min) { + dy = (dx * min + dy) / (min * min + 1); + dx = dy * min; + w = (int)dx + c->basew; + h = (int)dy + c->baseh; + } + else if(actual > max) { + dy = (dx * min + dy) / (max * max + 1); + dx = dy * min; + w = (int)dx + c->basew; + h = (int)dy + c->baseh; + } + } + } + if(c->incw) + w -= (w - c->basew) % c->incw; + if(c->inch) + h -= (h - c->baseh) % c->inch; + } + if(w == sw && h == sh) + c->border = 0; + else + c->border = BORDERPX; + /* offscreen appearance fixes */ + if(x > sw) + x = sw - w - 2 * c->border; + if(y > sh) + y = sh - h - 2 * c->border; + if(x + w + 2 * c->border < sx) + x = sx; + if(y + h + 2 * c->border < sy) + y = sy; + /* if(c->x != x || c->y != y || c->w != w || c->h != h) { */ + c->x = wc.x = x; + c->y = wc.y = y; + c->w = wc.width = w; + c->h = wc.height = h; + wc.border_width = c->border; + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); + /* } */ +} + +void +toggleversatile(Arg *arg) { + if(!sel || lt->arrange == versatile) + return; + sel->isversatile = !sel->isversatile; + lt->arrange(); +} + +void +updatesizehints(Client *c) { + long msize; + XSizeHints size; + + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) + size.flags = PSize; + c->flags = size.flags; + if(c->flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } + else + c->basew = c->baseh = 0; + if(c->flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } + else + c->incw = c->inch = 0; + if(c->flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } + else + c->maxw = c->maxh = 0; + if(c->flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } + else + c->minw = c->minh = 0; + if(c->flags & PAspect) { + c->minax = size.min_aspect.x; + c->minay = size.min_aspect.y; + c->maxax = size.max_aspect.x; + c->maxay = size.max_aspect.y; + } + else + c->minax = c->minay = c->maxax = c->maxay = 0; + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh + && c->maxw == c->minw && c->maxh == c->minh); +} + +void +updatetitle(Client *c) { + char **list = NULL; + int n; + XTextProperty name; + + name.nitems = 0; + c->name[0] = 0; + XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); + if(!name.nitems) + XGetWMName(dpy, c->win, &name); + if(!name.nitems) + return; + if(name.encoding == XA_STRING) + strncpy(c->name, (char *)name.value, sizeof c->name); + else { + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success + && n > 0 && *list) + { + strncpy(c->name, *list, sizeof c->name); + XFreeStringList(list); + } + } + XFree(name.value); +} + +void +unmanage(Client *c) { + Client *nc; + + /* The server grab construct avoids race conditions. */ + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + detach(c); + detachstack(c); + if(sel == c) { + for(nc = stack; nc && !isvisible(nc); nc = nc->snext); + focus(nc); + } + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + free(c->tags); + free(c); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + lt->arrange(); +} + +void +zoom(Arg *arg) { + unsigned int n; + Client *c; + + if(!sel) + return; + if(sel->isversatile || (lt->arrange == versatile)) { + togglemax(sel); + return; + } + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + if((c = sel) == nexttiled(clients)) + if(!(c = nexttiled(c->next))) + return; + detach(c); + attach(c); + focus(c); + lt->arrange(); +} + +Client * +prevtiled(Client *c) { + for(; c && (c->isversatile || !isvisible(c)); c = c->prev); + return c; +} + +void +pushup(Arg *arg) { + Client *c; + + if(!sel || sel->isversatile) + return; + if((c = prevtiled(sel->prev))) { + /* attach before c */ + detach(sel); + sel->next = c; + sel->prev = c->prev; + c->prev = sel; + if(sel->prev) + sel->prev->next = sel; + else + clients = sel; + } else { + /* move to the end */ + for(c = sel; c->next; c = c->next); + detach(sel); + sel->prev = c; + c->next = sel; + } + focus(sel); + lt->arrange(); +} + +void +pushdown(Arg *arg) { + Client *c; + + if(!sel || sel->isversatile) + return; + if((c = nexttiled(sel->next))) { + /* attach after c */ + detach(sel); + sel->prev = c; + sel->next = c->next; + c->next = sel; + if(sel->next) + sel->next->prev = sel; + } else { + /* move to the front */ + detach(sel); + attach(sel); + } + focus(sel); + lt->arrange(); +} + +void +moveresize(Arg *arg) { + int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh; + char xAbs, yAbs, wAbs, hAbs; + int mx, my, dx, dy, nmx, nmy; + unsigned int dui; + Window dummy; + + if (lt->arrange != versatile) + if (!sel || !sel->isversatile || !arg) + return; + if(sscanf(arg->cmd, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8) + return; + nx = xAbs == 'X' ? x : sel->x + x; + ny = yAbs == 'Y' ? y : sel->y + y; + nw = wAbs == 'W' ? w : sel->w + w; + nh = hAbs == 'H' ? h : sel->h + h; + + ox = sel->x; + oy = sel->y; + ow = sel->w; + oh = sel->h; + + Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &mx, &my, &dx, &dy, &dui); + resize(sel, nx, ny, nw, nh, True); + if (xqp && ox <= mx && (ox + ow) >= mx && oy <= my && (oy + oh) >= my) + { + nmx = mx-ox+sel->w-ow-1 < 0 ? 0 : mx-ox+sel->w-ow-1; + nmy = my-oy+sel->h-oh-1 < 0 ? 0 : my-oy+sel->h-oh-1; + XWarpPointer(dpy, None, sel->win, 0, 0, 0, 0, nmx, nmy); + } +} + diff -r 000000000000 -r 7024076fa948 config.arg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.arg.h Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,99 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ + +/* appearance */ +#define BORDERPX 1 +#define FONT "-*-terminus-medium-r-*-*-14-*-*-*-*-*-*-*" +#define NORMBORDERCOLOR "#333" +#define NORMBGCOLOR "#222" +#define NORMFGCOLOR "#ccc" +#define SELBORDERCOLOR "#69c" +#define SELBGCOLOR "#555" +#define SELFGCOLOR "#fff" +#define TOPBAR True /* False */ + +/* behavior */ +#define SNAP 40 /* pixel */ +#define TAGS \ +const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; +#define RULES \ +static Rule rule[] = { \ + /* class:instance:title regex tags regex isversatile */ \ + { "Firefox", "3", False }, \ + { "Gimp", NULL, True }, \ + { "MPlayer", NULL, True }, \ + { "Acroread", NULL, True }, \ +}; + +/* layout(s) */ +#define LAYOUTS \ +static Layout layout[] = { \ + /* symbol function */ \ + { "[]=", tile }, /* first entry is default */ \ + { "><>", versatile }, \ +}; +#define MASTER 600 /* per thousand */ +#define NMASTER 1 /* clients in master area */ + +/* key definitions */ +#define MODKEY Mod1Mask +#define KEYS \ +static Key key[] = { \ + /* modifier key function argument */ \ + { MODKEY|ShiftMask, XK_Return, spawn, \ + { .cmd = "exec uxterm -bg '#222' -fg '#eee' -cr '#eee' +sb -fn '"FONT"'" } }, \ + { MODKEY, XK_p, spawn, \ + { .cmd = "exe=\"$(lsx `echo $PATH | sed 's/:/ /g'` | sort -u " \ + " | dmenu -fn '"FONT"' -nb '"NORMBGCOLOR"' -nf '"NORMFGCOLOR"' " \ + "-sb '"SELBGCOLOR"' -sf '"SELFGCOLOR"')\" && exec $exe" } }, \ + { MODKEY, XK_j, focusnext, { 0 } }, \ + { MODKEY, XK_k, focusprev, { 0 } }, \ + { MODKEY, XK_Return, zoom, { 0 } }, \ + { MODKEY, XK_g, resizemaster, { .i = 15 } }, \ + { MODKEY, XK_s, resizemaster, { .i = -15 } }, \ + { MODKEY, XK_i, incnmaster, { .i = 1 } }, \ + { MODKEY, XK_d, incnmaster, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_0, tag, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_1, tag, { .i = 0 } }, \ + { MODKEY|ShiftMask, XK_2, tag, { .i = 1 } }, \ + { MODKEY|ShiftMask, XK_3, tag, { .i = 2 } }, \ + { MODKEY|ShiftMask, XK_4, tag, { .i = 3 } }, \ + { MODKEY|ShiftMask, XK_5, tag, { .i = 4 } }, \ + { MODKEY|ShiftMask, XK_6, tag, { .i = 5 } }, \ + { MODKEY|ShiftMask, XK_7, tag, { .i = 6 } }, \ + { MODKEY|ShiftMask, XK_8, tag, { .i = 7 } }, \ + { MODKEY|ShiftMask, XK_9, tag, { .i = 8 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_1, toggletag, { .i = 0 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_2, toggletag, { .i = 1 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_3, toggletag, { .i = 2 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_4, toggletag, { .i = 3 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_5, toggletag, { .i = 4 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_6, toggletag, { .i = 5 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_7, toggletag, { .i = 6 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_8, toggletag, { .i = 7 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_9, toggletag, { .i = 8 } }, \ + { MODKEY|ShiftMask, XK_c, killclient, { 0 } }, \ + { MODKEY, XK_space, setlayout, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_space, toggleversatile,{ 0 } }, \ + { MODKEY, XK_0, view, { .i = -1 } }, \ + { MODKEY, XK_1, view, { .i = 0 } }, \ + { MODKEY, XK_2, view, { .i = 1 } }, \ + { MODKEY, XK_3, view, { .i = 2 } }, \ + { MODKEY, XK_4, view, { .i = 3 } }, \ + { MODKEY, XK_5, view, { .i = 4 } }, \ + { MODKEY, XK_6, view, { .i = 5 } }, \ + { MODKEY, XK_7, view, { .i = 6 } }, \ + { MODKEY, XK_8, view, { .i = 7 } }, \ + { MODKEY, XK_9, view, { .i = 8 } }, \ + { MODKEY|ControlMask, XK_1, toggleview, { .i = 0 } }, \ + { MODKEY|ControlMask, XK_2, toggleview, { .i = 1 } }, \ + { MODKEY|ControlMask, XK_3, toggleview, { .i = 2 } }, \ + { MODKEY|ControlMask, XK_4, toggleview, { .i = 3 } }, \ + { MODKEY|ControlMask, XK_5, toggleview, { .i = 4 } }, \ + { MODKEY|ControlMask, XK_6, toggleview, { .i = 5 } }, \ + { MODKEY|ControlMask, XK_7, toggleview, { .i = 6 } }, \ + { MODKEY|ControlMask, XK_8, toggleview, { .i = 7 } }, \ + { MODKEY|ControlMask, XK_9, toggleview, { .i = 8 } }, \ + { MODKEY|ShiftMask, XK_q, quit, { 0 } }, \ +}; diff -r 000000000000 -r 7024076fa948 config.default.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.default.h Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,95 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ + +/* appearance */ +#define BORDERPX 1 +#define FONT "-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*" +#define NORMBORDERCOLOR "#dddddd" +#define NORMBGCOLOR "#eeeeee" +#define NORMFGCOLOR "#222222" +#define SELBORDERCOLOR "#ff0000" +#define SELBGCOLOR "#006699" +#define SELFGCOLOR "#ffffff" +#define TOPBAR True /* False */ + +/* behavior */ +#define SNAP 20 /* pixel */ +#define TAGS \ +const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; +/* Query class:instance:title for regex matching info with following command: + * xprop | awk -F '"' '/^WM_CLASS/ { printf("%s:%s:",$4,$2) }; /^WM_NAME/ { printf("%s\n",$2) }' */ +#define RULES \ +static Rule rule[] = { \ + /* class:instance:title regex tags regex isversatile */ \ + { "Gimp", NULL, True }, \ + { "MPlayer", NULL, True }, \ + { "Acroread", NULL, True }, \ +}; + +/* layout(s) */ +#define LAYOUTS \ +static Layout layout[] = { \ + /* symbol function */ \ + { "[]=", tile }, /* first entry is default */ \ + { "><>", versatile }, \ +}; +#define MASTER 600 /* per thousand */ +#define NMASTER 1 /* clients in master area */ + +/* key definitions */ +#define MODKEY Mod1Mask +#define KEYS \ +static Key key[] = { \ + /* modifier key function argument */ \ + { MODKEY|ShiftMask, XK_Return, spawn, { .cmd = "exec xterm" } }, \ + { MODKEY, XK_Tab, focusnext, { 0 } }, \ + { MODKEY|ShiftMask, XK_Tab, focusprev, { 0 } }, \ + { MODKEY, XK_Return, zoom, { 0 } }, \ + { MODKEY, XK_g, resizemaster, { .i = 15 } }, \ + { MODKEY, XK_s, resizemaster, { .i = -15 } }, \ + { MODKEY, XK_i, incnmaster, { .i = 1 } }, \ + { MODKEY, XK_d, incnmaster, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_0, tag, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_1, tag, { .i = 0 } }, \ + { MODKEY|ShiftMask, XK_2, tag, { .i = 1 } }, \ + { MODKEY|ShiftMask, XK_3, tag, { .i = 2 } }, \ + { MODKEY|ShiftMask, XK_4, tag, { .i = 3 } }, \ + { MODKEY|ShiftMask, XK_5, tag, { .i = 4 } }, \ + { MODKEY|ShiftMask, XK_6, tag, { .i = 5 } }, \ + { MODKEY|ShiftMask, XK_7, tag, { .i = 6 } }, \ + { MODKEY|ShiftMask, XK_8, tag, { .i = 7 } }, \ + { MODKEY|ShiftMask, XK_9, tag, { .i = 8 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_1, toggletag, { .i = 0 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_2, toggletag, { .i = 1 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_3, toggletag, { .i = 2 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_4, toggletag, { .i = 3 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_5, toggletag, { .i = 4 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_6, toggletag, { .i = 5 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_7, toggletag, { .i = 6 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_8, toggletag, { .i = 7 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_9, toggletag, { .i = 8 } }, \ + { MODKEY|ShiftMask, XK_c, killclient, { 0 } }, \ + { MODKEY, XK_space, setlayout, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_space, toggleversatile,{ 0 } }, \ + { MODKEY, XK_0, view, { .i = -1 } }, \ + { MODKEY, XK_1, view, { .i = 0 } }, \ + { MODKEY, XK_2, view, { .i = 1 } }, \ + { MODKEY, XK_3, view, { .i = 2 } }, \ + { MODKEY, XK_4, view, { .i = 3 } }, \ + { MODKEY, XK_5, view, { .i = 4 } }, \ + { MODKEY, XK_6, view, { .i = 5 } }, \ + { MODKEY, XK_7, view, { .i = 6 } }, \ + { MODKEY, XK_8, view, { .i = 7 } }, \ + { MODKEY, XK_9, view, { .i = 8 } }, \ + { MODKEY|ControlMask, XK_1, toggleview, { .i = 0 } }, \ + { MODKEY|ControlMask, XK_2, toggleview, { .i = 1 } }, \ + { MODKEY|ControlMask, XK_3, toggleview, { .i = 2 } }, \ + { MODKEY|ControlMask, XK_4, toggleview, { .i = 3 } }, \ + { MODKEY|ControlMask, XK_5, toggleview, { .i = 4 } }, \ + { MODKEY|ControlMask, XK_6, toggleview, { .i = 5 } }, \ + { MODKEY|ControlMask, XK_7, toggleview, { .i = 6 } }, \ + { MODKEY|ControlMask, XK_8, toggleview, { .i = 7 } }, \ + { MODKEY|ControlMask, XK_9, toggleview, { .i = 8 } }, \ + { MODKEY|ShiftMask, XK_q, quit, { 0 } }, \ +}; diff -r 000000000000 -r 7024076fa948 config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,131 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ + +/* appearance */ +#define BORDERPX 1 +#define FONT "6x9" +#define NORMBORDERCOLOR "#aaaaaa" +#define NORMBGCOLOR "#222222" +#define NORMFGCOLOR "#eeeeee" +#define SELBORDERCOLOR "#ff0000" +#define SELBGCOLOR "#222222" +#define SELFGCOLOR "#00ff00" +#define TOPBAR True /* False */ + +/* behavior */ +#define SNAP 10 /* pixel */ +#define TAGS \ +const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", NULL }; +/* Query class:instance:title for regex matching info with following command: + * xprop | awk -F '"' '/^WM_CLASS/ { printf("%s:%s:",$4,$2) }; /^WM_NAME/ { printf("%s\n",$2) }' */ +#define RULES \ +static Rule rule[] = { \ + /* class:instance:title regex tags regex isversatile */ \ + { "Gimp", NULL, False }, \ + { "MPlayer", NULL, False }, \ + { "Acroread", NULL, False }, \ +}; + +/* layout(s) */ +#define LAYOUTS \ +static Layout layout[] = { \ + /* symbol function */ \ + { " ", versatile }, \ + { "@", spiral }, \ + { "&", spiral_h }, \ + { "X", monocle }, \ + { "#", grid }, \ + { "U", bstack }, \ + { "!", tile }, \ + /*{ "\\", cascade },*/ \ + /* first entry is default */ \ +}; +#define MASTER 650 /* per thousand */ +#define NMASTER 1 /* clients in master area */ + +/* key definitions */ +#define MODKEY Mod4Mask +#define KEYS \ +static Key key[] = { \ + /* modifier key function argument */ \ + { MODKEY|ShiftMask, XK_Return, spawn, { .cmd = "/home/paulo/bin/myterm.sh" } }, \ + { MODKEY, XK_backslash, spawn, { .cmd = "/home/paulo/bin/myterm.sh -e /home/paulo/bin/viclip.sh" } }, \ + { MODKEY, XK_n, pushdown, { 0 } }, \ + { MODKEY, XK_p, pushup, { 0 } }, \ + { MODKEY, XK_Tab, focusnext, { 0 } }, \ + { MODKEY|ShiftMask, XK_Tab, focusprev, { 0 } }, \ + { MODKEY, XK_Return, zoom, { 0 } }, \ + { MODKEY, XK_g, resizemaster, { .i = 15 } }, \ + { MODKEY, XK_s, resizemaster, { .i = -15 } }, \ + { MODKEY, XK_i, incnmaster, { .i = 1 } }, \ + { MODKEY, XK_d, incnmaster, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_grave, tag, { .i = -1 } }, \ + { MODKEY|ShiftMask, XK_1, tag, { .i = 0 } }, \ + { MODKEY|ShiftMask, XK_2, tag, { .i = 1 } }, \ + { MODKEY|ShiftMask, XK_3, tag, { .i = 2 } }, \ + { MODKEY|ShiftMask, XK_4, tag, { .i = 3 } }, \ + { MODKEY|ShiftMask, XK_5, tag, { .i = 4 } }, \ + { MODKEY|ShiftMask, XK_6, tag, { .i = 5 } }, \ + { MODKEY|ShiftMask, XK_7, tag, { .i = 6 } }, \ + { MODKEY|ShiftMask, XK_8, tag, { .i = 7 } }, \ + { MODKEY|ShiftMask, XK_9, tag, { .i = 8 } }, \ + { MODKEY|ShiftMask, XK_0, tag, { .i = 9 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_1, toggletag, { .i = 0 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_2, toggletag, { .i = 1 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_3, toggletag, { .i = 2 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_4, toggletag, { .i = 3 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_5, toggletag, { .i = 4 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_6, toggletag, { .i = 5 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_7, toggletag, { .i = 6 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_8, toggletag, { .i = 7 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_9, toggletag, { .i = 8 } }, \ + { MODKEY|ControlMask|ShiftMask, XK_0, toggletag, { .i = 9 } }, \ + { MODKEY|ShiftMask, XK_c, killclient, { 0 } }, \ + { MODKEY, XK_space, setlayout, { .i = -1 } }, \ + { MODKEY, XK_F1, setlayout, { .i = 0 } }, \ + { MODKEY, XK_F2, setlayout, { .i = 1 } }, \ + { MODKEY, XK_F3, setlayout, { .i = 2 } }, \ + { MODKEY, XK_F4, setlayout, { .i = 3 } }, \ + { MODKEY, XK_F5, setlayout, { .i = 4 } }, \ + { MODKEY, XK_F6, setlayout, { .i = 5 } }, \ + { MODKEY, XK_F7, setlayout, { .i = 6 } }, \ + { MODKEY|ShiftMask, XK_space, toggleversatile,{ 0 } }, \ + { MODKEY, XK_grave, view, { .i = -1 } }, \ + { MODKEY, XK_1, view, { .i = 0 } }, \ + { MODKEY, XK_2, view, { .i = 1 } }, \ + { MODKEY, XK_3, view, { .i = 2 } }, \ + { MODKEY, XK_4, view, { .i = 3 } }, \ + { MODKEY, XK_5, view, { .i = 4 } }, \ + { MODKEY, XK_6, view, { .i = 5 } }, \ + { MODKEY, XK_7, view, { .i = 6 } }, \ + { MODKEY, XK_8, view, { .i = 7 } }, \ + { MODKEY, XK_9, view, { .i = 8 } }, \ + { MODKEY, XK_0, view, { .i = 9 } }, \ + { MODKEY, XK_BackSpace, last_view, { 0 } }, \ + { MODKEY|ControlMask, XK_1, toggleview, { .i = 0 } }, \ + { MODKEY|ControlMask, XK_2, toggleview, { .i = 1 } }, \ + { MODKEY|ControlMask, XK_3, toggleview, { .i = 2 } }, \ + { MODKEY|ControlMask, XK_4, toggleview, { .i = 3 } }, \ + { MODKEY|ControlMask, XK_5, toggleview, { .i = 4 } }, \ + { MODKEY|ControlMask, XK_6, toggleview, { .i = 5 } }, \ + { MODKEY|ControlMask, XK_7, toggleview, { .i = 6 } }, \ + { MODKEY|ControlMask, XK_8, toggleview, { .i = 7 } }, \ + { MODKEY|ControlMask, XK_9, toggleview, { .i = 8 } }, \ + { MODKEY|ControlMask, XK_0, toggleview, { .i = 9 } }, \ + { MODKEY|ShiftMask, XK_q, quit, { 0 } }, \ + { MODKEY, XK_h, moveresize, "-25x 0y 0w 0h" }, \ + { MODKEY, XK_l, moveresize, "25x 0y 0w 0h" }, \ + { MODKEY, XK_j, moveresize, "0x 25y 0w 0h" }, \ + { MODKEY, XK_k, moveresize, "0x -25y 0w 0h" }, \ + { MODKEY|ControlMask, XK_h, moveresize, "0X 0y 0w 0h" }, \ + { MODKEY|ControlMask, XK_l, moveresize, "9000X 0y 0w 0h" }, \ + { MODKEY|ControlMask, XK_j, moveresize, "0x 9000Y 0w 0h" }, \ + { MODKEY|ControlMask, XK_k, moveresize, "0x 15Y 0w 0h" }, \ + { MODKEY|ShiftMask, XK_h, moveresize, "0x 0y -25w 0h" }, \ + { MODKEY|ShiftMask, XK_l, moveresize, "0x 0y 25w 0h" }, \ + { MODKEY|ShiftMask, XK_j, moveresize, "0x 0y 0w 25h" }, \ + { MODKEY|ShiftMask, XK_k, moveresize, "0x 0y 0w -25h" }, \ +}; + +#define MAX_TASKS 10 /* max number of apps in taskbar */ diff -r 000000000000 -r 7024076fa948 config.mk --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.mk Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,29 @@ +# dwm version +VERSION = 3.6.1 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# includes and libs +INCS = -I. -I/usr/include -I${X11INC} +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 + +# flags +CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\" +LDFLAGS = ${LIBS} +#CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = -g ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} +#CFLAGS += -xtarget=ultra + +# compiler and linker +CC = cc diff -r 000000000000 -r 7024076fa948 draw.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/draw.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,154 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ +#include "dwm.h" +#include + +/* static */ + +static void +drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { + int x; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + gcv.foreground = col[ColFG]; + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + x = (dc.font.ascent + dc.font.descent + 2) / 4; + r.x = dc.x + 1; + r.y = dc.y + 1; + if(filled) { + r.width = r.height = x + 1; + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } + else if(empty) { + r.width = r.height = x; + XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } +} + +static Bool +isoccupied(unsigned int t) { + Client *c; + + for(c = clients; c; c = c->next) + if(c->tags[t]) + return True; + return False; +} + +static unsigned int +textnw(const char *text, unsigned int len) { + XRectangle r; + + if(dc.font.set) { + XmbTextExtents(dc.font.set, text, len, NULL, &r); + return r.width; + } + return XTextWidth(dc.font.xfont, text, len); +} + +/* extern */ + +void +drawstatus(void) { + Client *c; + int i, j, x; + + dc.x = dc.y = 0; + for(i = 0; i < ntags; i++) { + dc.w = textw(tags[i]); + if(seltag[i]) { + drawtext(tags[i], dc.sel); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); + } + else { + drawtext(tags[i], dc.norm); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); + } + dc.x += dc.w; + } + dc.w = blw; + drawtext(lt->symbol, dc.norm); + x = dc.x + dc.w; + dc.w = textw(stext); + dc.x = sw - dc.w; + if(dc.x < x) { + dc.x = x; + dc.w = sw - x; + } + drawtext(stext, dc.norm); + if((dc.w = dc.x - x) > bh) { + dc.x = x; + for(j=0, c = clients; c; c = c->next) { + if(isvisible(c)) + j++; + } + if(j && j < MAX_TASKS) + dc.w /= j; + else { + drawtext(sel ? sel->name : NULL, sel ? dc.sel : dc.norm); + j = 0; + } + for(c = clients; j && c; c = c->next) { + if(isvisible(c)) { + drawtext(c->name, (c == sel) ? dc.sel : dc.norm); + dc.x += dc.w; + j--; + } + } + } + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); + XSync(dpy, False); +} + +void +drawtext(const char *text, unsigned long col[ColLast]) { + int x, y, w, h; + static char buf[256]; + unsigned int len, olen; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, col[ColBG]); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if(!text) + return; + w = 0; + olen = len = strlen(text); + if(len >= sizeof buf) + len = sizeof buf - 1; + memcpy(buf, text, len); + buf[len] = 0; + h = dc.font.ascent + dc.font.descent; + y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; + x = dc.x + (h / 2); + /* shorten text if necessary */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + if(len < olen) { + if(len > 1) + buf[len - 1] = '.'; + if(len > 2) + buf[len - 2] = '.'; + if(len > 3) + buf[len - 3] = '.'; + } + if(w > dc.w) + return; /* too long */ + gcv.foreground = col[ColFG]; + if(dc.font.set) { + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); + } + else { + gcv.font = dc.font.xfont->fid; + XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv); + XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); + } +} + +unsigned int +textw(const char *text) { + return textnw(text, strlen(text)) + dc.font.height; +} diff -r 000000000000 -r 7024076fa948 dwm.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwm.1 Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,149 @@ +.TH DWM 1 dwm-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. It manages windows in tiling and +versatile layouts. Either layout can be applied dynamically, optimizing the +environment for the application in use and the task performed. +.P +In tiling layout windows are managed in a master and stacking area. The master +area contains the windows which currently need most attention, whereas the +stacking area contains all other windows. In versatile layout windows can be +resized and moved freely. Dialog windows are always managed versatile, +regardless of the layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +dwm contains a small status bar which displays all available tags, the layout, +the title of the focused window, and the text read from standard input. The +selected tags are indicated with a different color. The tags of the focused +window are indicated with a filled square in the top left corner. The tags +which are applied to one or more windows are indicated with an empty square in +the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. +.SH OPTIONS +.TP +.B \-v +prints version information to standard output, then exits. +.SH USAGE +.SS Status bar +.TP +.B Standard input +is read and displayed in the status text area. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiling and versatile layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Button4 +click on the layout label increases the number of windows in the master area (tiling layout only). +.TP +.B Button5 +click on the layout label decreases the number of windows in the master area (tiling layout only). +.TP +.B Mod1-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1-Shift-Return +Start +.BR xterm (1). +.TP +.B Mod1-Tab +Focus next window. +.TP +.B Mod1-Shift-Tab +Focus previous window. +.TP +.B Mod1-Return +Zooms/cycles current window to/from master area (tiling layout), toggles maximization of current window (versatile layout). +.TP +.B Mod1-g +Grow master area (tiling layout only). +.TP +.B Mod1-s +Shrink master area (tiling layout only). +.TP +.B Mod1-i +Increase the number of windows in the master area (tiling layout only). +.TP +.B Mod1-d +Decrease the number of windows in the master area (tiling layout only). +.TP +.B Mod1-Shift-[1..n] +Apply +.RB nth +tag to current window. +.TP +.B Mod1-Shift-0 +Apply all tags to current window. +.TP +.B Mod1-Control-Shift-[1..n] +Add/remove +.B nth +tag to/from current window. +.TP +.B Mod1-Shift-c +Close focused window. +.TP +.B Mod1-space +Toggle between tiling and versatile layout (affects all windows). +.TP +.B Mod1-Shift-space +Toggle focused window between versatile and non-versatile state (tiling layout only). +.TP +.B Mod1-[1..n] +View all windows with +.BR nth +tag. +.TP +.B Mod1-0 +View all windows with any tag. +.TP +.B Mod1-Control-[1..n] +Add/remove all windows with +.BR nth +tag to/from the view. +.TP +.B Mod1-Shift-q +Quit dwm. +.SS Mouse commands +.TP +.B Mod1-Button1 +Move current window while dragging (versatile layout only). +.TP +.B Mod1-Button2 +Zooms/cycles current window to/from master area (tiling layout), toggles maximization of current window (versatile layout). +.TP +.B Mod1-Button3 +Resize current window while dragging (versatile layout only). +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH SEE ALSO +.BR dmenu (1) +.SH BUGS +The status bar may display +.BR "EOF" +when dwm has been started by an X session manager like +.BR xdm (1), +because those close standard output before executing dwm. +.P +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. As a workaround +you can use JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or you +can set the following environment variable (to use the older Motif +backend instead): +.BR AWT_TOOLKIT=MToolkit . diff -r 000000000000 -r 7024076fa948 dwm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwm.h Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,160 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * Calls to fetch an X event from the event queue are blocking. Due reading + * status text from standard input, a select()-driven main loop has been + * implemented which selects for reads on the X connection and STDIN_FILENO to + * handle all data smoothly. The event handlers of dwm are organized in an + * array which is accessed whenever a new event has been fetched. This allows + * event dispatching in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a global + * doubly-linked client list, the focus history is remembered through a global + * stack list. Each client contains an array of Bools of the same size as the + * global tags array to indicate the tags of a client. For each client dwm + * creates a small title window, which is resized whenever the (_NET_)WM_NAME + * properties are updated or the client is moved/resized. + * + * Keys and tagging rules are organized as arrays and defined in the config.h + * file. These arrays are kept static in event.o and tag.o respectively, + * because no other part of dwm needs access to them. The current layout is + * represented by the lt pointer. + * + * To understand everything else, start reading main.c:main(). + */ + +#include "config.h" +#include + +/* mask shorthands, used in event.c and client.c */ +#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) + +enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMLast }; /* default atoms */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ + +typedef union { + const char *cmd; + int i; +} Arg; /* argument type */ + +typedef struct { + int ascent; + int descent; + int height; + XFontSet set; + XFontStruct *xfont; +} Fnt; + +typedef struct { + int x, y, w, h; + unsigned long norm[ColLast]; + unsigned long sel[ColLast]; + Drawable drawable; + Fnt font; + GC gc; +} DC; /* draw context */ + +typedef struct Client Client; +struct Client { + char name[256]; + int x, y, w, h; + int rx, ry, rw, rh; /* revert geometry */ + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int minax, minay, maxax, maxay; + long flags; + unsigned int border; + Bool isbanned, isfixed, ismax, isversatile; + Bool *tags; + Client *next; + Client *prev; + Client *snext; + Window win; +}; + +typedef struct { + const char *symbol; + void (*arrange)(void); +} Layout; + +extern const char *tags[]; /* all tags */ +extern char stext[256]; /* status text */ +extern int screen, sx, sy, sw, sh; /* screen geometry */ +extern int wax, way, wah, waw; /* windowarea geometry */ +extern unsigned int bh, blw; /* bar height, bar layout label width */ +extern unsigned int master, nmaster; /* master percent, number of master clients */ +extern unsigned int ntags, numlockmask; /* number of tags, dynamic lock mask */ +extern void (*handler[LASTEvent])(XEvent *); /* event handler */ +extern Atom wmatom[WMLast], netatom[NetLast]; +extern Bool selscreen, *seltag; /* seltag is array of Bool */ +extern Client *clients, *sel, *stack; /* global client list and stack */ +extern Cursor cursor[CurLast]; +extern DC dc; /* global draw context */ +extern Display *dpy; +extern Layout *lt; +extern Window root, barwin; + +/* client.c */ +extern void configure(Client *c); /* send synthetic configure event */ +extern void focus(Client *c); /* focus c, c may be NULL */ +extern void killclient(Arg *arg); /* kill c nicely */ +extern void manage(Window w, XWindowAttributes *wa); /* manage new client */ +extern void resize(Client *c, int x, int y, + int w, int h, Bool sizehints); /* resize with given coordinates c*/ +extern void toggleversatile(Arg *arg); /* toggles focused client between versatile/and non-versatile state */ +extern void updatesizehints(Client *c); /* update the size hint variables of c */ +extern void updatetitle(Client *c); /* update the name of c */ +extern void unmanage(Client *c); /* destroy c */ +extern void zoom(Arg *arg); /* zooms the focused client to master area, arg is ignored */ +extern void pushup(Arg *arg); +extern void pushdown(Arg *arg); +extern void moveresize(Arg *arg); + +/* draw.c */ +extern void drawstatus(void); /* draw the bar */ +extern void drawtext(const char *text, + unsigned long col[ColLast]); /* draw text */ +extern unsigned int textw(const char *text); /* return the width of text in px*/ + +/* event.c */ +extern void grabkeys(void); /* grab all keys defined in config.h */ + +/* layout.c */ +extern void focusnext(Arg *arg); /* focuses next visible client, arg is ignored */ +extern void focusprev(Arg *arg); /* focuses previous visible client, arg is ignored */ +extern void incnmaster(Arg *arg); /* increments nmaster with arg's index value */ +extern void initlayouts(void); /* initialize layout array */ +extern Client *nexttiled(Client *c); /* returns tiled successor of c */ +extern void resizemaster(Arg *arg); /* resizes the master percent with arg's index value */ +extern void restack(void); /* restores z layers of all clients */ +extern void setlayout(Arg *arg); /* sets layout, -1 toggles */ +extern void versatile(void); /* arranges all windows versatile */ + +/* main.c */ +extern void quit(Arg *arg); /* quit dwm nicely */ +extern void sendevent(Window w, Atom a, long value); /* send synthetic event to w */ +extern int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */ + +/* tag.c */ +extern void compileregs(void); /* initialize regexps of rules defined in config.h */ +extern Bool isvisible(Client *c); /* returns True if client is visible */ +extern void settags(Client *c, Client *trans); /* sets tags of c */ +extern void tag(Arg *arg); /* tags c with arg's index */ +extern void toggletag(Arg *arg); /* toggles c tags with arg's index */ +extern void toggleview(Arg *arg); /* toggles the tag with arg's index (in)visible */ +extern void view(Arg *arg); /* views the tag with arg's index */ +extern void last_view(Arg *arg); /* go to last viewed tag */ + +/* util.c */ +extern void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ +extern void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */ +extern void spawn(Arg *arg); /* forks a new subprocess with arg's cmd */ + diff -r 000000000000 -r 7024076fa948 event.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/event.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,380 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ +#include "dwm.h" +#include +#include +#include + +/* static */ + +typedef struct { + unsigned long mod; + KeySym keysym; + void (*func)(Arg *arg); + Arg arg; +} Key; + +KEYS + +#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) +#define MOUSEMASK (BUTTONMASK | PointerMotionMask) + +static Client * +getclient(Window w) { + Client *c; + + for(c = clients; c && c->win != w; c = c->next); + return c; +} + +static void +movemouse(Client *c) { + int x1, y1, ocx, ocy, di, nx, ny; + unsigned int dui; + Window dummy; + XEvent ev; + + ocx = nx = c->x; + ocy = ny = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); + switch (ev.type) { + case ButtonRelease: + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + nx = ocx + (ev.xmotion.x - x1); + ny = ocy + (ev.xmotion.y - y1); + if(abs(wax + nx) < SNAP) + nx = wax; + else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) + nx = wax + waw - c->w - 2 * c->border; + if(abs(way - ny) < SNAP) + ny = way; + else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) + ny = way + wah - c->h - 2 * c->border; + resize(c, nx, ny, c->w, c->h, False); + break; + } + } +} + +static void +resizemouse(Client *c) { + int ocx, ocy; + int nw, nh; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); + switch(ev.type) { + case ButtonRelease: + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, + c->w + c->border - 1, c->h + c->border - 1); + XUngrabPointer(dpy, CurrentTime); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) + nw = 1; + if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) + nh = 1; + resize(c, c->x, c->y, nw, nh, True); + break; + } + } +} + +static void +buttonpress(XEvent *e) { + int x; + Arg a; + Client *c; + XButtonPressedEvent *ev = &e->xbutton; + + if(barwin == ev->window) { + x = 0; + for(a.i = 0; a.i < ntags; a.i++) { + x += textw(tags[a.i]); + if(ev->x < x) { + if(ev->button == Button1) { + if(ev->state & MODKEY) + tag(&a); + else + view(&a); + } + else if(ev->button == Button3) { + if(ev->state & MODKEY) + toggletag(&a); + else + toggleview(&a); + } + return; + } + } + if(ev->x < x + blw) + switch(ev->button) { + case Button1: + a.i = -1; + setlayout(&a); + break; + case Button4: + a.i = 1; + incnmaster(&a); + break; + case Button5: + a.i = -1; + incnmaster(&a); + break; + } + if(ev->x > x + blw) + switch(ev->button) { + case Button1: zoom(NULL); break; + case Button2: toggleversatile(NULL); break; + case Button3: killclient(NULL); break; + case Button4: focusprev(NULL); break; + case Button5: focusnext(NULL); break; + } + } + else if((c = getclient(ev->window))) { + focus(c); + if(CLEANMASK(ev->state) != MODKEY) + return; + if(ev->button == Button1 && (lt->arrange == versatile || c->isversatile)) { + restack(); + movemouse(c); + } + else if(ev->button == Button2) + zoom(NULL); + else if(ev->button == Button3 + && (lt->arrange == versatile || c->isversatile) && !c->isfixed) + { + restack(); + resizemouse(c); + } + } +} + +static void +configurerequest(XEvent *e) { + Client *c; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if((c = getclient(ev->window))) { + c->ismax = False; + if(ev->value_mask & CWBorderWidth) + c->border = ev->border_width; + if(c->isfixed || c->isversatile || (lt->arrange == versatile)) { + if(ev->value_mask & CWX) + c->x = ev->x; + if(ev->value_mask & CWY) + c->y = ev->y; + if(ev->value_mask & CWWidth) + c->w = ev->width; + if(ev->value_mask & CWHeight) + c->h = ev->height; + 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); */ + resize(c, c->x, c->y, c->w, c->h, False); + } + 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); +} + +static void +destroynotify(XEvent *e) { + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +enternotify(XEvent *e) { + Client *c; + XCrossingEvent *ev = &e->xcrossing; + + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) + return; + if((c = getclient(ev->window)) && isvisible(c)) + focus(c); + else if(ev->window == root) { + selscreen = True; + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } +} + +static void +expose(XEvent *e) { + XExposeEvent *ev = &e->xexpose; + + if(ev->count == 0) { + if(barwin == ev->window) + drawstatus(); + } +} + +static void +keypress(XEvent *e) { + static unsigned int len = sizeof key / sizeof key[0]; + unsigned int i; + KeySym keysym; + XKeyEvent *ev = &e->xkey; + + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for(i = 0; i < len; i++) + if(keysym == key[i].keysym + && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) + { + if(key[i].func) + key[i].func(&key[i].arg); + } +} + +static void +leavenotify(XEvent *e) { + XCrossingEvent *ev = &e->xcrossing; + + if((ev->window == root) && !ev->same_screen) { + selscreen = False; + focus(NULL); + } +} + +static void +mappingnotify(XEvent *e) { + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if(ev->request == MappingKeyboard) + grabkeys(); +} + +static void +maprequest(XEvent *e) { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if(!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if(wa.override_redirect) + return; + if(!getclient(ev->window)) + manage(ev->window, &wa); +} + +static void +propertynotify(XEvent *e) { + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if(ev->state == PropertyDelete) + return; /* ignore */ + if((c = getclient(ev->window))) { + switch (ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(dpy, c->win, &trans); + if(!c->isversatile && (c->isversatile = (getclient(trans) != NULL))) + lt->arrange(); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == sel) + drawstatus(); + } + } +} + +static void +unmapnotify(XEvent *e) { + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if((c = getclient(ev->window))) + unmanage(c); +} + +/* extern */ + +void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ConfigureRequest] = configurerequest, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [LeaveNotify] = leavenotify, + [Expose] = expose, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; + +void +grabkeys(void) { + static unsigned int len = sizeof key / sizeof key[0]; + unsigned int i; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for(i = 0; i < len; i++) { + code = XKeysymToKeycode(dpy, key[i].keysym); + XGrabKey(dpy, code, key[i].mod, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | LockMask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | numlockmask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True, + GrabModeAsync, GrabModeAsync); + } +} diff -r 000000000000 -r 7024076fa948 layout.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,471 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ +#include "dwm.h" + +unsigned int master = MASTER; +unsigned int nmaster = NMASTER; +unsigned int blw = 0; +Layout *lt = NULL; + +/* static */ + +static unsigned int nlayouts = 0; + +static void +spiral(void) { + unsigned int i, n, nx, ny, nw, nh; + Client *c; + + nx = wax; + ny = way + wah; + nw = waw; + nh = wah; + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isbanned) + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + if(c->isversatile) + continue; + c->ismax = False; + if((i % 2 && nh / 2 > 2 * c->border) + || (!(i % 2) && nw / 2 > 2 * c->border)) + { + if(i < n - 1) { + if(i % 2) + nh /= 2; + else + nw /= 2; + if((i % 4) == 2) + nx += nw; + else if((i % 4) == 3) + ny += nh; + } + if((i % 4) == 0) + ny -= nh; + else if((i % 4) == 1) + nx += nw; + else if((i % 4) == 2) + ny += nh; + else + nx -= nw; + i++; + } + resize(c, nx, ny, nw - 2 * c->border, nh - 2 * c->border, False); + } + else { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +static void +spiral_h(void) { + unsigned int i, n, nx, ny, nw, nh; + Client *c; + + nx = wax + waw; + ny = way; + nw = waw; + nh = wah; + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isbanned) + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + if(c->isversatile) + continue; + c->ismax = False; + if((i % 2 && nw / 2 > 2 * c->border) + || (!(i % 2) && nh / 2 > 2 * c->border)) + { + if(i < n - 1) { + if(i % 2) + nw /= 2; + else + nh /= 2; + if((i % 4) == 2) + ny += nh; + else if((i % 4) == 3) + nx += nw; + } + if((i % 4) == 0) + nx -= nw; + else if((i % 4) == 1) + ny += nh; + else if((i % 4) == 2) + nx += nw; + else + ny -= nh; + i++; + } + resize(c, nx, ny, nw - 2 * c->border, nh - 2 * c->border, False); + } + else { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +static void +monocle(void) { + Client *c; + + for(c = clients; c; c = c->next) + if(isvisible(c) && c->isbanned) { + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + } + else if(!isvisible(c)) { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + + for(c = nexttiled(clients); c; c = nexttiled(c->next)) + resize(c, wax, way, waw - 2*c->border, wah - 2*c->border, False); + + restack(); +} + +static void +cascade(void) { + unsigned int i, n, px = 20; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next), n++); + + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isbanned) { + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + } + if(!c->isversatile) { + resize(c, wax+(px*i), way+(px*i), waw-(px*(n-1))-2*c->border, wah-(px*(n-1))-2*c->border, True); + i++; + } + } + else if(!isvisible(c)) { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + + restack(); +} + +static void +bstack(void) { + unsigned int i, n, nx, ny, nw, nh, mw, mh, tw, th; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + /* window geoms */ + mh = (n > nmaster) ? (wah * master) / 1000 : wah; + mw = (n > nmaster) ? waw / nmaster : waw / (n > 0 ? n : 1); + th = wah - mh; + tw = (n > nmaster) ? waw / (n - nmaster) : 0; + + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isbanned) + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + if(c->isversatile) + continue; + c->ismax = False; + nx = wax; + ny = way; + if(i < nmaster) { + nx += i * mw; + nw = mw - 2 * BORDERPX; + nh = mh - 2 * BORDERPX; + } + else { /* tile window */ + ny += mh; + nh = th - 2 * BORDERPX; + if(tw > 2 * BORDERPX) { + nx += (i - nmaster) * tw; + nw = tw - 2 * BORDERPX; + } + else /* fallback if th <= 2 * BORDERPX */ + nw = waw - 2 * BORDERPX; + } + resize(c, nx, ny, nw, nh, False); + i++; + } + else { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +static void +grid(void) { + unsigned int i, n, nx, ny, nw, nh, aw, ah, tw, th, cols, rows; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + + for(rows = 0; rows <= n/2; rows++) + if(rows*rows >= n) + break; + cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; + + th = (sh - dc.h) / (rows ? rows : 1); + tw = sw / (cols ? cols : 1); + nw = tw - 2 * BORDERPX; + nh = th - 2 * BORDERPX; + + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isbanned) + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + if(c->isversatile) + continue; + c->ismax = False; + nx = (i / rows) * tw; + ny = (i % rows) * th + (TOPBAR ? dc.h : 0); + /* adjust height and width of last row's and last column's windows */ + ah = ((i + 1) % rows == 0) ? sh - th * rows - dc.h : 0; + aw = (i >= rows * (cols - 1)) ? sw - tw * cols : 0; + resize(c, nx, ny, nw + aw, nh + ah, False); + i++; + } + else { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +static void +tile(void) { + unsigned int i, n, nx, ny, nw, nh, mw, mh, tw, th; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + /* window geoms */ + mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1); + mw = (n > nmaster) ? (waw * master) / 1000 : waw; + th = (n > nmaster) ? wah / (n - nmaster) : 0; + tw = waw - mw; + + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isbanned) + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + if(c->isversatile) + continue; + c->ismax = False; + nx = wax; + ny = way; + if(i < nmaster) { + ny += i * mh; + nw = mw - 2 * BORDERPX; + nh = mh - 2 * BORDERPX; + } + else { /* tile window */ + nx += mw; + nw = tw - 2 * BORDERPX; + if(th > 2 * BORDERPX) { + ny += (i - nmaster) * th; + nh = th - 2 * BORDERPX; + } + else /* fallback if th <= 2 * BORDERPX */ + nh = wah - 2 * BORDERPX; + } + resize(c, nx, ny, nw, nh, False); + i++; + } + else { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +LAYOUTS + +/* extern */ + +void +focusnext(Arg *arg) { + Client *c; + + if(!sel) + return; + for(c = sel->next; c && !isvisible(c); c = c->next); + if(!c) + for(c = clients; c && !isvisible(c); c = c->next); + if(c) { + focus(c); + restack(); + } +} + +void +focusprev(Arg *arg) { + Client *c; + + if(!sel) + return; + for(c = sel->prev; c && !isvisible(c); c = c->prev); + if(!c) { + for(c = clients; c && c->next; c = c->next); + for(; c && !isvisible(c); c = c->prev); + } + if(c) { + focus(c); + restack(); + } +} + +void +incnmaster(Arg *arg) { + //if((lt->arrange != tile) || (nmaster + arg->i < 1) + //|| (wah / (nmaster + arg->i) <= 2 * BORDERPX)) + // return; + if(nmaster + arg->i < 1 && nmaster + arg->i <= MAX_TASKS) + return; + nmaster += arg->i; + if(sel) + lt->arrange(); + else + drawstatus(); +} + +void +initlayouts(void) { + unsigned int i, w; + + lt = &layout[0]; + nlayouts = sizeof layout / sizeof layout[0]; + for(blw = i = 0; i < nlayouts; i++) { + w = textw(layout[i].symbol); + if(w > blw) + blw = w; + } +} + +Client * +nexttiled(Client *c) { + for(; c && (c->isversatile || !isvisible(c)); c = c->next); + return c; +} + +void +resizemaster(Arg *arg) { + //if(lt->arrange != tile) + // return; + if(arg->i == 0) + master = MASTER; + else { + //if(waw * (master + arg->i) / 1000 >= waw - 2 * BORDERPX + //|| waw * (master + arg->i) / 1000 <= 2 * BORDERPX) + // return; + master += arg->i; + } + lt->arrange(); +} + +void +restack(void) { + Client *c; + XEvent ev; + + drawstatus(); + if(!sel) + return; + if(sel->isversatile || lt->arrange == versatile) + XRaiseWindow(dpy, sel->win); + if(lt->arrange != versatile) { + if(!sel->isversatile) + XLowerWindow(dpy, sel->win); + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + if(c == sel) + continue; + XLowerWindow(dpy, c->win); + } + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +setlayout(Arg *arg) { + unsigned int i; + + if(arg->i == -1) { + for(i = 0; i < nlayouts && lt != &layout[i]; i++); + if(i == nlayouts - 1) + lt = &layout[0]; + else + lt = &layout[++i]; + } + else { + if(arg->i < 0 || arg->i >= nlayouts) + return; + lt = &layout[arg->i]; + } + if(sel) + lt->arrange(); + else + drawstatus(); +} + +void +versatile(void) { + Client *c; + + for(c = clients; c; c = c->next) { + if(isvisible(c)) { + if(c->isbanned) + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; + resize(c, c->x, c->y, c->w, c->h, True); + } + else { + c->isbanned = True; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} diff -r 000000000000 -r 7024076fa948 main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,338 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ + +#include "dwm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* extern */ + +char stext[256]; +int screen, sx, sy, sw, sh, wax, way, waw, wah; +unsigned int bh, ntags, numlockmask; +Atom wmatom[WMLast], netatom[NetLast]; +Bool *seltag; +Bool selscreen = True; +Client *clients = NULL; +Client *sel = NULL; +Client *stack = NULL; +Cursor cursor[CurLast]; +Display *dpy; +DC dc = {0}; +Window root, barwin; + +/* static */ + +static int (*xerrorxlib)(Display *, XErrorEvent *); +static Bool otherwm, readin; +static Bool running = True; + +static void +cleanup(void) { + close(STDIN_FILENO); + while(stack) { + if(stack->isbanned) + XMoveWindow(dpy, stack->win, stack->x, stack->y); + unmanage(stack); + } + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + else + XFreeFont(dpy, dc.font.xfont); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XFreePixmap(dpy, dc.drawable); + XFreeGC(dpy, dc.gc); + XDestroyWindow(dpy, barwin); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); + XFreeCursor(dpy, cursor[CurMove]); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XSync(dpy, False); + free(seltag); +} + +static unsigned long +initcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; +} + +static void +initfont(const char *fontstr) { + char *def, **missing; + int i, n; + + missing = NULL; + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "missing fontset: %s\n", missing[n]); + XFreeStringList(missing); + } + if(dc.font.set) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } + else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) + eprint("error, cannot load font: '%s'\n", fontstr); + dc.font.ascent = dc.font.xfont->ascent; + dc.font.descent = dc.font.xfont->descent; + } + dc.font.height = dc.font.ascent + dc.font.descent; +} + +static void +scan(void) { + unsigned int i, num; + Window *wins, d1, d2; + XWindowAttributes wa; + + wins = NULL; + 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) + manage(wins[i], &wa); + } + } + if(wins) + XFree(wins); +} + +static void +setup(void) { + int i, j; + unsigned int mask; + Window w; + XModifierKeymap *modmap; + XSetWindowAttributes wa; + + /* init atoms */ + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + /* init cursors */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + /* init modifier map */ + 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); + /* select for events */ + wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask + | EnterWindowMask | LeaveWindowMask; + wa.cursor = cursor[CurNormal]; + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); + grabkeys(); + compileregs(); + for(ntags = 0; tags[ntags]; ntags++); + seltag = emallocz(sizeof(Bool) * ntags); + seltag[0] = True; + /* style */ + dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); + dc.norm[ColBG] = initcolor(NORMBGCOLOR); + dc.norm[ColFG] = initcolor(NORMFGCOLOR); + dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); + dc.sel[ColBG] = initcolor(SELBGCOLOR); + dc.sel[ColFG] = initcolor(SELFGCOLOR); + initfont(FONT); + /* geometry */ + sx = sy = 0; + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + initlayouts(); + /* bar */ + dc.h = bh = dc.font.height + 2; + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy + (TOPBAR ? 0 : sh - bh), sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + /* windowarea */ + wax = sx; + way = sy + (TOPBAR ? bh : 0); + wah = sh - bh; + waw = sw; + /* pixmap for everything */ + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + /* multihead support */ + selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); +} + +/* + * Startup Error handler to check if another window manager + * is already running. + */ +static int +xerrorstart(Display *dsply, XErrorEvent *ee) { + otherwm = True; + return -1; +} + +/* extern */ + +void +sendevent(Window w, Atom a, long value) { + XEvent e; + + e.type = ClientMessage; + e.xclient.window = w; + e.xclient.message_type = a; + e.xclient.format = 32; + e.xclient.data.l[0] = value; + e.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, w, False, NoEventMask, &e); + XSync(dpy, False); +} + +void +quit(Arg *arg) { + readin = running = False; +} + +/* 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_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 +main(int argc, char *argv[]) { + char *p; + int r, xfd; + fd_set rd; + XEvent ev; + + if(argc == 2 && !strncmp("-v", argv[1], 3)) + eprint("dwm-"VERSION", (C)opyright MMVI-MMVII Anselm R. Garbe\n"); + else if(argc != 1) + eprint("usage: dwm [-v]\n"); + setlocale(LC_CTYPE, ""); + if(!(dpy = XOpenDisplay(0))) + eprint("dwm: cannot open display\n"); + xfd = ConnectionNumber(dpy); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + otherwm = False; + XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XSync(dpy, False); + if(otherwm) + eprint("dwm: another window manager is already running\n"); + + XSync(dpy, False); + XSetErrorHandler(NULL); + xerrorxlib = XSetErrorHandler(xerror); + XSync(dpy, False); + setup(); + drawstatus(); + scan(); + + /* main event loop, also reads status text from stdin */ + XSync(dpy, False); + readin = True; + while(running) { + FD_ZERO(&rd); + if(readin) + FD_SET(STDIN_FILENO, &rd); + FD_SET(xfd, &rd); + if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { + if(errno == EINTR) + continue; + eprint("select failed\n"); + } + if(FD_ISSET(STDIN_FILENO, &rd)) { + switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { + case -1: + strncpy(stext, strerror(errno), sizeof stext - 1); + stext[sizeof stext - 1] = '\0'; + readin = False; + break; + case 0: + strncpy(stext, "EOF", 4); + readin = False; + break; + default: + for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); + for(; p >= stext && *p != '\n'; --p); + if(p > stext) + strncpy(stext, p + 1, sizeof stext); + } + drawstatus(); + } + if(FD_ISSET(xfd, &rd)) + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + if(handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } + } + cleanup(); + XCloseDisplay(dpy); + return 0; +} diff -r 000000000000 -r 7024076fa948 tag.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tag.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,166 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ +#include "dwm.h" +#include +#include +#include +#include + +/* static */ + +typedef struct { + const char *prop; + const char *tags; + Bool isversatile; +} Rule; + +typedef struct { + regex_t *propregex; + regex_t *tagregex; +} Regs; + +TAGS +RULES + +static Regs *regs = NULL; +static unsigned int nrules = 0; +static unsigned int lastview = 0; + +/* extern */ + +void +compileregs(void) { + unsigned int i; + regex_t *reg; + + if(regs) + return; + nrules = sizeof rule / sizeof rule[0]; + regs = emallocz(nrules * sizeof(Regs)); + for(i = 0; i < nrules; i++) { + if(rule[i].prop) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rule[i].prop, REG_EXTENDED)) + free(reg); + else + regs[i].propregex = reg; + } + if(rule[i].tags) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rule[i].tags, REG_EXTENDED)) + free(reg); + else + regs[i].tagregex = reg; + } + } +} + +Bool +isvisible(Client *c) { + unsigned int i; + + for(i = 0; i < ntags; i++) + if(c->tags[i] && seltag[i]) + return True; + return False; +} + +void +settags(Client *c, Client *trans) { + char prop[512]; + unsigned int i, j; + regmatch_t tmp; + Bool matched = trans != NULL; + XClassHint ch = { 0 }; + + if(matched) + for(i = 0; i < ntags; i++) + c->tags[i] = trans->tags[i]; + else { + XGetClassHint(dpy, c->win, &ch); + snprintf(prop, sizeof prop, "%s:%s:%s", + ch.res_class ? ch.res_class : "", + ch.res_name ? ch.res_name : "", c->name); + for(i = 0; i < nrules; i++) + if(regs[i].propregex && !regexec(regs[i].propregex, prop, 1, &tmp, 0)) { + c->isversatile = rule[i].isversatile; + for(j = 0; regs[i].tagregex && j < ntags; j++) { + if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { + matched = True; + c->tags[j] = True; + } + } + } + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + } + if(!matched) + for(i = 0; i < ntags; i++) + c->tags[i] = seltag[i]; +} + +void +tag(Arg *arg) { + unsigned int i; + + if(!sel) + return; + for(i = 0; i < ntags; i++) + sel->tags[i] = (arg->i == -1) ? True : False; + if(arg->i >= 0 && arg->i < ntags) + sel->tags[arg->i] = True; + lt->arrange(); +} + +void +toggletag(Arg *arg) { + unsigned int i; + + if(!sel) + return; + sel->tags[arg->i] = !sel->tags[arg->i]; + for(i = 0; i < ntags && !sel->tags[i]; i++); + if(i == ntags) + sel->tags[arg->i] = True; + lt->arrange(); +} + +void +toggleview(Arg *arg) { + unsigned int i; + + seltag[arg->i] = !seltag[arg->i]; + for(i = 0; i < ntags && !seltag[i]; i++); + if(i == ntags) + seltag[arg->i] = True; /* cannot toggle last view */ + lt->arrange(); +} + +void +view(Arg *arg) { + unsigned int i; + Arg a; + + a.i = 0; + setlayout(&a); /* back to default layout */ + + for(i = 0; i < ntags; i++) { + if (seltag[i] == True) + lastview = i; + seltag[i] = (arg->i == -1) ? True : False; + } + if(arg->i >= 0 && arg->i < ntags) + seltag[arg->i] = True; + lt->arrange(); +} + +void +last_view(Arg *arg) { + Arg a; + + a.i = lastview; + view(&a); +} diff -r 000000000000 -r 7024076fa948 util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.c Sun Mar 22 23:26:35 2009 -0700 @@ -0,0 +1,54 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * See LICENSE file for license details. + */ +#include "dwm.h" +#include +#include +#include +#include +#include + +/* extern */ + +void * +emallocz(unsigned int size) { + void *res = calloc(1, size); + + if(!res) + eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + +void +eprint(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +void +spawn(Arg *arg) { + static char *shell = NULL; + + if(!shell && !(shell = getenv("SHELL"))) + shell = "/bin/sh"; + if(!arg->cmd) + return; + /* The double-fork construct avoids zombie processes and keeps the code + * clean from stupid signal handlers. */ + if(fork() == 0) { + if(fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + setsid(); + execl(shell, shell, "-c", arg->cmd, (char *)NULL); + fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg->cmd); + perror(" failed"); + } + exit(0); + } + wait(0); +}