about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2013-05-29 22:37:37 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2013-05-29 22:41:51 +0200
commit5515a365ba77cec3e7bf9797c3a4cc5e6c5c91ae (patch)
tree437f902828ef965474a0bcf9402f30bf937135cb
parent843f18aaee1550344b8521d95d952502e064c5f0 (diff)
parent692d341dfc6b059db570356e9dfadba92dc93f19 (diff)
downloadcwm-5515a365ba77cec3e7bf9797c3a4cc5e6c5c91ae.tar.gz
cwm-5515a365ba77cec3e7bf9797c3a4cc5e6c5c91ae.tar.xz
cwm-5515a365ba77cec3e7bf9797c3a4cc5e6c5c91ae.zip
cvsimport
-rw-r--r--Makefile4
-rw-r--r--calmwm.c10
-rw-r--r--calmwm.h95
-rw-r--r--client.c56
-rw-r--r--conf.c178
-rw-r--r--font.c105
-rw-r--r--group.c11
-rw-r--r--kbfunc.c17
-rw-r--r--menu.c133
-rw-r--r--mousefunc.c34
-rw-r--r--parse.y111
-rw-r--r--screen.c7
-rw-r--r--xevents.c56
-rw-r--r--xutil.c183
14 files changed, 485 insertions, 515 deletions
diff --git a/Makefile b/Makefile
index 58a27f4..e0abfac 100644
--- a/Makefile
+++ b/Makefile
@@ -7,11 +7,11 @@ PREFIX=         /usr/local
 
 SRCS=		calmwm.c screen.c xmalloc.c client.c menu.c \
 		search.c util.c xutil.c conf.c xevents.c group.c \
-		kbfunc.c mousefunc.c font.c parse.y
+		kbfunc.c mousefunc.c parse.y
 
 OBJS=		calmwm.o screen.o xmalloc.o client.o menu.o \
 		search.o util.o xutil.o conf.o xevents.o group.o \
-		kbfunc.o mousefunc.o font.o strlcpy.o strlcat.o y.tab.o \
+		kbfunc.o mousefunc.o strlcpy.o strlcat.o y.tab.o \
 		strtonum.o fgetln.o
 
 CPPFLAGS+=	`pkg-config --cflags fontconfig x11 xft xinerama xrandr`
diff --git a/calmwm.c b/calmwm.c
index 237071b..1ee349e 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -144,7 +144,6 @@ dpy_init(const char *dpyname)
 static void
 x_setup(void)
 {
-	struct keybinding	*kb;
 	int			 i;
 
 	Cursor_default = XCreateFontCursor(X_Dpy, XC_X_cursor);
@@ -155,13 +154,6 @@ x_setup(void)
 
 	for (i = 0; i < ScreenCount(X_Dpy); i++)
 		screen_init(i);
-
-	/*
-	 * XXX key grabs weren't done before, since Screenq was empty,
-	 * do them here for now (this needs changing).
-	 */
-	TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
-		conf_grab(&Conf, kb);
 }
 
 static void
@@ -181,7 +173,7 @@ x_wmerrorhandler(Display *dpy, XErrorEvent *e)
 static int
 x_errorhandler(Display *dpy, XErrorEvent *e)
 {
-#if DEBUG
+#ifdef DEBUG
 	char msg[80], number[80], req[80];
 
 	XGetErrorText(X_Dpy, e->error_code, msg, sizeof(msg));
diff --git a/calmwm.h b/calmwm.h
index c08f6a5..d5119b1 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -97,20 +97,16 @@ union arg {
 	int	 i;
 };
 
-enum menucolor {
-	CWM_COLOR_MENU_FG,
-	CWM_COLOR_MENU_BG,
-	CWM_COLOR_MENU_FONT,
-	CWM_COLOR_MENU_FONT_SEL,
-	CWM_COLOR_MENU_MAX
-};
-
-enum bordercolor {
+enum color {
 	CWM_COLOR_BORDER_ACTIVE,
 	CWM_COLOR_BORDER_INACTIVE,
 	CWM_COLOR_BORDER_GROUP,
 	CWM_COLOR_BORDER_UNGROUP,
-	CWM_COLOR_BORDER_MAX
+	CWM_COLOR_MENU_FG,
+	CWM_COLOR_MENU_BG,
+	CWM_COLOR_MENU_FONT,
+	CWM_COLOR_MENU_FONT_SEL,
+	CWM_COLOR_MAX
 };
 
 struct geom {
@@ -133,6 +129,11 @@ struct winname {
 };
 TAILQ_HEAD(winname_q, winname);
 
+enum wm_protocols {
+	_WM_DELETE_WINDOW	= 0x0001,
+	_WM_TAKE_FOCUS		= 0x0002,
+};
+
 struct client_ctx {
 	TAILQ_ENTRY(client_ctx) entry;
 	TAILQ_ENTRY(client_ctx) group_entry;
@@ -159,9 +160,7 @@ struct client_ctx {
 		int		 x;	/* x position */
 		int		 y;	/* y position */
 	} ptr;
-#define CLIENT_PROTO_DELETE		 0x0001
-#define CLIENT_PROTO_TAKEFOCUS		 0x0002
-	int			 xproto;
+	enum wm_protocols	 xproto;
 #define CLIENT_HIDDEN			0x0001
 #define CLIENT_IGNORE			0x0002
 #define CLIENT_VMAXIMIZED		0x0004
@@ -222,13 +221,12 @@ struct screen_ctx {
 	Colormap		 colormap;
 	Window			 rootwin;
 	Window			 menuwin;
-	unsigned long		 color[CWM_COLOR_BORDER_MAX];
 	int			 cycling;
 	struct geom		 view; /* viewable area */
 	struct geom		 work; /* workable area, gap-applied */
 	struct gap		 gap;
 	struct cycle_entry_q	 mruq;
-	XftColor		 xftcolor[CWM_COLOR_MENU_MAX];
+	XftColor		 xftcolor[CWM_COLOR_MAX];
 	XftDraw			*xftdraw;
 	XftFont			*xftfont;
 	int			 xinerama_no;
@@ -247,8 +245,8 @@ struct keybinding {
 	TAILQ_ENTRY(keybinding)	 entry;
 	void			(*callback)(struct client_ctx *, union arg *);
 	union arg		 argument;
-	int			 modmask;
-	int			 keysym;
+	u_int			 modmask;
+	KeySym			 keysym;
 	int			 keycode;
 #define KBFLAG_NEEDCLIENT	 0x0001
 	int			 flags;
@@ -259,8 +257,8 @@ TAILQ_HEAD(keybinding_q, keybinding);
 struct mousebinding {
 	TAILQ_ENTRY(mousebinding)	entry;
 	void			 	(*callback)(struct client_ctx *, void *);
-	int				modmask;
-	int			 	button;
+	u_int				modmask;
+	u_int			 	button;
 #define MOUSEBIND_CTX_ROOT		0x0001
 #define MOUSEBIND_CTX_WIN		0x0002
 	int				context;
@@ -302,8 +300,7 @@ struct conf {
 #define	CONF_SNAPDIST			0
 	int			 snapdist;
 	struct gap		 gap;
-	char			*color[CWM_COLOR_BORDER_MAX];
-	char		 	*menucolor[CWM_COLOR_MENU_MAX];
+	char			*color[CWM_COLOR_MAX];
 	char			 termpath[MAXPATHLEN];
 	char			 lockpath[MAXPATHLEN];
 	char			 known_hosts[MAXPATHLEN];
@@ -343,7 +340,7 @@ void			 client_lower(struct client_ctx *);
 void			 client_map(struct client_ctx *);
 void			 client_maximize(struct client_ctx *);
 void			 client_move(struct client_ctx *);
-struct client_ctx	*client_new(Window, struct screen_ctx *, int);
+struct client_ctx	*client_init(Window, struct screen_ctx *, int);
 void			 client_ptrsave(struct client_ctx *);
 void			 client_ptrwarp(struct client_ctx *);
 void			 client_raise(struct client_ctx *);
@@ -360,7 +357,6 @@ void			 client_warp(struct client_ctx *);
 
 void			 group_alltoggle(struct screen_ctx *);
 void			 group_autogroup(struct client_ctx *);
-void			 group_client_delete(struct client_ctx *);
 void			 group_cycle(struct screen_ctx *, int);
 void			 group_hidetoggle(struct screen_ctx *, int);
 void			 group_init(struct screen_ctx *);
@@ -442,7 +438,6 @@ struct menu  		*menu_filter(struct screen_ctx *, struct menu_q *,
 			     char *, char *, int,
 			     void (*)(struct menu_q *, struct menu_q *, char *),
 			     void (*)(struct menu *, int));
-void			 menu_init(struct screen_ctx *);
 void			 menuq_clear(struct menu_q *);
 
 int			 parse_config(const char *, struct conf *);
@@ -452,24 +447,12 @@ void			 conf_bindname(struct conf *, char *, char *);
 void			 conf_clear(struct conf *);
 void			 conf_client(struct client_ctx *);
 void			 conf_cmd_add(struct conf *, char *, char *);
-void			 conf_color(struct conf *, struct screen_ctx *);
-void			 conf_font(struct conf *, struct screen_ctx *);
-void			 conf_gap(struct conf *, struct screen_ctx *);
-void			 conf_grab(struct conf *, struct keybinding *);
-void			 conf_grab_mouse(struct client_ctx *);
+void			 conf_grab_kbd(Window);
+void			 conf_grab_mouse(Window);
 void			 conf_init(struct conf *);
 void			 conf_ignore(struct conf *, char *);
-void			 conf_mousebind(struct conf *, char *, char *);
-void			 conf_ungrab(struct conf *, struct keybinding *);
-
-int			 font_ascent(struct screen_ctx *);
-int			 font_descent(struct screen_ctx *);
-void			 font_draw(struct screen_ctx *, const char *, int,
-			     Drawable, int, int, int);
-u_int			 font_height(struct screen_ctx *);
-void			 font_init(struct screen_ctx *, const char *,
-			     const char **);
-int			 font_width(struct screen_ctx *, const char *, int);
+int			 conf_mousebind(struct conf *, char *, char *);
+void			 conf_screen(struct screen_ctx *);
 
 void			 xev_loop(void);
 
@@ -477,21 +460,21 @@ void			 xu_btn_grab(Window, int, u_int);
 void			 xu_btn_ungrab(Window, int, u_int);
 void			 xu_configure(struct client_ctx *);
 void			 xu_getatoms(void);
-unsigned long		 xu_getcolor(struct screen_ctx *, char *);
 int			 xu_getprop(Window, Atom, Atom, long, u_char **);
 int			 xu_get_wm_state(Window, int *);
 int			 xu_getstrprop(Window, Atom, char **);
-void			 xu_key_grab(Window, int, int);
-void			 xu_key_ungrab(Window, int, int);
+void			 xu_key_grab(Window, u_int, KeySym);
 void			 xu_ptr_getpos(Window, int *, int *);
-int			 xu_ptr_grab(Window, int, Cursor);
-int			 xu_ptr_regrab(int, Cursor);
+int			 xu_ptr_grab(Window, u_int, Cursor);
+int			 xu_ptr_regrab(u_int, Cursor);
 void			 xu_ptr_setpos(Window, int, int);
 void			 xu_ptr_ungrab(void);
-void			 xu_sendmsg(Window, Atom, long);
+void			 xu_sendmsg(Window, Atom, Atom);
 void			 xu_set_wm_state(Window win, int);
-void 			 xu_xorcolor(XRenderColor, XRenderColor,
-			     XRenderColor *);
+void			 xu_xft_draw(struct screen_ctx *, const char *,
+			     int, int, int);
+int			 xu_xft_width(XftFont *, const char *, int);
+void 			 xu_xorcolor(XftColor, XftColor, XftColor *);
 
 void			 xu_ewmh_net_supported(struct screen_ctx *);
 void			 xu_ewmh_net_supported_wm_check(struct screen_ctx *);
@@ -508,6 +491,11 @@ void			 xu_ewmh_net_desktop_names(struct screen_ctx *, char *,
 			     int);
 
 void			 xu_ewmh_net_wm_desktop(struct client_ctx *);
+Atom 			*xu_ewmh_get_net_wm_state(struct client_ctx *, int *);
+void 			 xu_ewmh_handle_net_wm_state_msg(struct client_ctx *,
+			     int, Atom , Atom);
+void 			 xu_ewmh_set_net_wm_state(struct client_ctx *);
+void 			 xu_ewmh_restore_net_wm_state(struct client_ctx *);
 
 void			 u_exec(char *);
 void			 u_spawn(char *);
@@ -542,6 +530,7 @@ enum {
 	WM_PROTOCOLS,
 	_MOTIF_WM_HINTS,
 	UTF8_STRING,
+	WM_CHANGE_STATE,
 	CWMH_NITEMS
 };
 enum {
@@ -559,8 +548,18 @@ enum {
 	_NET_WORKAREA,
 	_NET_WM_NAME,
 	_NET_WM_DESKTOP,
+	_NET_CLOSE_WINDOW,
+	_NET_WM_STATE,
+#define	_NET_WM_STATES_NITEMS	2
+	_NET_WM_STATE_MAXIMIZED_VERT,
+	_NET_WM_STATE_MAXIMIZED_HORZ,
 	EWMH_NITEMS
 };
+enum {
+	_NET_WM_STATE_REMOVE,
+	_NET_WM_STATE_ADD,
+	_NET_WM_STATE_TOGGLE
+};
 struct atom_ctx {
 	char	*name;
 	Atom	 atom;
diff --git a/client.c b/client.c
index c485b07..cfa253b 100644
--- a/client.c
+++ b/client.c
@@ -36,7 +36,7 @@ static struct client_ctx	*client_mruprev(struct client_ctx *);
 static void			 client_mtf(struct client_ctx *);
 static void			 client_none(struct screen_ctx *);
 static void			 client_placecalc(struct client_ctx *);
-static void			 client_update(struct client_ctx *);
+static void			 client_wm_protocols(struct client_ctx *);
 static void			 client_getmwmhints(struct client_ctx *);
 static int			 client_inbound(struct client_ctx *, int, int);
 
@@ -55,7 +55,7 @@ client_find(Window win)
 }
 
 struct client_ctx *
-client_new(Window win, struct screen_ctx *sc, int mapped)
+client_init(Window win, struct screen_ctx *sc, int mapped)
 {
 	struct client_ctx	*cc;
 	XClassHint		 xch;
@@ -132,7 +132,8 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
 
 	xu_ewmh_net_client_list(sc);
 
-	client_update(cc);
+	client_wm_protocols(cc);
+	xu_ewmh_restore_net_wm_state(cc);
 
 	if (mapped)
 		group_autogroup(cc);
@@ -149,13 +150,10 @@ client_delete(struct client_ctx *cc)
 	struct screen_ctx	*sc = cc->sc;
 	struct winname		*wn;
 
-	group_client_delete(cc);
-
 	XGrabServer(X_Dpy);
 	cc->state = WithdrawnState;
 	xu_set_wm_state(cc->win, cc->state);
 	XRemoveFromSaveSet(X_Dpy, cc->win);
-
 	XSync(X_Dpy, False);
 	XUngrabServer(X_Dpy);
 
@@ -164,6 +162,9 @@ client_delete(struct client_ctx *cc)
 
 	xu_ewmh_net_client_list(sc);
 
+	if (cc->group != NULL)
+		TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
+
 	if (cc == client_current())
 		client_none(sc);
 
@@ -212,7 +213,7 @@ client_setactive(struct client_ctx *cc, int fg)
 		XInstallColormap(X_Dpy, cc->colormap);
 		XSetInputFocus(X_Dpy, cc->win,
 		    RevertToPointerRoot, CurrentTime);
-		conf_grab_mouse(cc);
+		conf_grab_mouse(cc->win);
 		/*
 		 * If we're in the middle of alt-tabbing, don't change
 		 * the order please.
@@ -301,6 +302,7 @@ client_maximize(struct client_ctx *cc)
 
 resize:
 	client_resize(cc, 0);
+	xu_ewmh_set_net_wm_state(cc);
 }
 
 void
@@ -341,6 +343,7 @@ client_vmaximize(struct client_ctx *cc)
 
 resize:
 	client_resize(cc, 0);
+	xu_ewmh_set_net_wm_state(cc);
 }
 
 void
@@ -381,6 +384,7 @@ client_hmaximize(struct client_ctx *cc)
 
 resize:
 	client_resize(cc, 0);
+	xu_ewmh_set_net_wm_state(cc);
 }
 
 void
@@ -389,6 +393,7 @@ client_resize(struct client_ctx *cc, int reset)
 	if (reset) {
 		cc->flags &= ~CLIENT_MAXIMIZED;
 		cc->bwidth = Conf.bwidth;
+		xu_ewmh_set_net_wm_state(cc);
 	}
 
 	client_draw_border(cc);
@@ -480,46 +485,43 @@ client_draw_border(struct client_ctx *cc)
 	if (cc->active)
 		switch (cc->flags & CLIENT_HIGHLIGHT) {
 		case CLIENT_GROUP:
-			pixel = sc->color[CWM_COLOR_BORDER_GROUP];
+			pixel = sc->xftcolor[CWM_COLOR_BORDER_GROUP].pixel;
 			break;
 		case CLIENT_UNGROUP:
-			pixel = sc->color[CWM_COLOR_BORDER_UNGROUP];
+			pixel = sc->xftcolor[CWM_COLOR_BORDER_UNGROUP].pixel;
 			break;
 		default:
-			pixel = sc->color[CWM_COLOR_BORDER_ACTIVE];
+			pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE].pixel;
 			break;
 		}
 	else
-		pixel = sc->color[CWM_COLOR_BORDER_INACTIVE];
+		pixel = sc->xftcolor[CWM_COLOR_BORDER_INACTIVE].pixel;
 
 	XSetWindowBorderWidth(X_Dpy, cc->win, cc->bwidth);
 	XSetWindowBorder(X_Dpy, cc->win, pixel);
 }
 
 static void
-client_update(struct client_ctx *cc)
+client_wm_protocols(struct client_ctx *cc)
 {
 	Atom	*p;
-	int	 i;
-	long	 n;
-
-	if ((n = xu_getprop(cc->win, cwmh[WM_PROTOCOLS].atom,
-		 XA_ATOM, 20L, (u_char **)&p)) <= 0)
-		return;
-
-	for (i = 0; i < n; i++)
-		if (p[i] == cwmh[WM_DELETE_WINDOW].atom)
-			cc->xproto |= CLIENT_PROTO_DELETE;
-		else if (p[i] == cwmh[WM_TAKE_FOCUS].atom)
-			cc->xproto |= CLIENT_PROTO_TAKEFOCUS;
-
-	XFree(p);
+	int	 i, j;
+
+	if (XGetWMProtocols(X_Dpy, cc->win, &p, &j)) {
+		for (i = 0; i < j; i++) {
+			if (p[i] == cwmh[WM_DELETE_WINDOW].atom)
+				cc->xproto |= _WM_DELETE_WINDOW;
+			else if (p[i] == cwmh[WM_TAKE_FOCUS].atom)
+				cc->xproto |= _WM_TAKE_FOCUS;
+		}
+		XFree(p);
+	}
 }
 
 void
 client_send_delete(struct client_ctx *cc)
 {
-	if (cc->xproto & CLIENT_PROTO_DELETE)
+	if (cc->xproto & _WM_DELETE_WINDOW)
 		xu_sendmsg(cc->win,
 		    cwmh[WM_PROTOCOLS].atom, cwmh[WM_DELETE_WINDOW].atom);
 	else
diff --git a/conf.c b/conf.c
index 705c9e0..7c4f77d 100644
--- a/conf.c
+++ b/conf.c
@@ -84,39 +84,63 @@ conf_ignore(struct conf *c, char *val)
 	TAILQ_INSERT_TAIL(&c->ignoreq, wm, entry);
 }
 
-void
-conf_gap(struct conf *c, struct screen_ctx *sc)
-{
-	sc->gap = c->gap;
-}
-
-void
-conf_font(struct conf *c, struct screen_ctx *sc)
-{
-	font_init(sc, c->font, (const char **)c->menucolor);
-}
-
-static char *menu_color_binds[CWM_COLOR_MENU_MAX] = {
-	"black",	/* CWM_COLOR_MENU_FG */
-	"white",	/* CWM_COLOR_MENU_BG */
-	"black",	/* CWM_COLOR_MENU_FONT */
-	"",		/* CWM_COLOR_MENU_FONT_SEL */
-};
-
-static char *color_binds[CWM_COLOR_BORDER_MAX] = {
+static char *color_binds[CWM_COLOR_MAX] = {
 	"#CCCCCC",	/* CWM_COLOR_BORDER_ACTIVE */
 	"#666666",	/* CWM_COLOR_BORDER_INACTIVE */
 	"blue",		/* CWM_COLOR_BORDER_GROUP */
 	"red",		/* CWM_COLOR_BORDER_UNGROUP */
+	"black",	/* CWM_COLOR_MENU_FG */
+	"white",	/* CWM_COLOR_MENU_BG */
+	"black",	/* CWM_COLOR_MENU_FONT */
+	"",		/* CWM_COLOR_MENU_FONT_SEL */
 };
 
 void
-conf_color(struct conf *c, struct screen_ctx *sc)
+conf_screen(struct screen_ctx *sc)
 {
-	int	 i;
+	int			 i;
+	XftColor		 xc;
 
-	for (i = 0; i < CWM_COLOR_BORDER_MAX; i++)
-		sc->color[i] = xu_getcolor(sc, c->color[i]);
+	sc->gap = Conf.gap;
+
+	sc->xftfont = XftFontOpenName(X_Dpy, sc->which, Conf.font);
+	if (sc->xftfont == NULL)
+		errx(1, "XftFontOpenName");
+
+	for (i = 0; i < CWM_COLOR_MAX; i++) {
+		if (*Conf.color[i] == '\0')
+			break;
+		if (XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
+		    Conf.color[i], &xc)) {
+			sc->xftcolor[i] = xc;
+			XftColorFree(X_Dpy, sc->visual, sc->colormap, &xc);
+		} else {
+			warnx("XftColorAllocName: '%s'", Conf.color[i]);
+			XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
+			    color_binds[i], &sc->xftcolor[i]);
+		}
+	}
+	if (i == CWM_COLOR_MAX)
+		return;
+
+	xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG],
+		    sc->xftcolor[CWM_COLOR_MENU_FG], &xc);
+	xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT], xc, &xc);
+	if (!XftColorAllocValue(X_Dpy, sc->visual, sc->colormap,
+	    &xc.color, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL]))
+		warnx("XftColorAllocValue: '%s'", Conf.color[i]);
+
+	sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
+	    Conf.bwidth,
+	    sc->xftcolor[CWM_COLOR_MENU_FG].pixel,
+	    sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
+
+	sc->xftdraw = XftDrawCreate(X_Dpy, sc->menuwin,
+	    sc->visual, sc->colormap);
+	if (sc->xftdraw == NULL)
+		errx(1, "XftDrawCreate");
+
+	conf_grab_kbd(sc->rootwin);
 }
 
 static struct {
@@ -195,7 +219,7 @@ m_binds[] = {
 void
 conf_init(struct conf *c)
 {
-	int	i;
+	u_int	i;
 
 	bzero(c, sizeof(*c));
 
@@ -218,9 +242,6 @@ conf_init(struct conf *c)
 	for (i = 0; i < nitems(color_binds); i++)
 		c->color[i] = xstrdup(color_binds[i]);
 
-	for (i = 0; i < nitems(menu_color_binds); i++)
-		c->menucolor[i] = xstrdup(menu_color_binds[i]);
-
 	/* Default term/lock */
 	(void)strlcpy(c->termpath, "xterm", sizeof(c->termpath));
 	(void)strlcpy(c->lockpath, "xlock", sizeof(c->lockpath));
@@ -268,7 +289,7 @@ conf_clear(struct conf *c)
 		free(mb);
 	}
 
-	for (i = 0; i < CWM_COLOR_BORDER_MAX; i++)
+	for (i = 0; i < CWM_COLOR_MAX; i++)
 		free(c->color[i]);
 
 	free(c->font);
@@ -413,37 +434,6 @@ static struct {
 	    {.i = CWM_TILE_VERT } },
 };
 
-/*
- * The following two functions are used when grabbing and ungrabbing keys for
- * bindings
- */
-
-/*
- * Grab key combination on all screens and add to the global queue
- */
-void
-conf_grab(struct conf *c, struct keybinding *kb)
-{
-	extern struct screen_ctx_q	 Screenq;
-	struct screen_ctx		*sc;
-
-	TAILQ_FOREACH(sc, &Screenq, entry)
-		xu_key_grab(sc->rootwin, kb->modmask, kb->keysym);
-}
-
-/*
- * Ungrab key combination from all screens and remove from global queue
- */
-void
-conf_ungrab(struct conf *c, struct keybinding *kb)
-{
-	extern struct screen_ctx_q	 Screenq;
-	struct screen_ctx		*sc;
-
-	TAILQ_FOREACH(sc, &Screenq, entry)
-		xu_key_ungrab(sc->rootwin, kb->modmask, kb->keysym);
-}
-
 static struct {
 	char	chr;
 	int	mask;
@@ -459,7 +449,7 @@ conf_bindname(struct conf *c, char *name, char *binding)
 {
 	struct keybinding	*current_binding;
 	char			*substring, *tmp;
-	int			 i;
+	u_int			 i;
 
 	current_binding = xcalloc(1, sizeof(*current_binding));
 
@@ -507,7 +497,6 @@ conf_bindname(struct conf *c, char *name, char *binding)
 		current_binding->flags = name_to_kbfunc[i].flags;
 		current_binding->argument = name_to_kbfunc[i].argument;
 		current_binding->argtype |= ARG_INT;
-		conf_grab(c, current_binding);
 		TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
 		return;
 	}
@@ -516,7 +505,6 @@ conf_bindname(struct conf *c, char *name, char *binding)
 	current_binding->flags = 0;
 	current_binding->argument.c = xstrdup(binding);
 	current_binding->argtype |= ARG_CHAR;
-	conf_grab(c, current_binding);
 	TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
 }
 
@@ -532,7 +520,6 @@ conf_unbind(struct conf *c, struct keybinding *unbind)
 		if ((key->keycode != 0 && key->keysym == NoSymbol &&
 		    key->keycode == unbind->keycode) ||
 		    key->keysym == unbind->keysym) {
-			conf_ungrab(c, key);
 			TAILQ_REMOVE(&c->keybindingq, key, entry);
 			if (key->argtype & ARG_CHAR)
 				free(key->argument.c);
@@ -558,13 +545,16 @@ static struct {
 	{ "menu_cmd", mousefunc_menu_cmd, MOUSEBIND_CTX_ROOT },
 };
 
-void
+static unsigned int mouse_btns[] = { Button1, Button2, Button3 };
+
+int
 conf_mousebind(struct conf *c, char *name, char *binding)
 {
 	struct mousebinding	*current_binding;
 	char			*substring, *tmp;
+	u_int			 button;
 	const char		*errstr;
-	int			 i;
+	u_int			 i;
 
 	current_binding = xcalloc(1, sizeof(*current_binding));
 
@@ -581,16 +571,27 @@ conf_mousebind(struct conf *c, char *name, char *binding)
 	} else
 		substring = name;
 
-	current_binding->button = strtonum(substring, 1, 3, &errstr);
+	button = strtonum(substring, 1, 3, &errstr);
 	if (errstr)
-		warnx("number of buttons is %s: %s", errstr, substring);
+		warnx("button number is %s: %s", errstr, substring);
+
+	for (i = 0; i < nitems(mouse_btns); i++) {
+		if (button == mouse_btns[i]) {
+			current_binding->button = button;
+			break;
+		}
+	}
+	if (!current_binding->button || errstr) {
+		free(current_binding);
+		return (0);
+	}
 
 	/* We now have the correct binding, remove duplicates. */
 	conf_mouseunbind(c, current_binding);
 
 	if (strcmp("unmap", binding) == 0) {
 		free(current_binding);
-		return;
+		return (1);
 	}
 
 	for (i = 0; i < nitems(name_to_mousefunc); i++) {
@@ -600,8 +601,10 @@ conf_mousebind(struct conf *c, char *name, char *binding)
 		current_binding->context = name_to_mousefunc[i].context;
 		current_binding->callback = name_to_mousefunc[i].handler;
 		TAILQ_INSERT_TAIL(&c->mousebindingq, current_binding, entry);
-		return;
+		return (1);
 	}
+
+	return (0);
 }
 
 static void
@@ -620,33 +623,26 @@ conf_mouseunbind(struct conf *c, struct mousebinding *unbind)
 	}
 }
 
-/*
- * Grab the mouse buttons that we need for bindings for this client
- */
 void
-conf_grab_mouse(struct client_ctx *cc)
+conf_grab_mouse(Window win)
 {
 	struct mousebinding	*mb;
-	int			 button;
 
 	TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
 		if (mb->context != MOUSEBIND_CTX_WIN)
 			continue;
-
-		switch(mb->button) {
-		case 1:
-			button = Button1;
-			break;
-		case 2:
-			button = Button2;
-			break;
-		case 3:
-			button = Button3;
-			break;
-		default:
-			warnx("strange button in mousebinding\n");
-			continue;
-		}
-		xu_btn_grab(cc->win, mb->modmask, button);
+		xu_btn_grab(win, mb->modmask, mb->button);
 	}
 }
+
+void
+conf_grab_kbd(Window win)
+{
+	struct keybinding	*kb;
+
+	XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
+
+	TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
+		xu_key_grab(win, kb->modmask, kb->keysym);
+}
+
diff --git a/font.c b/font.c
deleted file mode 100644
index 648450e..0000000
--- a/font.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * calmwm - the calm window manager
- *
- * Copyright (c) 2005 Marius Eriksen <marius@monkey.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * $OpenBSD$
- */
-
-#include <sys/param.h>
-#include "queue.h"
-
-#include <err.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "calmwm.h"
-
-int
-font_ascent(struct screen_ctx *sc)
-{
-	return (sc->xftfont->ascent);
-}
-
-int
-font_descent(struct screen_ctx *sc)
-{
-	return (sc->xftfont->descent);
-}
-
-u_int
-font_height(struct screen_ctx *sc)
-{
-	return (sc->xftfont->height + 1);
-}
-
-void
-font_init(struct screen_ctx *sc, const char *name, const char **color)
-{
-	int		 i;
-	XRenderColor	 c;
-
-	sc->xftdraw = XftDrawCreate(X_Dpy, sc->rootwin,
-	    sc->visual, sc->colormap);
-	if (sc->xftdraw == NULL)
-		errx(1, "XftDrawCreate");
-
-	sc->xftfont = XftFontOpenName(X_Dpy, sc->which, name);
-	if (sc->xftfont == NULL)
-		errx(1, "XftFontOpenName");
-
-	for (i = 0; i < CWM_COLOR_MENU_MAX; i++) {
-		if (*color[i] == '\0')
-			break;
-		if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
-			color[i], &sc->xftcolor[i]))
-			errx(1, "XftColorAllocName");
-	}
-	if (i == CWM_COLOR_MENU_MAX)
-		return;
-
-	xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_BG].color,
-		    sc->xftcolor[CWM_COLOR_MENU_FG].color, &c);
-	xu_xorcolor(sc->xftcolor[CWM_COLOR_MENU_FONT].color, c, &c);
-	if (!XftColorAllocValue(X_Dpy, sc->visual, sc->colormap,
-		&c, &sc->xftcolor[CWM_COLOR_MENU_FONT_SEL]))
-		errx(1, "XftColorAllocValue");
-}
-
-int
-font_width(struct screen_ctx *sc, const char *text, int len)
-{
-	XGlyphInfo	 extents;
-
-	XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
-	    len, &extents);
-
-	return (extents.xOff);
-}
-
-void
-font_draw(struct screen_ctx *sc, const char *text, int len,
-    Drawable d, int active, int x, int y)
-{
-	int	 color;
-
-	color = active ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
-	XftDrawChange(sc->xftdraw, d);
-	XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor[color], sc->xftfont, x, y,
-	    (const FcChar8*)text, len);
-}
diff --git a/group.c b/group.c
index 48dfc68..30bc5d5 100644
--- a/group.c
+++ b/group.c
@@ -308,17 +308,6 @@ group_cycle(struct screen_ctx *sc, int flags)
 		group_setactive(sc, showgroup->shortcut);
 }
 
-/* called when a client is deleted */
-void
-group_client_delete(struct client_ctx *cc)
-{
-	if (cc->group == NULL)
-		return;
-
-	TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
-	cc->group = NULL; /* he he */
-}
-
 void
 group_menu(XButtonEvent *e)
 {
diff --git a/kbfunc.c b/kbfunc.c
index bec81a4..37b8af7 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -88,17 +88,16 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
 	}
 	switch (flags & TYPEMASK) {
 	case CWM_MOVE:
-		cc->geom.y += my;
-		if (cc->geom.y + cc->geom.h < 0)
-			cc->geom.y = -cc->geom.h;
-		if (cc->geom.y > sc->view.h - 1)
-			cc->geom.y = sc->view.h - 1;
-
 		cc->geom.x += mx;
 		if (cc->geom.x + cc->geom.w < 0)
 			cc->geom.x = -cc->geom.w;
 		if (cc->geom.x > sc->view.w - 1)
 			cc->geom.x = sc->view.w - 1;
+		cc->geom.y += my;
+		if (cc->geom.y + cc->geom.h < 0)
+			cc->geom.y = -cc->geom.h;
+		if (cc->geom.y > sc->view.h - 1)
+			cc->geom.y = sc->view.h - 1;
 
 		cc->geom.x += client_snapcalc(cc->geom.x,
 		    cc->geom.x + cc->geom.w + (cc->bwidth * 2),
@@ -109,15 +108,15 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
 
 		client_move(cc);
 		xu_ptr_getpos(cc->win, &x, &y);
-		cc->ptr.y = y + my;
 		cc->ptr.x = x + mx;
+		cc->ptr.y = y + my;
 		client_ptrwarp(cc);
 		break;
 	case CWM_RESIZE:
-		if ((cc->geom.h += my) < 1)
-			cc->geom.h = 1;
 		if ((cc->geom.w += mx) < 1)
 			cc->geom.w = 1;
+		if ((cc->geom.h += my) < 1)
+			cc->geom.h = 1;
 		client_resize(cc, 1);
 
 		/* Make sure the pointer stays within the window. */
diff --git a/menu.c b/menu.c
index b467ffb..d610945 100644
--- a/menu.c
+++ b/menu.c
@@ -42,7 +42,7 @@ enum ctltype {
 };
 
 struct menu_ctx {
-	struct screen_ctx 	*sc;
+	struct screen_ctx	*sc;
 	char			 searchstr[MENU_MAXENTRY + 1];
 	char			 dispstr[MENU_MAXENTRY*2 + 1];
 	char			 promptstr[MENU_MAXENTRY + 1];
@@ -65,27 +65,16 @@ struct menu_ctx {
 static struct menu	*menu_handle_key(XEvent *, struct menu_ctx *,
 			     struct menu_q *, struct menu_q *);
 static void		 menu_handle_move(XEvent *, struct menu_ctx *,
-			     struct screen_ctx *, struct menu_q *);
+			     struct menu_q *);
 static struct menu	*menu_handle_release(XEvent *, struct menu_ctx *,
-			     struct screen_ctx *, struct menu_q *);
-static void		 menu_draw(struct screen_ctx *, struct menu_ctx *,
-			     struct menu_q *, struct menu_q *);
-static void 		 menu_draw_entry(struct screen_ctx *, struct menu_ctx *,
-			     struct menu_q *, int, int);
-static int		 menu_calc_entry(struct screen_ctx *, struct menu_ctx *,
+			     struct menu_q *);
+static void		 menu_draw(struct menu_ctx *, struct menu_q *,
+			     struct menu_q *);
+static void 		 menu_draw_entry(struct menu_ctx *, struct menu_q *,
 			     int, int);
+static int		 menu_calc_entry(struct menu_ctx *, int, int);
 static struct menu 	*menu_complete_path(struct menu_ctx *);
-static int		 menu_keycode(XKeyEvent *, enum ctltype *,
-			     char *);
-
-void
-menu_init(struct screen_ctx *sc)
-{
-	sc->menuwin = XCreateSimpleWindow(X_Dpy, sc->rootwin, 0, 0, 1, 1,
-	    Conf.bwidth,
-	    sc->xftcolor[CWM_COLOR_MENU_FG].pixel,
-	    sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
-}
+static int		 menu_keycode(XKeyEvent *, enum ctltype *, char *);
 
 struct menu *
 menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
@@ -112,18 +101,13 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
 
 	mc.sc = sc;
 	mc.flags = flags;
-	if (prompt == NULL) {
+	if (prompt != NULL) {
+		evmask = MENUMASK | KEYMASK; /* accept keys as well */
+		(void)strlcpy(mc.promptstr, prompt, sizeof(mc.promptstr));
+		mc.hasprompt = 1;
+	} else {
 		evmask = MENUMASK;
-		mc.promptstr[0] = '\0';
 		mc.list = 1;
-	} else {
-		evmask = MENUMASK | KEYMASK; /* only accept keys if prompt */
-		(void)snprintf(mc.promptstr, sizeof(mc.promptstr), "%s%s",
-		    prompt, PROMPT_SCHAR);
-		(void)snprintf(mc.dispstr, sizeof(mc.dispstr), "%s%s%s",
-		    mc.promptstr, mc.searchstr, PROMPT_ECHAR);
-		mc.width = font_width(sc, mc.dispstr, strlen(mc.dispstr));
-		mc.hasprompt = 1;
 	}
 
 	if (initial != NULL)
@@ -135,8 +119,6 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
 	mc.print = print;
 	mc.entry = mc.prev = -1;
 
-	XMoveResizeWindow(X_Dpy, sc->menuwin, mc.x, mc.y, mc.width,
-	    font_height(sc));
 	XSelectInput(X_Dpy, sc->menuwin, evmask);
 	XMapRaised(X_Dpy, sc->menuwin);
 
@@ -164,13 +146,13 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
 				goto out;
 			/* FALLTHROUGH */
 		case Expose:
-			menu_draw(sc, &mc, menuq, &resultq);
+			menu_draw(&mc, menuq, &resultq);
 			break;
 		case MotionNotify:
-			menu_handle_move(&e, &mc, sc, &resultq);
+			menu_handle_move(&e, &mc, &resultq);
 			break;
 		case ButtonRelease:
-			if ((mi = menu_handle_release(&e, &mc, sc, &resultq))
+			if ((mi = menu_handle_release(&e, &mc, &resultq))
 			    != NULL)
 				goto out;
 			break;
@@ -192,6 +174,7 @@ out:
 		xu_ptr_setpos(sc->rootwin, xsave, ysave);
 	xu_ptr_ungrab();
 
+	XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, 1, 1);
 	XUnmapWindow(X_Dpy, sc->menuwin);
 	XUngrabKeyboard(X_Dpy, CurrentTime);
 
@@ -203,7 +186,7 @@ menu_complete_path(struct menu_ctx *mc)
 {
 	struct menu		*mi, *mr;
 	struct menu_q		 menuq;
-	char *path = NULL;
+	char			*path = NULL;
 
 	path = xcalloc(1, sizeof(mr->text));
 	mr = xcalloc(1, sizeof(*mr));
@@ -348,9 +331,9 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
 }
 
 static void
-menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
-    struct menu_q *resultq)
+menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
 {
+	struct screen_ctx	*sc = mc->sc;
 	struct menu		*mi;
 	struct geom		 xine;
 	int			 n, xsave, ysave;
@@ -371,10 +354,11 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
 	mc->width = 0;
 	mc->height = 0;
 	if (mc->hasprompt) {
-		(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s",
-		    mc->promptstr, mc->searchstr, PROMPT_ECHAR);
-		mc->width = font_width(sc, mc->dispstr, strlen(mc->dispstr));
-		mc->height = font_height(sc);
+		(void)snprintf(mc->dispstr, sizeof(mc->dispstr), "%s%s%s%s",
+		    mc->promptstr, PROMPT_SCHAR, mc->searchstr, PROMPT_ECHAR);
+		mc->width = xu_xft_width(sc->xftfont, mc->dispstr,
+		    strlen(mc->dispstr));
+		mc->height = sc->xftfont->height + 1;
 		mc->num = 1;
 	}
 
@@ -389,9 +373,9 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
 			text = mi->text;
 		}
 
-		mc->width = MAX(mc->width, font_width(sc, text,
+		mc->width = MAX(mc->width, xu_xft_width(sc->xftfont, text,
 		    MIN(strlen(text), MENU_MAXENTRY)));
-		mc->height += font_height(sc);
+		mc->height += sc->xftfont->height + 1;
 		mc->num++;
 	}
 
@@ -424,8 +408,8 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
 	    mc->width, mc->height);
 
 	if (mc->hasprompt) {
-		font_draw(sc, mc->dispstr, strlen(mc->dispstr), sc->menuwin, 0,
-		    0, font_ascent(sc));
+		xu_xft_draw(sc, mc->dispstr, CWM_COLOR_MENU_FONT,
+		    0, sc->xftfont->ascent);
 		n = 1;
 	} else
 		n = 0;
@@ -433,29 +417,29 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
 	TAILQ_FOREACH(mi, resultq, resultentry) {
 		char *text = mi->print[0] != '\0' ?
 		    mi->print : mi->text;
-		int y = n * font_height(sc) + font_ascent(sc) + 1;
+		int y = n * (sc->xftfont->height + 1) + sc->xftfont->ascent + 1;
 
 		/* Stop drawing when menu doesn't fit inside the screen. */
 		if (mc->y + y > xine.h)
 			break;
 
-		font_draw(sc, text, MIN(strlen(text), MENU_MAXENTRY),
-		    sc->menuwin, 0, 0, y);
+		xu_xft_draw(sc, text, CWM_COLOR_MENU_FONT, 0, y);
 		n++;
 	}
 	if (mc->hasprompt && n > 1 && (mc->searchstr[0] != '\0')) {
 		mc->entry = 1;
-		menu_draw_entry(sc, mc, resultq, mc->entry, 1);
+		menu_draw_entry(mc, resultq, mc->entry, 1);
 	}
 }
 
 static void
-menu_draw_entry(struct screen_ctx *sc, struct menu_ctx *mc,
-    struct menu_q *resultq, int entry, int active)
+menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
+    int entry, int active)
 {
-	struct menu	*mi;
-	char 		*text;
-	int		 color, i = 0;
+	struct screen_ctx	*sc = mc->sc;
+	struct menu		*mi;
+	char 			*text;
+	int			 color, i = 0;
 
 	if (mc->hasprompt)
 		i = 1;
@@ -467,44 +451,43 @@ menu_draw_entry(struct screen_ctx *sc, struct menu_ctx *mc,
 		return;
 
 	color = active ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
-	text = mi->print[0] != '\0' ?  mi->print : mi->text;
+	text = mi->print[0] != '\0' ? mi->print : mi->text;
 	XftDrawRect(sc->xftdraw, &sc->xftcolor[color], 0,
-	    font_height(sc) * entry, mc->width,
-	    font_height(sc) + font_descent(sc));
-	font_draw(sc, text, strlen(text), sc->menuwin, active,
-	    0, font_height(sc) * entry + font_ascent(sc) + 1);
+	    (sc->xftfont->height + 1) * entry, mc->width,
+	    (sc->xftfont->height + 1) + sc->xftfont->descent);
+	color = active ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
+	xu_xft_draw(sc, text, color,
+	    0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1);
 }
 
 static void
-menu_handle_move(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
-    struct menu_q *resultq)
+menu_handle_move(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
 {
 	mc->prev = mc->entry;
-	mc->entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
+	mc->entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y);
 
 	if (mc->prev == mc->entry)
 		return;
 
 	if (mc->prev != -1)
-		menu_draw_entry(sc, mc, resultq, mc->prev, 0);
+		menu_draw_entry(mc, resultq, mc->prev, 0);
 	if (mc->entry != -1) {
 		(void)xu_ptr_regrab(MENUGRABMASK, Cursor_normal);
-		menu_draw_entry(sc, mc, resultq, mc->entry, 1);
+		menu_draw_entry(mc, resultq, mc->entry, 1);
 	} else
 		(void)xu_ptr_regrab(MENUGRABMASK, Cursor_default);
 
 	if (mc->hasprompt)
-		menu_draw_entry(sc, mc, resultq, 1, 1);
+		menu_draw_entry(mc, resultq, 1, 1);
 }
 
 static struct menu *
-menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
-    struct menu_q *resultq)
+menu_handle_release(XEvent *e, struct menu_ctx *mc, struct menu_q *resultq)
 {
-	struct menu	*mi;
-	int		 entry, i = 0;
+	struct menu		*mi;
+	int			 entry, i = 0;
 
-	entry = menu_calc_entry(sc, mc, e->xbutton.x, e->xbutton.y);
+	entry = menu_calc_entry(mc, e->xbutton.x, e->xbutton.y);
 
 	if (mc->hasprompt)
 		i = 1;
@@ -521,15 +504,17 @@ menu_handle_release(XEvent *e, struct menu_ctx *mc, struct screen_ctx *sc,
 }
 
 static int
-menu_calc_entry(struct screen_ctx *sc, struct menu_ctx *mc, int x, int y)
+menu_calc_entry(struct menu_ctx *mc, int x, int y)
 {
-	int	 entry;
+	struct screen_ctx	*sc = mc->sc;
+	int			 entry;
 
-	entry = y / font_height(sc);
+	entry = y / (sc->xftfont->height + 1);
 
 	/* in bounds? */
 	if (x < 0 || x > mc->width || y < 0 ||
-	    y > font_height(sc) * mc->num || entry < 0 || entry >= mc->num)
+	    y > (sc->xftfont->height + 1) * mc->num ||
+	    entry < 0 || entry >= mc->num)
 		entry = -1;
 
 	if (mc->hasprompt && entry == 0)
diff --git a/mousefunc.c b/mousefunc.c
index dcf1073..9305633 100644
--- a/mousefunc.c
+++ b/mousefunc.c
@@ -31,14 +31,12 @@
 
 #include "calmwm.h"
 
-static int	mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
+static void	mousefunc_sweep_calc(struct client_ctx *, int, int, int, int);
 static void	mousefunc_sweep_draw(struct client_ctx *);
 
-static int
+static void
 mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my)
 {
-	int	 width = cc->geom.w, height = cc->geom.h;
-
 	cc->geom.w = abs(x - mx) - cc->bwidth;
 	cc->geom.h = abs(y - my) - cc->bwidth;
 
@@ -46,32 +44,27 @@ mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my)
 
 	cc->geom.x = x <= mx ? x : x - cc->geom.w;
 	cc->geom.y = y <= my ? y : y - cc->geom.h;
-
-	return (width != cc->geom.w || height != cc->geom.h);
 }
 
 static void
 mousefunc_sweep_draw(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
-	char			 asize[10]; /* fits "nnnnxnnnn\0" */
-	int			 width, width_size, width_name;
+	char			 asize[14]; /* fits " nnnn x nnnn \0" */
 
-	(void)snprintf(asize, sizeof(asize), "%dx%d",
+	(void)snprintf(asize, sizeof(asize), " %4d x %-4d ",
 	    (cc->geom.w - cc->hint.basew) / cc->hint.incw,
 	    (cc->geom.h - cc->hint.baseh) / cc->hint.inch);
-	width_size = font_width(sc, asize, strlen(asize)) + 4;
-	width_name = font_width(sc, cc->name, strlen(cc->name)) + 4;
-	width = MAX(width_size, width_name);
 
 	XReparentWindow(X_Dpy, sc->menuwin, cc->win, 0, 0);
-	XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0, width, font_height(sc) * 2);
+	XMoveResizeWindow(X_Dpy, sc->menuwin, 0, 0,
+	    xu_xft_width(sc->xftfont, asize, strlen(asize)),
+	    sc->xftfont->height);
 	XMapWindow(X_Dpy, sc->menuwin);
 	XClearWindow(X_Dpy, sc->menuwin);
-	font_draw(sc, cc->name, strlen(cc->name), sc->menuwin, 0,
-	    2, font_ascent(sc) + 1);
-	font_draw(sc, asize, strlen(asize), sc->menuwin, 0,
-	    width / 2 - width_size / 2, font_height(sc) + font_ascent(sc) + 1);
+
+	xu_xft_draw(sc, asize, CWM_COLOR_MENU_FONT,
+	    0, sc->xftfont->ascent + 1);
 }
 
 void
@@ -102,15 +95,14 @@ mousefunc_window_resize(struct client_ctx *cc, void *arg)
 			client_draw_border(cc);
 			break;
 		case MotionNotify:
-			if (mousefunc_sweep_calc(cc, x, y,
-			    ev.xmotion.x_root, ev.xmotion.y_root))
-				/* Recompute window output */
-				mousefunc_sweep_draw(cc);
+			mousefunc_sweep_calc(cc, x, y,
+			    ev.xmotion.x_root, ev.xmotion.y_root);
 
 			/* don't resize more than 60 times / second */
 			if ((ev.xmotion.time - ltime) > (1000 / 60)) {
 				ltime = ev.xmotion.time;
 				client_resize(cc, 1);
+				mousefunc_sweep_draw(cc);
 			}
 			break;
 		case ButtonRelease:
diff --git a/parse.y b/parse.y
index 376bc2b..9e93749 100644
--- a/parse.y
+++ b/parse.y
@@ -44,18 +44,17 @@ static struct file {
 	char			*name;
 	int			 lineno;
 	int			 errors;
-} *file;
-
-struct file		*pushfile(const char *);
-int			 popfile(void);
-int			 yyparse(void);
-int			 yylex(void);
-int			 yyerror(const char *, ...);
-int			 kw_cmp(const void *, const void *);
-int			 lookup(char *);
-int			 lgetc(int);
-int			 lungetc(int);
-int			 findeol(void);
+} *file, *topfile;
+struct file	*pushfile(const char *);
+int		 popfile(void);
+int		 yyparse(void);
+int		 yylex(void);
+int		 yyerror(const char *, ...);
+int		 kw_cmp(const void *, const void *);
+int		 lookup(char *);
+int		 lgetc(int);
+int		 lungetc(int);
+int		 findeol(void);
 
 static struct conf	*conf;
 
@@ -119,12 +118,24 @@ main		: FONTNAME STRING		{
 				conf->flags |= CONF_STICKY_GROUPS;
 		}
 		| BORDERWIDTH NUMBER {
+			if ($2 < 0) {
+				yyerror("invalid borderwidth: %d", $2);
+				YYERROR;
+			}
 			conf->bwidth = $2;
 		}
 		| MOVEAMOUNT NUMBER {
+			if ($2 < 0) {
+				yyerror("invalid movemount: %d", $2);
+				YYERROR;
+			}
 			conf->mamount = $2;
 		}
 		| SNAPDIST NUMBER {
+			if ($2 < 0) {
+				yyerror("invalid snapdist: %d", $2);
+				YYERROR;
+			}
 			conf->snapdist = $2;
 		}
 		| COMMAND STRING string		{
@@ -135,10 +146,9 @@ main		: FONTNAME STRING		{
 		| AUTOGROUP NUMBER STRING	{
 			if ($2 < 0 || $2 > 9) {
 				free($3);
-				yyerror("autogroup number out of range: %d", $2);
+				yyerror("invalid autogroup: %d", $2);
 				YYERROR;
 			}
-
 			conf_autogroup(conf, $2, $3);
 			free($3);
 		}
@@ -152,13 +162,23 @@ main		: FONTNAME STRING		{
 			free($3);
 		}
 		| GAP NUMBER NUMBER NUMBER NUMBER {
+			if ($2 < 0 || $3 < 0 || $4 < 0 || $5 < 0) {
+				yyerror("invalid gap: %d %d %d %d",
+				    $2, $3, $4, $5);
+				YYERROR;
+			}
 			conf->gap.top = $2;
 			conf->gap.bottom = $3;
 			conf->gap.left = $4;
 			conf->gap.right = $5;
 		}
 		| MOUSEBIND STRING string	{
-			conf_mousebind(conf, $2, $3);
+			if (!conf_mousebind(conf, $2, $3)) {
+				yyerror("invalid mousebind: %s %s", $2, $3);
+				free($2);
+				free($3);
+				YYERROR;
+			}
 			free($2);
 			free($3);
 		}
@@ -184,20 +204,20 @@ colors		: ACTIVEBORDER STRING {
 			conf->color[CWM_COLOR_BORDER_UNGROUP] = $2;
 		}
 		| MENUBG STRING {
-			free(conf->menucolor[CWM_COLOR_MENU_BG]);
-			conf->menucolor[CWM_COLOR_MENU_BG] = $2;
+			free(conf->color[CWM_COLOR_MENU_BG]);
+			conf->color[CWM_COLOR_MENU_BG] = $2;
 		}
 		| MENUFG STRING {
-			free(conf->menucolor[CWM_COLOR_MENU_FG]);
-			conf->menucolor[CWM_COLOR_MENU_FG] = $2;
+			free(conf->color[CWM_COLOR_MENU_FG]);
+			conf->color[CWM_COLOR_MENU_FG] = $2;
 		}
 		| FONTCOLOR STRING {
-			free(conf->menucolor[CWM_COLOR_MENU_FONT]);
-			conf->menucolor[CWM_COLOR_MENU_FONT] = $2;
+			free(conf->color[CWM_COLOR_MENU_FONT]);
+			conf->color[CWM_COLOR_MENU_FONT] = $2;
 		}
 		| FONTSELCOLOR STRING {
-			free(conf->menucolor[CWM_COLOR_MENU_FONT_SEL]);
-			conf->menucolor[CWM_COLOR_MENU_FONT_SEL] = $2;
+			free(conf->color[CWM_COLOR_MENU_FONT_SEL]);
+			conf->color[CWM_COLOR_MENU_FONT_SEL] = $2;
 		}
 		;
 %%
@@ -210,7 +230,7 @@ struct keywords {
 int
 yyerror(const char *fmt, ...)
 {
-	va_list ap;
+	va_list		 ap;
 
 	file->errors++;
 	va_start(ap, fmt);
@@ -294,8 +314,9 @@ lgetc(int quotec)
 
 	if (quotec) {
 		if ((c = getc(file->stream)) == EOF) {
-			yyerror("reached end of file while parsing quoted string");
-			if (popfile() == EOF)
+			yyerror("reached end of file while parsing "
+			    "quoted string");
+			if (file == topfile || popfile() == EOF)
 				return (EOF);
 			return (quotec);
 		}
@@ -313,7 +334,7 @@ lgetc(int quotec)
 	}
 
 	while (c == EOF) {
-		if (popfile() == EOF)
+		if (file == topfile || popfile() == EOF)
 			return (EOF);
 		c = getc(file->stream);
 	}
@@ -342,11 +363,13 @@ findeol(void)
 	int	c;
 
 	parsebuf = NULL;
-	pushback_index = 0;
 
 	/* skip to either EOF or the first real EOL */
 	while (1) {
-		c = lgetc(0);
+		if (pushback_index)
+			c = pushback_buffer[--pushback_index];
+		else
+			c = lgetc(0);
 		if (c == '\n') {
 			file->lineno++;
 			break;
@@ -447,9 +470,10 @@ nodigits:
 #define allowed_in_string(x) \
 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
 	x != '{' && x != '}' && x != '<' && x != '>' && \
-	x != '!' && x != '=' && x != '#' && x != ','))
+	x != '!' && x != '=' && x != '/' && x != '#' && \
+	x != ','))
 
-	if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') {
+	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
 		do {
 			*p++ = c;
 			if ((unsigned)(p-buf) >= sizeof(buf)) {
@@ -481,6 +505,7 @@ pushfile(const char *name)
 	nfile->name = xstrdup(name);
 
 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+		warn("%s", nfile->name);
 		free(nfile->name);
 		free(nfile);
 		return (NULL);
@@ -495,16 +520,15 @@ popfile(void)
 {
 	struct file	*prev;
 
-	if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
+	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
 		prev->errors += file->errors;
-		TAILQ_REMOVE(&files, file, entry);
-		fclose(file->stream);
-		free(file->name);
-		free(file);
-		file = prev;
-		return (0);
-	}
-	return (EOF);
+
+	TAILQ_REMOVE(&files, file, entry);
+	fclose(file->stream);
+	free(file->name);
+	free(file);
+	file = prev;
+	return (file ? 0 : EOF);
 }
 
 int
@@ -518,12 +542,12 @@ parse_config(const char *filename, struct conf *xconf)
 		free(conf);
 		return (-1);
 	}
+	topfile = file;
 
 	conf_init(conf);
 
 	yyparse();
 	errors = file->errors;
-	file->errors = 0;
 	popfile();
 
 	if (errors) {
@@ -575,12 +599,9 @@ parse_config(const char *filename, struct conf *xconf)
 		(void)strlcpy(xconf->lockpath, conf->lockpath,
 		    sizeof(xconf->lockpath));
 
-		for (i = 0; i < CWM_COLOR_BORDER_MAX; i++)
+		for (i = 0; i < CWM_COLOR_MAX; i++)
 			xconf->color[i] = conf->color[i];
 
-		for (i = 0; i < CWM_COLOR_MENU_MAX; i++)
-			xconf->menucolor[i] = conf->menucolor[i];
-
 		xconf->font = conf->font;
 	}
 
diff --git a/screen.c b/screen.c
index e856336..14e7608 100644
--- a/screen.c
+++ b/screen.c
@@ -49,16 +49,13 @@ screen_init(int which)
 	xu_ewmh_net_supported(sc);
 	xu_ewmh_net_supported_wm_check(sc);
 
-	conf_gap(&Conf, sc);
-	conf_color(&Conf, sc);
-	conf_font(&Conf, sc);
+	conf_screen(sc);
 
 	screen_update_geometry(sc);
 
 	TAILQ_INIT(&sc->mruq);
 
 	group_init(sc);
-	menu_init(sc);
 
 	rootattr.cursor = Cursor_normal;
 	rootattr.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|
@@ -76,7 +73,7 @@ screen_init(int which)
 		if (winattr.override_redirect ||
 		    winattr.map_state != IsViewable)
 			continue;
-		(void)client_new(wins[i], sc, winattr.map_state != IsUnmapped);
+		(void)client_init(wins[i], sc, winattr.map_state != IsUnmapped);
 	}
 	XFree(wins);
 
diff --git a/xevents.c b/xevents.c
index da7542b..9c42737 100644
--- a/xevents.c
+++ b/xevents.c
@@ -86,7 +86,7 @@ xev_handle_maprequest(XEvent *ee)
 
 	if ((cc = client_find(e->window)) == NULL) {
 		XGetWindowAttributes(X_Dpy, e->window, &xattr);
-		cc = client_new(e->window, screen_fromroot(xattr.root), 1);
+		cc = client_init(e->window, screen_fromroot(xattr.root), 1);
 	}
 
 	if ((cc->flags & CLIENT_IGNORE) == 0)
@@ -203,13 +203,12 @@ xev_handle_propertynotify(XEvent *ee)
 			break;
 		}
 	} else {
-		TAILQ_FOREACH(sc, &Screenq, entry)
-			if (sc->rootwin == e->window)
-				goto test;
-		return;
-test:
-		if (e->atom == ewmh[_NET_DESKTOP_NAMES].atom)
-			group_update_names(sc);
+		TAILQ_FOREACH(sc, &Screenq, entry) {
+			if (sc->rootwin == e->window) {
+				if (e->atom == ewmh[_NET_DESKTOP_NAMES].atom)
+					group_update_names(sc);
+			}
+		}
 	}
 }
 
@@ -277,7 +276,7 @@ xev_handle_keypress(XEvent *ee)
 	struct client_ctx	*cc = NULL, fakecc;
 	struct keybinding	*kb;
 	KeySym			 keysym, skeysym;
-	int			 modshift;
+	u_int			 modshift;
 
 	keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
 	skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
@@ -322,7 +321,8 @@ xev_handle_keyrelease(XEvent *ee)
 	XKeyEvent		*e = &ee->xkey;
 	struct screen_ctx	*sc;
 	struct client_ctx	*cc;
-	int			 i, keysym;
+	KeySym			 keysym;
+	u_int			 i;
 
 	sc = screen_fromroot(e->root);
 	cc = client_current();
@@ -340,17 +340,29 @@ static void
 xev_handle_clientmessage(XEvent *ee)
 {
 	XClientMessageEvent	*e = &ee->xclient;
-	Atom			 xa_wm_change_state;
-	struct client_ctx	*cc;
-
-	xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
+	struct client_ctx	*cc, *old_cc;
 
 	if ((cc = client_find(e->window)) == NULL)
 		return;
 
-	if (e->message_type == xa_wm_change_state && e->format == 32 &&
-	    e->data.l[0] == IconicState)
+	if (e->message_type == cwmh[WM_CHANGE_STATE].atom &&
+	    e->format == 32 && e->data.l[0] == IconicState)
 		client_hide(cc);
+
+	if (e->message_type == ewmh[_NET_CLOSE_WINDOW].atom)
+		client_send_delete(cc);
+
+	if (e->message_type == ewmh[_NET_ACTIVE_WINDOW].atom &&
+	    e->format == 32) {                                                
+		old_cc = client_current();
+		if (old_cc)
+			client_ptrsave(old_cc);
+		client_ptrwarp(cc);
+	}
+	if (e->message_type == ewmh[_NET_WM_STATE].atom &&
+	    e->format == 32)
+		xu_ewmh_handle_net_wm_state_msg(cc,
+		    e->data.l[0], e->data.l[1], e->data.l[2]);
 }
 
 static void
@@ -377,15 +389,13 @@ static void
 xev_handle_mappingnotify(XEvent *ee)
 {
 	XMappingEvent		*e = &ee->xmapping;
-	struct keybinding	*kb;
-
-	TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
-		conf_ungrab(&Conf, kb);
+	struct screen_ctx	*sc;
 
 	XRefreshKeyboardMapping(e);
-
-	TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
-		conf_grab(&Conf, kb);
+	if (e->request == MappingKeyboard) {
+		TAILQ_FOREACH(sc, &Screenq, entry)
+			conf_grab_kbd(sc->rootwin);
+	}
 }
 
 static void
diff --git a/xutil.c b/xutil.c
index f8bd200..df5fc4f 100644
--- a/xutil.c
+++ b/xutil.c
@@ -33,7 +33,7 @@
 static unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
 
 int
-xu_ptr_grab(Window win, int mask, Cursor curs)
+xu_ptr_grab(Window win, u_int mask, Cursor curs)
 {
 	return (XGrabPointer(X_Dpy, win, False, mask,
 	    GrabModeAsync, GrabModeAsync,
@@ -41,7 +41,7 @@ xu_ptr_grab(Window win, int mask, Cursor curs)
 }
 
 int
-xu_ptr_regrab(int mask, Cursor curs)
+xu_ptr_regrab(u_int mask, Cursor curs)
 {
 	return (XChangeActivePointerGrab(X_Dpy, mask,
 	    curs, CurrentTime) == GrabSuccess ? 0 : -1);
@@ -56,7 +56,8 @@ xu_ptr_ungrab(void)
 void
 xu_btn_grab(Window win, int mask, u_int btn)
 {
-	int	i;
+	u_int	i;
+
 	for (i = 0; i < nitems(ign_mods); i++)
 		XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win,
 		    False, BUTTONMASK, GrabModeAsync,
@@ -66,7 +67,8 @@ xu_btn_grab(Window win, int mask, u_int btn)
 void
 xu_btn_ungrab(Window win, int mask, u_int btn)
 {
-	int	i;
+	u_int	i;
+
 	for (i = 0; i < nitems(ign_mods); i++)
 		XUngrabButton(X_Dpy, btn, (mask | ign_mods[i]), win);
 }
@@ -88,10 +90,10 @@ xu_ptr_setpos(Window win, int x, int y)
 }
 
 void
-xu_key_grab(Window win, int mask, int keysym)
+xu_key_grab(Window win, u_int mask, KeySym keysym)
 {
 	KeyCode	 code;
-	int	 i;
+	u_int	 i;
 
 	code = XKeysymToKeycode(X_Dpy, keysym);
 	if ((XkbKeycodeToKeysym(X_Dpy, code, 0, 0) != keysym) &&
@@ -104,21 +106,6 @@ xu_key_grab(Window win, int mask, int keysym)
 }
 
 void
-xu_key_ungrab(Window win, int mask, int keysym)
-{
-	KeyCode	 code;
-	int	 i;
-
-	code = XKeysymToKeycode(X_Dpy, keysym);
-	if ((XkbKeycodeToKeysym(X_Dpy, code, 0, 0) != keysym) &&
-	    (XkbKeycodeToKeysym(X_Dpy, code, 0, 1) == keysym))
-		mask |= ShiftMask;
-
-	for (i = 0; i < nitems(ign_mods); i++)
-		XUngrabKey(X_Dpy, code, (mask | ign_mods[i]), win);
-}
-
-void
 xu_configure(struct client_ctx *cc)
 {
 	XConfigureEvent	 ce;
@@ -138,19 +125,19 @@ xu_configure(struct client_ctx *cc)
 }
 
 void
-xu_sendmsg(Window win, Atom atm, long val)
+xu_sendmsg(Window win, Atom type, Atom atm)
 {
-	XEvent	 e;
+	XClientMessageEvent	 e;
 
-	(void)memset(&e, 0, sizeof(e));
-	e.xclient.type = ClientMessage;
-	e.xclient.window = win;
-	e.xclient.message_type = atm;
-	e.xclient.format = 32;
-	e.xclient.data.l[0] = val;
-	e.xclient.data.l[1] = CurrentTime;
+	bzero(&e, sizeof(e));
+	e.type = ClientMessage;
+	e.window = win;
+	e.message_type = type;
+	e.format = 32;
+	e.data.l[0] = atm;
+	e.data.l[1] = CurrentTime;
 
-	XSendEvent(X_Dpy, win, False, 0, &e);
+	XSendEvent(X_Dpy, win, False, 0L, (XEvent *)&e);
 }
 
 int
@@ -237,6 +224,7 @@ struct atom_ctx cwmh[CWMH_NITEMS] = {
 	{"WM_PROTOCOLS",		None},
 	{"_MOTIF_WM_HINTS",		None},
 	{"UTF8_STRING",			None},
+	{"WM_CHANGE_STATE",		None},
 };
 struct atom_ctx ewmh[EWMH_NITEMS] = {
 	{"_NET_SUPPORTED",		None},
@@ -253,12 +241,16 @@ struct atom_ctx ewmh[EWMH_NITEMS] = {
 	{"_NET_WORKAREA",		None},
 	{"_NET_WM_NAME",		None},
 	{"_NET_WM_DESKTOP",		None},
+	{"_NET_CLOSE_WINDOW",		None},
+	{"_NET_WM_STATE", 		None},
+	{"_NET_WM_STATE_MAXIMIZED_VERT",None},
+	{"_NET_WM_STATE_MAXIMIZED_HORZ",None},
 };
 
 void
 xu_getatoms(void)
 {
-	int	 i;
+	u_int	 i;
 
 	for (i = 0; i < nitems(cwmh); i++)
 		cwmh[i].atom = XInternAtom(X_Dpy, cwmh[i].name, False);
@@ -271,7 +263,7 @@ void
 xu_ewmh_net_supported(struct screen_ctx *sc)
 {
 	Atom	 atom[EWMH_NITEMS];
-	int	 i;
+	u_int	 i;
 
 	for (i = 0; i < nitems(ewmh); i++)
 		atom[i] = ewmh[i].atom;
@@ -416,24 +408,125 @@ xu_ewmh_net_wm_desktop(struct client_ctx *cc)
 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&no, 1);
 }
 
-unsigned long
-xu_getcolor(struct screen_ctx *sc, char *name)
+Atom *
+xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
 {
-	XColor	 color, tmp;
+	Atom	*state, *p = NULL;
 
-	if (!XAllocNamedColor(X_Dpy, sc->colormap, name, &color, &tmp)) {
-		warnx("XAllocNamedColor error: '%s'", name);
-		return (0);
+	if ((*n = xu_getprop(cc->win, ewmh[_NET_WM_STATE].atom, XA_ATOM, 64L,
+	    (u_char **)&p)) <= 0)
+		return (NULL);
+
+	state = xmalloc(*n * sizeof(Atom));
+	memcpy(state, p, *n * sizeof(Atom));
+	XFree((char *)p);
+
+	return (state);
+}
+
+void
+xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
+    Atom first, Atom second)
+{
+	int i;
+	static struct handlers {
+		int atom;
+		int property;
+		void (*toggle)(struct client_ctx *);
+	} handlers[] = {
+		{ _NET_WM_STATE_MAXIMIZED_VERT,
+			CLIENT_VMAXIMIZED,
+			client_vmaximize },
+		{ _NET_WM_STATE_MAXIMIZED_HORZ,
+			CLIENT_HMAXIMIZED,
+			client_hmaximize },
+	};
+
+	for (i = 0; i < nitems(handlers); i++) {
+		if (first != ewmh[handlers[i].atom].atom &&
+		    second != ewmh[handlers[i].atom].atom)
+			continue;
+		switch (action) {
+		case _NET_WM_STATE_ADD:
+			if ((cc->flags & handlers[i].property) == 0)
+				handlers[i].toggle(cc);
+			break;
+		case _NET_WM_STATE_REMOVE:
+			if (cc->flags & handlers[i].property)
+				handlers[i].toggle(cc);
+			break;
+		case _NET_WM_STATE_TOGGLE:
+			handlers[i].toggle(cc);
+		}
+	}
+}
+
+void
+xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
+{
+	Atom	*atoms;
+	int	 i, n;
+
+	atoms = xu_ewmh_get_net_wm_state(cc, &n);
+	for (i = 0; i < n; i++) {
+		if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom)
+			client_hmaximize(cc);
+		if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom)
+			client_vmaximize(cc);
 	}
+	free(atoms);
+}
+
+void
+xu_ewmh_set_net_wm_state(struct client_ctx *cc)
+{
+	Atom	*atoms, *oatoms;
+	int	 n, i, j;
+
+	oatoms = xu_ewmh_get_net_wm_state(cc, &n);
+	atoms = xmalloc((n + _NET_WM_STATES_NITEMS) * sizeof(Atom));
+	for (i = j = 0; i < n; i++) {
+		if (oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom &&
+		    oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom)
+			atoms[j++] = oatoms[i];
+	}
+	free(oatoms);
+	if (cc->flags & CLIENT_HMAXIMIZED)
+		atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom;
+	if (cc->flags & CLIENT_VMAXIMIZED)
+		atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom;
+	if (j > 0)
+		XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE].atom,
+		    XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j);
+	else
+		XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE].atom);
+	free(atoms);
+}
+
+void
+xu_xorcolor(XftColor a, XftColor b, XftColor *r)
+{
+	r->pixel = a.pixel ^ b.pixel;
+	r->color.red = a.color.red ^ b.color.red;
+	r->color.green = a.color.green ^ b.color.green;
+	r->color.blue = a.color.blue ^ b.color.blue;
+	r->color.alpha = 0xffff;
+}
+
+int
+xu_xft_width(XftFont *xftfont, const char *text, int len)
+{
+	XGlyphInfo	 extents;
+
+	XftTextExtentsUtf8(X_Dpy, xftfont, (const FcChar8*)text,
+	    len, &extents);
 
-	return (color.pixel);
+	return (extents.xOff);
 }
 
 void
-xu_xorcolor(XRenderColor a, XRenderColor b, XRenderColor *r)
+xu_xft_draw(struct screen_ctx *sc, const char *text, int color, int x, int y)
 {
-	r->red = a.red ^ b.red;
-	r->green = a.green ^ b.green;
-	r->blue = a.blue ^ b.blue;
-	r->alpha = 0xffff;
+	XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor[color], sc->xftfont,
+	    x, y, (const FcChar8*)text, strlen(text));
 }