diff options
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | bar.c | 263 | ||||
| -rw-r--r-- | client.c | 386 | ||||
| -rw-r--r-- | config.h | 26 | ||||
| -rw-r--r-- | config.mk | 3 | ||||
| -rw-r--r-- | dwm.c | 1847 | ||||
| -rw-r--r-- | dwm.h | 147 | ||||
| -rw-r--r-- | event.c | 375 | ||||
| -rw-r--r-- | main.c | 296 | ||||
| -rw-r--r-- | screen.c | 340 | ||||
| -rw-r--r-- | tile.c | 85 | ||||
| -rw-r--r-- | tile.h | 6 | ||||
| -rw-r--r-- | util.c | 52 | 
13 files changed, 1861 insertions, 1971 deletions
| @@ -3,7 +3,7 @@  include config.mk -SRC += bar.c client.c event.c main.c screen.c util.c +SRC = dwm.c  OBJ = ${SRC:.c=.o}  all: options dwm @@ -18,7 +18,7 @@ options:  	@echo CC $<  	@${CC} -c ${CFLAGS} $< -${OBJ}: dwm.h config.h config.mk +${OBJ}: config.h config.mk  dwm: ${OBJ}  	@echo CC -o $@ @@ -32,7 +32,7 @@ dist: clean  	@echo creating dist tarball  	@mkdir -p dwm-${VERSION}  	@cp -R LICENSE Makefile README config.h config.mk \ -		dwm.1 dwm.h tile.h ${SRC} dwm-${VERSION} +		dwm.1 ${SRC} dwm-${VERSION}  	@tar -cf dwm-${VERSION}.tar dwm-${VERSION}  	@gzip dwm-${VERSION}.tar  	@rm -rf dwm-${VERSION} @@ -1,263 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <string.h> -#include <stdio.h> - -/* 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 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, "dwm: 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)) -		|| !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) -			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 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); -} - -static void -drawtext(const char *text, unsigned long col[ColLast]) { -	int x, y, w, h; -	static char buf[256]; -	unsigned int len, olen; -	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 */ -	XSetForeground(dpy, dc.gc, col[ColFG]); -	if(dc.font.set) -		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); -	else -		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); -} - -/* extern */ - -unsigned int bh; -unsigned int bpos = BARPOS; -DC dc = {0}; -Window barwin; - -void -drawbar(void) { -	int i, x; - -	dc.x = dc.y = 0; -	for(i = 0; i < ntags; i++) { -		dc.w = textw(tags[i]); -		if(seltags[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(getsymbol(), 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; -		if(sel) { -			drawtext(sel->name, dc.sel); -			drawsquare(sel->ismax, sel->isfloating, dc.sel); -		} -		else -			drawtext(NULL, dc.norm); -	} -	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); -	XSync(dpy, False); -} - -void -initstyle(void) { -	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); -	dc.h = bh = dc.font.height + 2; -} - -void -initbar(void) { -	XSetWindowAttributes wa; - -	wa.override_redirect = 1; -	wa.background_pixmap = ParentRelative; -	wa.event_mask = ButtonPressMask | ExposureMask; -	barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, -			DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), -			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); -	XDefineCursor(dpy, barwin, cursor[CurNormal]); -	updatebarpos(); -	XMapRaised(dpy, barwin); -	strcpy(stext, "dwm-"VERSION); -	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); -	if(!dc.font.set) -		XSetFont(dpy, dc.gc, dc.font.xfont->fid); -} - -unsigned int -textw(const char *text) { -	return textnw(text, strlen(text)) + dc.font.height; -} - -void -togglebar(const char *arg) { -	if(bpos == BarOff) -		bpos = (BARPOS == BarOff) ? BarTop : BARPOS; -	else -		bpos = BarOff; -	updatebarpos(); -	arrange(); -} - -void -updatebarpos(void) { -	XEvent ev; - -	wax = sx; -	way = sy; -	wah = sh; -	waw = sw; -	switch(bpos) { -	default: -		wah -= bh; -		way += bh; -		XMoveWindow(dpy, barwin, sx, sy); -		break; -	case BarBot: -		wah -= bh; -		XMoveWindow(dpy, barwin, sx, sy + wah); -		break; -	case BarOff: -		XMoveWindow(dpy, barwin, sx, sy - bh); -		break; -	} -	XSync(dpy, False); -	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - - diff --git a/client.c b/client.c deleted file mode 100644 index c716625..0000000 --- a/client.c +++ /dev/null @@ -1,386 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <stdlib.h> -#include <X11/Xutil.h> - -/* 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 int -xerrordummy(Display *dsply, XErrorEvent *ee) { -	return 0; -} - -/* extern */ - -void -attach(Client *c) { -	if(clients) -		clients->prev = c; -	c->next = clients; -	clients = c; -} - -void -ban(Client *c) { -	if(c->isbanned) -		return; -	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); -	c->isbanned = True; -} - -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 && selscreen) || (c && !isvisible(c))) -		for(c = stack; c && !isvisible(c); c = c->snext); -	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; -	drawbar(); -	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(const char *arg) { -	XEvent ev; - -	if(!sel) -		return; -	if(isprotodel(sel)) { -		ev.type = ClientMessage; -		ev.xclient.window = sel->win; -		ev.xclient.message_type = wmatom[WMProtocols]; -		ev.xclient.format = 32; -		ev.xclient.data.l[0] = wmatom[WMDelete]; -		ev.xclient.data.l[1] = CurrentTime; -		XSendEvent(dpy, sel->win, False, NoEventMask, &ev); -	} -	else -		XKillClient(dpy, sel->win); -} - -void -manage(Window w, XWindowAttributes *wa) { -	unsigned int i; -	Client *c, *t = NULL; -	Window trans; -	Status rettrans; -	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; -	c->oldborder = wa->border_width; -	if(c->w == sw && c->h == sh) { -		c->x = sx; -		c->y = sy; -		c->border = wa->border_width; -	} -	else { -		if(c->x + c->w + 2 * c->border > wax + waw) -			c->x = wax + waw - c->w - 2 * c->border; -		if(c->y + c->h + 2 * c->border > way + wah) -			c->y = way + wah - c->h - 2 * c->border; -		if(c->x < wax) -			c->x = wax; -		if(c->y < way) -			c->y = way; -		c->border = BORDERPX; -	} -	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 */ -	updatesizehints(c); -	XSelectInput(dpy, w, -		StructureNotifyMask | PropertyChangeMask | EnterWindowMask); -	grabbuttons(c, False); -	updatetitle(c); -	if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) -		for(t = clients; t && t->win != trans; t = t->next); -	if(t) -		for(i = 0; i < ntags; i++) -			c->tags[i] = t->tags[i]; -	applyrules(c); -	if(!c->isfloating) -		c->isfloating = (rettrans == Success) || c->isfixed; -	attach(c); -	attachstack(c); -	XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ -	ban(c); -	XMapWindow(dpy, c->win); -	setclientstate(c, NormalState); -	arrange(); -} - -void -resize(Client *c, int x, int y, int w, int h, Bool sizehints) { -	double dx, dy, max, min, ratio; -	XWindowChanges wc;  - -	if(sizehints) { -		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { -			dx = (double)(w - c->basew); -			dy = (double)(h - c->baseh); -			min = (double)(c->minax) / (double)(c->minay); -			max = (double)(c->maxax) / (double)(c->maxay); -			ratio = dx / dy; -			if(max > 0 && min > 0 && ratio > 0) { -				if(ratio < min) { -					dy = (dx * min + dy) / (min * min + 1); -					dx = dy * min; -					w = (int)dx + c->basew; -					h = (int)dy + c->baseh; -				} -				else if(ratio > max) { -					dy = (dx * min + dy) / (max * max + 1); -					dx = dy * min; -					w = (int)dx + c->basew; -					h = (int)dy + c->baseh; -				} -			} -		} -		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; -		if(c->incw) -			w -= (w - c->basew) % c->incw; -		if(c->inch) -			h -= (h - c->baseh) % c->inch; -	} -	if(w <= 0 || h <= 0) -		return; -	/* 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 -unban(Client *c) { -	if(!c->isbanned) -		return; -	XMoveWindow(dpy, c->win, c->x, c->y); -	c->isbanned = False; -} - -void -unmanage(Client *c) { -	XWindowChanges wc; - -	wc.border_width = c->oldborder; -	/* The server grab construct avoids race conditions. */ -	XGrabServer(dpy); -	XSetErrorHandler(xerrordummy); -	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ -	detach(c); -	detachstack(c); -	if(sel == c) -		focus(NULL); -	XUngrabButton(dpy, AnyButton, AnyModifier, c->win); -	setclientstate(c, WithdrawnState); -	free(c->tags); -	free(c); -	XSync(dpy, False); -	XSetErrorHandler(xerror); -	XUngrabServer(dpy); -	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 if(c->flags & PMinSize) { -		c->basew = size.min_width; -		c->baseh = size.min_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 if(c->flags & PBaseSize) { -		c->minw = size.base_width; -		c->minh = size.base_height; -	} -	else -		c->minw = c->minh = 0; -	if(c->flags & PAspect) { -		c->minax = size.min_aspect.x; -		c->maxax = size.max_aspect.x; -		c->minay = size.min_aspect.y; -		c->maxay = size.max_aspect.y; -	} -	else -		c->minax = c->maxax = c->minay = 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) { -	if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) -		gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); -} @@ -12,24 +12,20 @@  #define SELFGCOLOR		"#fff"  /* tagging */ -#define TAGS \ -const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; -#define RULES \ -static Rule rules[] = { \ -	/* class:instance:title regex	tags regex	isfloating */ \ -	{ "Firefox",			"3",		False }, \ -	{ "Gimp",			NULL,		True }, \ -	{ "MPlayer",			NULL,		True }, \ -	{ "Acroread",			NULL,		True }, \ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; +static Rule rules[] = { +	/* class:instance:title regex	tags regex	isfloating */ +	{ "Firefox",			"3",		False }, +	{ "Gimp",			NULL,		True }, +	{ "MPlayer",			NULL,		True }, +	{ "Acroread",			NULL,		True },  };  /* layout(s) */ -#include "tile.h" -#define LAYOUTS \ -static Layout layouts[] = { \ -	/* symbol		function */ \ -	{ "[]=",		tile }, /* first entry is default */ \ -	{ "><>",		floating }, \ +static Layout layouts[] = { +	/* symbol		function */ +	{ "[]=",		tile }, /* first entry is default */ +	{ "><>",		floating },  };  #define RESIZEHINTS		True	/* False - respect size hints in tiled resizals */  #define MWFACT			0.6	/* master width factor [0.1 .. 0.9] */ @@ -3,9 +3,6 @@ VERSION = 4.5  # Customize below to fit your system -# additional layouts beside floating -SRC = tile.c -  # paths  PREFIX = /usr/local  MANPREFIX = ${PREFIX}/share/man @@ -0,0 +1,1847 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <locale.h> +#include <regex.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/select.h> +#include <sys/wait.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Xproto.h> +#include <X11/Xutil.h> + +/* macros */ +#define BUTTONMASK		(ButtonPressMask | ButtonReleaseMask) +#define CLEANMASK(mask)		(mask & ~(numlockmask | LockMask)) +#define MOUSEMASK		(BUTTONMASK | PointerMotionMask) + +/* enums */ +enum { BarTop, BarBot, BarOff };			/* bar position */ +enum { CurNormal, CurResize, CurMove, CurLast };	/* cursor */ +enum { ColBorder, ColFG, ColBG, ColLast };		/* color */ +enum { NetSupported, NetWMName, NetLast };		/* EWMH atoms */ +enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */ + +/* typedefs */ +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, maxax, minay, maxay; +	long flags;  +	unsigned int border, oldborder; +	Bool isbanned, isfixed, ismax, isfloating; +	Bool *tags; +	Client *next; +	Client *prev; +	Client *snext; +	Window win; +}; + +typedef struct { +	int x, y, w, h; +	unsigned long norm[ColLast]; +	unsigned long sel[ColLast]; +	Drawable drawable; +	GC gc; +	struct { +		int ascent; +		int descent; +		int height; +		XFontSet set; +		XFontStruct *xfont; +	} font; +} DC; /* draw context */ + +typedef struct { +	unsigned long mod; +	KeySym keysym; +	void (*func)(const char *arg); +	const char *arg; +} Key; + +typedef struct { +	const char *symbol; +	void (*arrange)(void); +} Layout; + +typedef struct { +	const char *prop; +	const char *tags; +	Bool isfloating; +} Rule; + +typedef struct { +	regex_t *propregex; +	regex_t *tagregex; +} Regs; + +/* functions */ +static void eprint(const char *errstr, ...); +static void *emallocz(unsigned int size); +static void spawn(const char *arg); +static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]); +static unsigned long initcolor(const char *colstr); +static void initfont(const char *fontstr); +static Bool isoccupied(unsigned int t); +static unsigned int textnw(const char *text, unsigned int len); +static void drawtext(const char *text, unsigned long col[ColLast]); +static void drawbar(void); +static void initstyle(void); +static void initbar(void); +static unsigned int textw(const char *text); +static void togglebar(const char *arg); +static void updatebarpos(void); +static void attachstack(Client *c); +static void detachstack(Client *c); +static void grabbuttons(Client *c, Bool focused); +static Bool isprotodel(Client *c); +static void setclientstate(Client *c, long state); +static int xerrordummy(Display *dsply, XErrorEvent *ee); +static void ban(Client *c); +static void configure(Client *c); +static void killclient(const char *arg); +static void manage(Window w, XWindowAttributes *wa); +static void resize(Client *c, int x, int y, int w, int h, Bool sizehints); +static void unban(Client *c); +static void unmanage(Client *c); +static void updatesizehints(Client *c); +static void updatetitle(Client *c); +static Client *getclient(Window w); +static void movemouse(Client *c); +static void resizemouse(Client *c); +static void buttonpress(XEvent *e); +static void configurerequest(XEvent *e); +static void configurenotify(XEvent *e); +static void destroynotify(XEvent *e); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void keypress(XEvent *e); +static void leavenotify(XEvent *e); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void propertynotify(XEvent *e); +static void unmapnotify(XEvent *e); +static void grabkeys(void); +static unsigned int idxoftag(const char *tag); +static void floating(void); /* default floating layout */ +static void applyrules(Client *c); +static void compileregs(void); +static void focusnext(const char *arg); +static void focusprev(const char *arg); +static void initlayouts(void); +static Bool isfloating(void); +static Bool isvisible(Client *c); +static void restack(void); +static void setlayout(const char *arg); +static void tag(const char *arg); +static void togglefloating(const char *arg); +static void togglemax(const char *arg); +static void toggletag(const char *arg); +static void toggleview(const char *arg); +static void view(const char *arg); +static void cleanup(void); +static long getstate(Window w); +static void scan(void); +static void setup(void); +static int xerrorstart(Display *dsply, XErrorEvent *ee); +static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void quit(const char *arg); +static int xerror(Display *dpy, XErrorEvent *ee); +static void arrange(void); +static void attach(Client *c); +static void detach(Client *c); +static void focus(Client *c); +static Bool isarrange(void (*func)()); +static Client *nexttiled(Client *c); +static void setmwfact(const char *arg); +static void tile(void); +static void zoom(const char *arg); + +#include "config.h" + +/* variables */ +static char stext[256]; +static double mwfact = MWFACT; +static int screen, sx, sy, sw, sh, wax, way, waw, wah; +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int bh; +static unsigned int blw = 0; +static unsigned int bpos = BARPOS; +static unsigned int ltidx = 0; /* default */ +static unsigned int nlayouts = 0; +static unsigned int nrules = 0; +static unsigned int ntags; +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { +	[ButtonPress] = buttonpress, +	[ConfigureRequest] = configurerequest, +	[ConfigureNotify] = configurenotify, +	[DestroyNotify] = destroynotify, +	[EnterNotify] = enternotify, +	[LeaveNotify] = leavenotify, +	[Expose] = expose, +	[KeyPress] = keypress, +	[MappingNotify] = mappingnotify, +	[MapRequest] = maprequest, +	[PropertyNotify] = propertynotify, +	[UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static Bool otherwm, readin; +static Bool running = True; +static Bool *seltags; +static Bool selscreen = True; +static Client *clients = NULL; +static Client *sel = NULL; +static Client *stack = NULL; +static Cursor cursor[CurLast]; +static Display *dpy; +static DC dc = {0}; +static Window barwin, root; +static Regs *regs = NULL; + +static void +eprint(const char *errstr, ...) { +	va_list ap; + +	va_start(ap, errstr); +	vfprintf(stderr, errstr, ap); +	va_end(ap); +	exit(EXIT_FAILURE); +} + +static void * +emallocz(unsigned int size) { +	void *res = calloc(1, size); + +	if(!res) +		eprint("fatal: could not malloc() %u bytes\n", size); +	return res; +} + +static void +spawn(const char *arg) { +	static char *shell = NULL; + +	if(!shell && !(shell = getenv("SHELL"))) +		shell = "/bin/sh"; +	if(!arg) +		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, (char *)NULL); +			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); +			perror(" failed"); +		} +		exit(0); +	} +	wait(0); +} + +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 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, "dwm: 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)) +		|| !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) +			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 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); +} + +static void +drawtext(const char *text, unsigned long col[ColLast]) { +	int x, y, w, h; +	static char buf[256]; +	unsigned int len, olen; +	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 */ +	XSetForeground(dpy, dc.gc, col[ColFG]); +	if(dc.font.set) +		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); +	else +		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); +} + +static void +drawbar(void) { +	int i, x; + +	dc.x = dc.y = 0; +	for(i = 0; i < ntags; i++) { +		dc.w = textw(tags[i]); +		if(seltags[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(layouts[ltidx].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; +		if(sel) { +			drawtext(sel->name, dc.sel); +			drawsquare(sel->ismax, sel->isfloating, dc.sel); +		} +		else +			drawtext(NULL, dc.norm); +	} +	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); +	XSync(dpy, False); +} + +static void +initstyle(void) { +	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); +	dc.h = bh = dc.font.height + 2; +} + +static void +initbar(void) { +	XSetWindowAttributes wa; + +	wa.override_redirect = 1; +	wa.background_pixmap = ParentRelative; +	wa.event_mask = ButtonPressMask | ExposureMask; +	barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, +			DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), +			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); +	XDefineCursor(dpy, barwin, cursor[CurNormal]); +	updatebarpos(); +	XMapRaised(dpy, barwin); +	strcpy(stext, "dwm-"VERSION); +	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); +	if(!dc.font.set) +		XSetFont(dpy, dc.gc, dc.font.xfont->fid); +} + +static unsigned int +textw(const char *text) { +	return textnw(text, strlen(text)) + dc.font.height; +} + +static void +togglebar(const char *arg) { +	if(bpos == BarOff) +		bpos = (BARPOS == BarOff) ? BarTop : BARPOS; +	else +		bpos = BarOff; +	updatebarpos(); +	arrange(); +} + +static void +updatebarpos(void) { +	XEvent ev; + +	wax = sx; +	way = sy; +	wah = sh; +	waw = sw; +	switch(bpos) { +	default: +		wah -= bh; +		way += bh; +		XMoveWindow(dpy, barwin, sx, sy); +		break; +	case BarBot: +		wah -= bh; +		XMoveWindow(dpy, barwin, sx, sy + wah); +		break; +	case BarOff: +		XMoveWindow(dpy, barwin, sx, sy - bh); +		break; +	} +	XSync(dpy, False); +	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +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 int +xerrordummy(Display *dsply, XErrorEvent *ee) { +	return 0; +} + +static void +ban(Client *c) { +	if(c->isbanned) +		return; +	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); +	c->isbanned = True; +} + +static 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); +} + +static void +killclient(const char *arg) { +	XEvent ev; + +	if(!sel) +		return; +	if(isprotodel(sel)) { +		ev.type = ClientMessage; +		ev.xclient.window = sel->win; +		ev.xclient.message_type = wmatom[WMProtocols]; +		ev.xclient.format = 32; +		ev.xclient.data.l[0] = wmatom[WMDelete]; +		ev.xclient.data.l[1] = CurrentTime; +		XSendEvent(dpy, sel->win, False, NoEventMask, &ev); +	} +	else +		XKillClient(dpy, sel->win); +} + +static void +manage(Window w, XWindowAttributes *wa) { +	unsigned int i; +	Client *c, *t = NULL; +	Window trans; +	Status rettrans; +	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; +	c->oldborder = wa->border_width; +	if(c->w == sw && c->h == sh) { +		c->x = sx; +		c->y = sy; +		c->border = wa->border_width; +	} +	else { +		if(c->x + c->w + 2 * c->border > wax + waw) +			c->x = wax + waw - c->w - 2 * c->border; +		if(c->y + c->h + 2 * c->border > way + wah) +			c->y = way + wah - c->h - 2 * c->border; +		if(c->x < wax) +			c->x = wax; +		if(c->y < way) +			c->y = way; +		c->border = BORDERPX; +	} +	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 */ +	updatesizehints(c); +	XSelectInput(dpy, w, +		StructureNotifyMask | PropertyChangeMask | EnterWindowMask); +	grabbuttons(c, False); +	updatetitle(c); +	if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) +		for(t = clients; t && t->win != trans; t = t->next); +	if(t) +		for(i = 0; i < ntags; i++) +			c->tags[i] = t->tags[i]; +	applyrules(c); +	if(!c->isfloating) +		c->isfloating = (rettrans == Success) || c->isfixed; +	attach(c); +	attachstack(c); +	XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ +	ban(c); +	XMapWindow(dpy, c->win); +	setclientstate(c, NormalState); +	arrange(); +} + +static void +resize(Client *c, int x, int y, int w, int h, Bool sizehints) { +	double dx, dy, max, min, ratio; +	XWindowChanges wc;  + +	if(sizehints) { +		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { +			dx = (double)(w - c->basew); +			dy = (double)(h - c->baseh); +			min = (double)(c->minax) / (double)(c->minay); +			max = (double)(c->maxax) / (double)(c->maxay); +			ratio = dx / dy; +			if(max > 0 && min > 0 && ratio > 0) { +				if(ratio < min) { +					dy = (dx * min + dy) / (min * min + 1); +					dx = dy * min; +					w = (int)dx + c->basew; +					h = (int)dy + c->baseh; +				} +				else if(ratio > max) { +					dy = (dx * min + dy) / (max * max + 1); +					dx = dy * min; +					w = (int)dx + c->basew; +					h = (int)dy + c->baseh; +				} +			} +		} +		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; +		if(c->incw) +			w -= (w - c->basew) % c->incw; +		if(c->inch) +			h -= (h - c->baseh) % c->inch; +	} +	if(w <= 0 || h <= 0) +		return; +	/* 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); +	} +} + +static void +unban(Client *c) { +	if(!c->isbanned) +		return; +	XMoveWindow(dpy, c->win, c->x, c->y); +	c->isbanned = False; +} + +static void +unmanage(Client *c) { +	XWindowChanges wc; + +	wc.border_width = c->oldborder; +	/* The server grab construct avoids race conditions. */ +	XGrabServer(dpy); +	XSetErrorHandler(xerrordummy); +	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ +	detach(c); +	detachstack(c); +	if(sel == c) +		focus(NULL); +	XUngrabButton(dpy, AnyButton, AnyModifier, c->win); +	setclientstate(c, WithdrawnState); +	free(c->tags); +	free(c); +	XSync(dpy, False); +	XSetErrorHandler(xerror); +	XUngrabServer(dpy); +	arrange(); +} + +static 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 if(c->flags & PMinSize) { +		c->basew = size.min_width; +		c->baseh = size.min_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 if(c->flags & PBaseSize) { +		c->minw = size.base_width; +		c->minh = size.base_height; +	} +	else +		c->minw = c->minh = 0; +	if(c->flags & PAspect) { +		c->minax = size.min_aspect.x; +		c->maxax = size.max_aspect.x; +		c->minay = size.min_aspect.y; +		c->maxay = size.max_aspect.y; +	} +	else +		c->minax = c->maxax = c->minay = c->maxay = 0; +	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh +			&& c->maxw == c->minw && c->maxh == c->minh); +} + +static void +updatetitle(Client *c) { +	if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) +		gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); +} + +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) { +	unsigned int i, x; +	Client *c; +	XButtonPressedEvent *ev = &e->xbutton; + +	if(barwin == ev->window) { +		x = 0; +		for(i = 0; i < ntags; i++) { +			x += textw(tags[i]); +			if(ev->x < x) { +				if(ev->button == Button1) { +					if(ev->state & MODKEY) +						tag(tags[i]); +					else +						view(tags[i]); +				} +				else if(ev->button == Button3) { +					if(ev->state & MODKEY) +						toggletag(tags[i]); +					else +						toggleview(tags[i]); +				} +				return; +			} +		} +		if((ev->x < x + blw) && ev->button == Button1) +			setlayout(NULL); +	} +	else if((c = getclient(ev->window))) { +		focus(c); +		if(CLEANMASK(ev->state) != MODKEY) +			return; +		if(ev->button == Button1 && (isfloating() || c->isfloating)) { +			restack(); +			movemouse(c); +		} +		else if(ev->button == Button2) +			zoom(NULL); +		else if(ev->button == Button3 +		&& (isfloating() || c->isfloating) && !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->isfloating || isfloating()) { +			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((c->x + c->w) > sw && c->isfloating) +				c->x = sw / 2 - c->w / 2; /* center in x direction */ +			if((c->y + c->h) > sh && c->isfloating) +				c->y = sh / 2 - c->h / 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); +} + +static void +configurenotify(XEvent *e) { +	XConfigureEvent *ev = &e->xconfigure; + +	if (ev->window == root && (ev->width != sw || ev->height != sh)) { +		sw = ev->width; +		sh = ev->height; +		XFreePixmap(dpy, dc.drawable); +		dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); +		XResizeWindow(dpy, barwin, sw, bh); +		updatebarpos(); +		arrange(); +	} +} + +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))) +		focus(c); +	else if(ev->window == root) { +		selscreen = True; +		focus(NULL); +	} +} + +static void +expose(XEvent *e) { +	XExposeEvent *ev = &e->xexpose; + +	if(ev->count == 0) { +		if(barwin == ev->window) +			drawbar(); +	} +} + +static void +keypress(XEvent *e) { +	KEYS +	unsigned int len = sizeof keys / sizeof keys[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 == keys[i].keysym +		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) +		{ +			if(keys[i].func) +				keys[i].func(keys[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->isfloating && (c->isfloating = (getclient(trans) != NULL))) +					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) +				drawbar(); +		} +	} +} + +static void +unmapnotify(XEvent *e) { +	Client *c; +	XUnmapEvent *ev = &e->xunmap; + +	if((c = getclient(ev->window))) +		unmanage(c); +} + +static void +grabkeys(void) { +	KEYS +	unsigned int len = sizeof keys / sizeof keys[0]; +	unsigned int i; +	KeyCode code; + +	XUngrabKey(dpy, AnyKey, AnyModifier, root); +	for(i = 0; i < len; i++) { +		code = XKeysymToKeycode(dpy, keys[i].keysym); +		XGrabKey(dpy, code, keys[i].mod, root, True, +				GrabModeAsync, GrabModeAsync); +		XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, +				GrabModeAsync, GrabModeAsync); +		XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True, +				GrabModeAsync, GrabModeAsync); +		XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True, +				GrabModeAsync, GrabModeAsync); +	} +} + +static unsigned int +idxoftag(const char *tag) { +	unsigned int i; + +	for(i = 0; i < ntags; i++) +		if(tags[i] == tag) +			return i; +	return 0; +} + +static void +floating(void) { /* default floating layout */ +	Client *c; + +	for(c = clients; c; c = c->next) +		if(isvisible(c)) +			resize(c, c->x, c->y, c->w, c->h, True); +} + +static void +applyrules(Client *c) { +	static char buf[512]; +	unsigned int i, j; +	regmatch_t tmp; +	Bool matched = False; +	XClassHint ch = { 0 }; + +	/* rule matching */ +	XGetClassHint(dpy, c->win, &ch); +	snprintf(buf, sizeof buf, "%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, buf, 1, &tmp, 0)) { +			c->isfloating = rules[i].isfloating; +			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] = seltags[i]; +} + +static void +compileregs(void) { +	unsigned int i; +	regex_t *reg; + +	if(regs) +		return; +	nrules = sizeof rules / sizeof rules[0]; +	regs = emallocz(nrules * sizeof(Regs)); +	for(i = 0; i < nrules; i++) { +		if(rules[i].prop) { +			reg = emallocz(sizeof(regex_t)); +			if(regcomp(reg, rules[i].prop, REG_EXTENDED)) +				free(reg); +			else +				regs[i].propregex = reg; +		} +		if(rules[i].tags) { +			reg = emallocz(sizeof(regex_t)); +			if(regcomp(reg, rules[i].tags, REG_EXTENDED)) +				free(reg); +			else +				regs[i].tagregex = reg; +		} +	} +} + +static void +focusnext(const char *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(); +	} +} + +static void +focusprev(const char *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(); +	} +} + +static void +initlayouts(void) { +	unsigned int i, w; + +	nlayouts = sizeof layouts / sizeof layouts[0]; +	for(blw = i = 0; i < nlayouts; i++) { +		w = textw(layouts[i].symbol); +		if(w > blw) +			blw = w; +	} +} + +static Bool +isfloating(void) { +	return layouts[ltidx].arrange == floating; +} + +static Bool +isvisible(Client *c) { +	unsigned int i; + +	for(i = 0; i < ntags; i++) +		if(c->tags[i] && seltags[i]) +			return True; +	return False; +} + +static void +restack(void) { +	Client *c; +	XEvent ev; +	XWindowChanges wc; + +	drawbar(); +	if(!sel) +		return; +	if(sel->isfloating || isfloating()) +		XRaiseWindow(dpy, sel->win); +	if(!isfloating()) { +		wc.stack_mode = Below; +		wc.sibling = barwin; +		if(!sel->isfloating) { +			XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); +			wc.sibling = sel->win; +		} +		for(c = nexttiled(clients); c; c = nexttiled(c->next)) { +			if(c == sel) +				continue; +			XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); +			wc.sibling = c->win; +		} +	} +	XSync(dpy, False); +	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +setlayout(const char *arg) { +	unsigned int i; + +	if(!arg) { +		if(++ltidx == nlayouts) +			ltidx = 0;; +	} +	else { +		for(i = 0; i < nlayouts; i++) +			if(!strcmp(arg, layouts[i].symbol)) +				break; +		if(i == nlayouts) +			return; +		ltidx = i; +	} +	if(sel) +		arrange(); +	else +		drawbar(); +} + +static void +tag(const char *arg) { +	unsigned int i; + +	if(!sel) +		return; +	for(i = 0; i < ntags; i++) +		sel->tags[i] = arg == NULL; +	i = idxoftag(arg); +	if(i >= 0 && i < ntags) +		sel->tags[i] = True; +	arrange(); +} + +static void +togglefloating(const char *arg) { +	if(!sel) +		return; +	sel->isfloating = !sel->isfloating; +	if(sel->isfloating) +		resize(sel, sel->x, sel->y, sel->w, sel->h, True); +	arrange(); +} + +static void +togglemax(const char *arg) { +	XEvent ev; + +	if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) +		return; +	if((sel->ismax = !sel->ismax)) { +		sel->rx = sel->x; +		sel->ry = sel->y; +		sel->rw = sel->w; +		sel->rh = sel->h; +		resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); +	} +	else +		resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); +	drawbar(); +	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +toggletag(const char *arg) { +	unsigned int i, j; + +	if(!sel) +		return; +	i = idxoftag(arg); +	sel->tags[i] = !sel->tags[i]; +	for(j = 0; j < ntags && !sel->tags[j]; j++); +	if(j == ntags) +		sel->tags[i] = True; +	arrange(); +} + +static void +toggleview(const char *arg) { +	unsigned int i, j; + +	i = idxoftag(arg); +	seltags[i] = !seltags[i]; +	for(j = 0; j < ntags && !seltags[j]; j++); +	if(j == ntags) +		seltags[i] = True; /* cannot toggle last view */ +	arrange(); +} + +static void +view(const char *arg) { +	unsigned int i; + +	for(i = 0; i < ntags; i++) +		seltags[i] = arg == NULL; +	i = idxoftag(arg); +	if(i >= 0 && i < ntags) +		seltags[i] = True; +	arrange(); +} + +static void +cleanup(void) { +	close(STDIN_FILENO); +	while(stack) { +		unban(stack); +		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(seltags); +} + +static long +getstate(Window w) { +	int format, status; +	long result = -1; +	unsigned char *p = NULL; +	unsigned long n, extra; +	Atom real; + +	status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], +			&real, &format, &n, &extra, (unsigned char **)&p); +	if(status != Success) +		return -1; +	if(n != 0) +		result = *p; +	XFree(p); +	return result; +} + +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 || getstate(wins[i]) == IconicState) +				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); +} + +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[WMName] = XInternAtom(dpy, "WM_NAME", 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 */ +	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 | StructureNotifyMask; +	wa.cursor = cursor[CurNormal]; +	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); +	XSelectInput(dpy, root, wa.event_mask); +	grabkeys(); +	compileregs(); +	for(ntags = 0; tags[ntags]; ntags++); +	seltags = emallocz(sizeof(Bool) * ntags); +	seltags[0] = True; +	/* geometry */ +	sx = sy = 0; +	sw = DisplayWidth(dpy, screen); +	sh = DisplayHeight(dpy, screen); +	initstyle(); +	initlayouts(); +	initbar(); +	/* 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; +} + +static Bool +gettextprop(Window w, Atom atom, char *text, unsigned int size) { +	char **list = NULL; +	int n; +	XTextProperty name; + +	if(!text || size == 0) +		return False; +	text[0] = '\0'; +	XGetTextProperty(dpy, w, &name, atom); +	if(!name.nitems) +		return False; +	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 True; +} + +static void +quit(const char *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. + */ +static 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 */ +} + +static void +arrange(void) { +	Client *c; + +	for(c = clients; c; c = c->next) +		if(isvisible(c)) +			unban(c); +		else +			ban(c); +	layouts[ltidx].arrange(); +	focus(NULL); +	restack(); +} + +static void +attach(Client *c) { +	if(clients) +		clients->prev = c; +	c->next = clients; +	clients = c; +} + +static 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; +} + +static void +focus(Client *c) { +	if((!c && selscreen) || (c && !isvisible(c))) +		for(c = stack; c && !isvisible(c); c = c->snext); +	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; +	drawbar(); +	if(!selscreen) +		return; +	if(c) { +		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); +		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); +	} +	else +		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +} + +static Bool +isarrange(void (*func)()) +{ +	return func == layouts[ltidx].arrange; +} + +static Client * +nexttiled(Client *c) { +	for(; c && (c->isfloating || !isvisible(c)); c = c->next); +	return c; +} + +static void +setmwfact(const char *arg) { +	double delta; + +	if(!isarrange(tile)) +		return; +	/* arg handling, manipulate mwfact */ +	if(arg == NULL) +		mwfact = MWFACT; +	else if(1 == sscanf(arg, "%lf", &delta)) { +		if(arg[0] != '+' && arg[0] != '-') +			mwfact = delta; +		else +			mwfact += delta; +		if(mwfact < 0.1) +			mwfact = 0.1; +		else if(mwfact > 0.9) +			mwfact = 0.9; +	} +	arrange(); +} + +static void +tile(void) { +	unsigned int i, n, nx, ny, nw, nh, mw, th; +	Client *c; + +	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) +		n++; + +	/* window geoms */ +	mw = (n == 1) ? waw : mwfact * waw; +	th = (n > 1) ? wah / (n - 1) : 0; +	if(n > 1 && th < bh) +		th = wah; + +	nx = wax; +	ny = way; +	for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { +		c->ismax = False; +		if(i == 0) { /* master */ +			nw = mw - 2 * c->border; +			nh = wah - 2 * c->border; +		} +		else {  /* tile window */ +			if(i == 1) { +				ny = way; +				nx += mw; +			} +			nw = waw - mw - 2 * c->border; +			if(i + 1 == n) /* remainder */ +				nh = (way + wah) - ny - 2 * c->border; +			else +				nh = th - 2 * c->border; +		} +		resize(c, nx, ny, nw, nh, RESIZEHINTS); +		if(n > 1 && th != wah) +			ny += nh + 2 * c->border; +	} +} + +static void +zoom(const char *arg) { +	Client *c; + +	if(!sel || !isarrange(tile) || sel->isfloating) +		return; +	if((c = sel) == nexttiled(clients)) +		if(!(c = nexttiled(c->next))) +			return; +	detach(c); +	attach(c); +	focus(c); +	arrange(); +} + +int +main(int argc, char *argv[]) { +	char *p; +	int r, xfd; +	fd_set rd; +	XEvent ev; + +	if(argc == 2 && !strcmp("-v", argv[1])) +		eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\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(); +	drawbar(); +	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); +			} +			drawbar(); +		} +		while(XPending(dpy)) { +			XNextEvent(dpy, &ev); +			if(handler[ev.type]) +				(handler[ev.type])(&ev); /* call handler */ +		} +	} +	cleanup(); +	XCloseDisplay(dpy); +	return 0; +} @@ -1,147 +0,0 @@ -/* See LICENSE file for copyright and 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 <X11/Xlib.h> - -/* mask shorthands, used in event.c and client.c */ -#define BUTTONMASK		(ButtonPressMask | ButtonReleaseMask) - -enum { BarTop, BarBot, BarOff };			/* bar position */ -enum { CurNormal, CurResize, CurMove, CurLast };	/* cursor */ -enum { ColBorder, ColFG, ColBG, ColLast };		/* color */ -enum { NetSupported, NetWMName, NetLast };		/* EWMH atoms */ -enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */ - -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, maxax, minay, maxay; -	long flags;  -	unsigned int border, oldborder; -	Bool isbanned, isfixed, ismax, isfloating; -	Bool *tags; -	Client *next; -	Client *prev; -	Client *snext; -	Window win; -}; - -typedef struct { -	int x, y, w, h; -	unsigned long norm[ColLast]; -	unsigned long sel[ColLast]; -	Drawable drawable; -	GC gc; -	struct { -		int ascent; -		int descent; -		int height; -		XFontSet set; -		XFontStruct *xfont; -	} font; -} DC; /* draw context */ - -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, bpos;		/* bar height, bar layout label width, bar position */ -extern unsigned int ntags, numlockmask;		/* number of tags, numlock mask */ -extern void (*handler[LASTEvent])(XEvent *);	/* event handler */ -extern Atom wmatom[WMLast], netatom[NetLast]; -extern Bool selscreen, *seltags;		/* seltags 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 Window root, barwin; - -/* bar.c */ -void drawbar(void);			/* draw the bar */ -void initbar(void);			/* initializes the bar */ -void initstyle(void);			/* initializes colors and font */ -unsigned int textw(const char *text);	/* return the width of text in px*/ -void togglebar(const char *arg);	/* shows/hides the bar */ -void updatebarpos(void);		/* updates the bar position */ - -/* client.c */ -void attach(Client *c);			/* attaches c to global client list */ -void ban(Client *c);			/* bans c */ -void configure(Client *c);		/* send synthetic configure event */ -void detach(Client *c);			/* detaches c from global client list */ -void focus(Client *c);			/* focus c if visible && !NULL, or focus top visible */ -void killclient(const char *arg);	/* kill sel  nicely */ -void manage(Window w, XWindowAttributes *wa);	/* manage new client */ -void resize(Client *c, int x, int y, -		int w, int h, Bool sizehints);	/* resize with given coordinates c*/ -void unban(Client *c);			/* unbans c */ -void unmanage(Client *c);		/* unmanage c */ -void updatesizehints(Client *c);	/* update the size hint variables of c */ -void updatetitle(Client *c);		/* update the name of c */ - -/* event.c */ -void grabkeys(void);			/* grab all keys defined in config.h */ - -/* main.c */ -Bool gettextprop(Window w, Atom atom, -		char *text, unsigned int size); /* return text property, UTF-8 compliant */ -void quit(const char *arg);			/* quit dwm nicely */ -int xerror(Display *dsply, XErrorEvent *ee);	/* dwm's X error handler */ - -/* screen.c */ -void applyrules(Client *c);		/* applies rules to c */ -void arrange(void);			/* arranges all windows depending on the layout in use */ -void compileregs(void);			/* initialize regexps of rules defined in config.h */ -void focusnext(const char *arg);	/* focuses next visible client */ -void focusprev(const char *arg);	/* focuses prev visible client */ -const char *getsymbol(void);		/* returns symbol of enabled layout */ -void initlayouts(void);			/* initialize layout array */ -Bool isarrange(void (*func)());		/* returns True if func is the layout function in use */ -Bool isfloating(void);			/* returns True if floating layout is enabled */ -Bool isvisible(Client *c);		/* returns True if client is visible */ -Client *nexttiled(Client *c);		/* returns tiled successor of c */ -void restack(void);			/* restores z layers of all clients */ -void setlayout(const char *arg);	/* sets layout, NULL means next layout */ -void tag(const char *arg);		/* tags sel with arg's index */ -void togglefloating(const char *arg);	/* toggles sel between floating/tiled state */ -void togglemax(const char *arg);	/* toggles maximization of floating client */ -void toggletag(const char *arg);	/* toggles sel tags with arg's index */ -void toggleview(const char *arg);	/* toggles the tag with arg's index (in)visible */ -void view(const char *arg);		/* views the tag with arg's index */ - -/* util.c */ -void *emallocz(unsigned int size);	/* allocates zero-initialized memory, exits on error */ -void eprint(const char *errstr, ...);	/* prints errstr and exits with 1 */ -void spawn(const char *arg);		/* forks a new subprocess with arg's cmd */ diff --git a/event.c b/event.c deleted file mode 100644 index 16201f3..0000000 --- a/event.c +++ /dev/null @@ -1,375 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <stdlib.h> -#include <X11/keysym.h> -#include <X11/Xatom.h> -#include <X11/Xutil.h> - -/* static */ - -typedef struct { -	unsigned long mod; -	KeySym keysym; -	void (*func)(const char *arg); -	const char *arg; -} Key; - -#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) { -	unsigned int i, x; -	Client *c; -	XButtonPressedEvent *ev = &e->xbutton; - -	if(barwin == ev->window) { -		x = 0; -		for(i = 0; i < ntags; i++) { -			x += textw(tags[i]); -			if(ev->x < x) { -				if(ev->button == Button1) { -					if(ev->state & MODKEY) -						tag(tags[i]); -					else -						view(tags[i]); -				} -				else if(ev->button == Button3) { -					if(ev->state & MODKEY) -						toggletag(tags[i]); -					else -						toggleview(tags[i]); -				} -				return; -			} -		} -		if((ev->x < x + blw) && ev->button == Button1) -			setlayout(NULL); -	} -	else if((c = getclient(ev->window))) { -		focus(c); -		if(CLEANMASK(ev->state) != MODKEY) -			return; -		if(ev->button == Button1 && (isfloating() || c->isfloating)) { -			restack(); -			movemouse(c); -		} -		else if(ev->button == Button2) -			zoom(NULL); -		else if(ev->button == Button3 -		&& (isfloating() || c->isfloating) && !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->isfloating || isfloating()) { -			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((c->x + c->w) > sw && c->isfloating) -				c->x = sw / 2 - c->w / 2; /* center in x direction */ -			if((c->y + c->h) > sh && c->isfloating) -				c->y = sh / 2 - c->h / 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); -} - -static void -configurenotify(XEvent *e) { -	XConfigureEvent *ev = &e->xconfigure; - -	if (ev->window == root && (ev->width != sw || ev->height != sh)) { -		sw = ev->width; -		sh = ev->height; -		XFreePixmap(dpy, dc.drawable); -		dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); -		XResizeWindow(dpy, barwin, sw, bh); -		updatebarpos(); -		arrange(); -	} -} - -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))) -		focus(c); -	else if(ev->window == root) { -		selscreen = True; -		focus(NULL); -	} -} - -static void -expose(XEvent *e) { -	XExposeEvent *ev = &e->xexpose; - -	if(ev->count == 0) { -		if(barwin == ev->window) -			drawbar(); -	} -} - -static void -keypress(XEvent *e) { -	KEYS -	unsigned int len = sizeof keys / sizeof keys[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 == keys[i].keysym -		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) -		{ -			if(keys[i].func) -				keys[i].func(keys[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->isfloating && (c->isfloating = (getclient(trans) != NULL))) -					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) -				drawbar(); -		} -	} -} - -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, -	[ConfigureNotify] = configurenotify, -	[DestroyNotify] = destroynotify, -	[EnterNotify] = enternotify, -	[LeaveNotify] = leavenotify, -	[Expose] = expose, -	[KeyPress] = keypress, -	[MappingNotify] = mappingnotify, -	[MapRequest] = maprequest, -	[PropertyNotify] = propertynotify, -	[UnmapNotify] = unmapnotify -}; - -void -grabkeys(void) { -	KEYS -	unsigned int len = sizeof keys / sizeof keys[0]; -	unsigned int i; -	KeyCode code; - -	XUngrabKey(dpy, AnyKey, AnyModifier, root); -	for(i = 0; i < len; i++) { -		code = XKeysymToKeycode(dpy, keys[i].keysym); -		XGrabKey(dpy, code, keys[i].mod, root, True, -				GrabModeAsync, GrabModeAsync); -		XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, -				GrabModeAsync, GrabModeAsync); -		XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True, -				GrabModeAsync, GrabModeAsync); -		XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True, -				GrabModeAsync, GrabModeAsync); -	} -} @@ -1,296 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <errno.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/select.h> -#include <X11/cursorfont.h> -#include <X11/keysym.h> -#include <X11/Xatom.h> -#include <X11/Xproto.h> -#include <X11/Xutil.h> - -/* extern */ - -char stext[256]; -int screen, sx, sy, sw, sh, wax, way, waw, wah; -unsigned int ntags; -unsigned int numlockmask = 0; -Atom wmatom[WMLast], netatom[NetLast]; -Bool *seltags; -Bool selscreen = True; -Client *clients = NULL; -Client *sel = NULL; -Client *stack = NULL; -Cursor cursor[CurLast]; -Display *dpy; -Window root; - -/* static */ - -static int (*xerrorxlib)(Display *, XErrorEvent *); -static Bool otherwm, readin; -static Bool running = True; - -static void -cleanup(void) { -	close(STDIN_FILENO); -	while(stack) { -		unban(stack); -		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(seltags); -} - -static long -getstate(Window w) { -	int format, status; -	long result = -1; -	unsigned char *p = NULL; -	unsigned long n, extra; -	Atom real; - -	status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], -			&real, &format, &n, &extra, (unsigned char **)&p); -	if(status != Success) -		return -1; -	if(n != 0) -		result = *p; -	XFree(p); -	return result; -} - -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 || getstate(wins[i]) == IconicState) -				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); -} - -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[WMName] = XInternAtom(dpy, "WM_NAME", 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 */ -	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 | StructureNotifyMask; -	wa.cursor = cursor[CurNormal]; -	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); -	XSelectInput(dpy, root, wa.event_mask); -	grabkeys(); -	compileregs(); -	for(ntags = 0; tags[ntags]; ntags++); -	seltags = emallocz(sizeof(Bool) * ntags); -	seltags[0] = True; -	/* geometry */ -	sx = sy = 0; -	sw = DisplayWidth(dpy, screen); -	sh = DisplayHeight(dpy, screen); -	initstyle(); -	initlayouts(); -	initbar(); -	/* 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 */ - -Bool -gettextprop(Window w, Atom atom, char *text, unsigned int size) { -	char **list = NULL; -	int n; -	XTextProperty name; - -	if(!text || size == 0) -		return False; -	text[0] = '\0'; -	XGetTextProperty(dpy, w, &name, atom); -	if(!name.nitems) -		return False; -	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 True; -} - -void -quit(const char *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 && !strcmp("-v", argv[1])) -		eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\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(); -	drawbar(); -	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); -			} -			drawbar(); -		} -		while(XPending(dpy)) { -			XNextEvent(dpy, &ev); -			if(handler[ev.type]) -				(handler[ev.type])(&ev); /* call handler */ -		} -	} -	cleanup(); -	XCloseDisplay(dpy); -	return 0; -} diff --git a/screen.c b/screen.c deleted file mode 100644 index 142ed56..0000000 --- a/screen.c +++ /dev/null @@ -1,340 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <regex.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <X11/Xutil.h> - -/* static */ - -typedef struct { -	const char *symbol; -	void (*arrange)(void); -} Layout; - -typedef struct { -	const char *prop; -	const char *tags; -	Bool isfloating; -} Rule; - -typedef struct { -	regex_t *propregex; -	regex_t *tagregex; -} Regs; - -TAGS -RULES - -static unsigned int nrules = 0; -static unsigned int nlayouts = 0; -static unsigned int ltidx = 0; /* default */ -static Regs *regs = NULL; - -static unsigned int -idxoftag(const char *tag) { -	unsigned int i; - -	for(i = 0; i < ntags; i++) -		if(tags[i] == tag) -			return i; -	return 0; -} - -static void -floating(void) { /* default floating layout */ -	Client *c; - -	for(c = clients; c; c = c->next) -		if(isvisible(c)) -			resize(c, c->x, c->y, c->w, c->h, True); -} - -LAYOUTS - -/* extern */ - -unsigned int blw = 0; - -void -applyrules(Client *c) { -	static char buf[512]; -	unsigned int i, j; -	regmatch_t tmp; -	Bool matched = False; -	XClassHint ch = { 0 }; - -	/* rule matching */ -	XGetClassHint(dpy, c->win, &ch); -	snprintf(buf, sizeof buf, "%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, buf, 1, &tmp, 0)) { -			c->isfloating = rules[i].isfloating; -			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] = seltags[i]; -} - -void -arrange(void) { -	Client *c; - -	for(c = clients; c; c = c->next) -		if(isvisible(c)) -			unban(c); -		else -			ban(c); -	layouts[ltidx].arrange(); -	focus(NULL); -	restack(); -} - -void -compileregs(void) { -	unsigned int i; -	regex_t *reg; - -	if(regs) -		return; -	nrules = sizeof rules / sizeof rules[0]; -	regs = emallocz(nrules * sizeof(Regs)); -	for(i = 0; i < nrules; i++) { -		if(rules[i].prop) { -			reg = emallocz(sizeof(regex_t)); -			if(regcomp(reg, rules[i].prop, REG_EXTENDED)) -				free(reg); -			else -				regs[i].propregex = reg; -		} -		if(rules[i].tags) { -			reg = emallocz(sizeof(regex_t)); -			if(regcomp(reg, rules[i].tags, REG_EXTENDED)) -				free(reg); -			else -				regs[i].tagregex = reg; -		} -	} -} - -void -focusnext(const char *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(const char *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(); -	} -} - -const char * -getsymbol(void) -{ -	return layouts[ltidx].symbol; -} - -void -initlayouts(void) { -	unsigned int i, w; - -	nlayouts = sizeof layouts / sizeof layouts[0]; -	for(blw = i = 0; i < nlayouts; i++) { -		w = textw(layouts[i].symbol); -		if(w > blw) -			blw = w; -	} -} - -Bool -isfloating(void) { -	return layouts[ltidx].arrange == floating; -} - -Bool -isarrange(void (*func)()) -{ -	return func == layouts[ltidx].arrange; -} - -Bool -isvisible(Client *c) { -	unsigned int i; - -	for(i = 0; i < ntags; i++) -		if(c->tags[i] && seltags[i]) -			return True; -	return False; -} - -Client * -nexttiled(Client *c) { -	for(; c && (c->isfloating || !isvisible(c)); c = c->next); -	return c; -} - -void -restack(void) { -	Client *c; -	XEvent ev; -	XWindowChanges wc; - -	drawbar(); -	if(!sel) -		return; -	if(sel->isfloating || isfloating()) -		XRaiseWindow(dpy, sel->win); -	if(!isfloating()) { -		wc.stack_mode = Below; -		wc.sibling = barwin; -		if(!sel->isfloating) { -			XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); -			wc.sibling = sel->win; -		} -		for(c = nexttiled(clients); c; c = nexttiled(c->next)) { -			if(c == sel) -				continue; -			XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); -			wc.sibling = c->win; -		} -	} -	XSync(dpy, False); -	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -setlayout(const char *arg) { -	unsigned int i; - -	if(!arg) { -		if(++ltidx == nlayouts) -			ltidx = 0;; -	} -	else { -		for(i = 0; i < nlayouts; i++) -			if(!strcmp(arg, layouts[i].symbol)) -				break; -		if(i == nlayouts) -			return; -		ltidx = i; -	} -	if(sel) -		arrange(); -	else -		drawbar(); -} - -void -tag(const char *arg) { -	unsigned int i; - -	if(!sel) -		return; -	for(i = 0; i < ntags; i++) -		sel->tags[i] = arg == NULL; -	i = idxoftag(arg); -	if(i >= 0 && i < ntags) -		sel->tags[i] = True; -	arrange(); -} - -void -togglefloating(const char *arg) { -	if(!sel) -		return; -	sel->isfloating = !sel->isfloating; -	if(sel->isfloating) -		resize(sel, sel->x, sel->y, sel->w, sel->h, True); -	arrange(); -} - -void -togglemax(const char *arg) { -	XEvent ev; - -	if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) -		return; -	if((sel->ismax = !sel->ismax)) { -		sel->rx = sel->x; -		sel->ry = sel->y; -		sel->rw = sel->w; -		sel->rh = sel->h; -		resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); -	} -	else -		resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); -	drawbar(); -	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -toggletag(const char *arg) { -	unsigned int i, j; - -	if(!sel) -		return; -	i = idxoftag(arg); -	sel->tags[i] = !sel->tags[i]; -	for(j = 0; j < ntags && !sel->tags[j]; j++); -	if(j == ntags) -		sel->tags[i] = True; -	arrange(); -} - -void -toggleview(const char *arg) { -	unsigned int i, j; - -	i = idxoftag(arg); -	seltags[i] = !seltags[i]; -	for(j = 0; j < ntags && !seltags[j]; j++); -	if(j == ntags) -		seltags[i] = True; /* cannot toggle last view */ -	arrange(); -} - -void -view(const char *arg) { -	unsigned int i; - -	for(i = 0; i < ntags; i++) -		seltags[i] = arg == NULL; -	i = idxoftag(arg); -	if(i >= 0 && i < ntags) -		seltags[i] = True; -	arrange(); -} @@ -1,85 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <stdio.h> - -/* static */ - -static double mwfact = MWFACT; - -/* extern */ - -void -setmwfact(const char *arg) { -	double delta; - -	if(!isarrange(tile)) -		return; -	/* arg handling, manipulate mwfact */ -	if(arg == NULL) -		mwfact = MWFACT; -	else if(1 == sscanf(arg, "%lf", &delta)) { -		if(arg[0] != '+' && arg[0] != '-') -			mwfact = delta; -		else -			mwfact += delta; -		if(mwfact < 0.1) -			mwfact = 0.1; -		else if(mwfact > 0.9) -			mwfact = 0.9; -	} -	arrange(); -} - -void -tile(void) { -	unsigned int i, n, nx, ny, nw, nh, mw, th; -	Client *c; - -	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) -		n++; - -	/* window geoms */ -	mw = (n == 1) ? waw : mwfact * waw; -	th = (n > 1) ? wah / (n - 1) : 0; -	if(n > 1 && th < bh) -		th = wah; - -	nx = wax; -	ny = way; -	for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { -		c->ismax = False; -		if(i == 0) { /* master */ -			nw = mw - 2 * c->border; -			nh = wah - 2 * c->border; -		} -		else {  /* tile window */ -			if(i == 1) { -				ny = way; -				nx += mw; -			} -			nw = waw - mw - 2 * c->border; -			if(i + 1 == n) /* remainder */ -				nh = (way + wah) - ny - 2 * c->border; -			else -				nh = th - 2 * c->border; -		} -		resize(c, nx, ny, nw, nh, RESIZEHINTS); -		if(n > 1 && th != wah) -			ny += nh + 2 * c->border; -	} -} - -void -zoom(const char *arg) { -	Client *c; - -	if(!sel || !isarrange(tile) || sel->isfloating) -		return; -	if((c = sel) == nexttiled(clients)) -		if(!(c = nexttiled(c->next))) -			return; -	detach(c); -	attach(c); -	focus(c); -	arrange(); -} @@ -1,6 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -/* tile.c */ -void setmwfact(const char *arg);	/* sets master width factor */ -void tile(void);			/* arranges all windows tiled */ -void zoom(const char *arg);		/* zooms the focused client to master area, arg is ignored */ @@ -1,52 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/wait.h> -#include <unistd.h> - -/* 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(const char *arg) { -	static char *shell = NULL; - -	if(!shell && !(shell = getenv("SHELL"))) -		shell = "/bin/sh"; -	if(!arg) -		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, (char *)NULL); -			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); -			perror(" failed"); -		} -		exit(0); -	} -	wait(0); -} | 
