about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--calmwm.c22
-rw-r--r--calmwm.h78
-rw-r--r--client.c373
-rw-r--r--conf.c74
-rw-r--r--cwmrc.549
-rw-r--r--group.c46
-rw-r--r--kbfunc.c79
-rw-r--r--menu.c36
-rw-r--r--parse.y18
-rw-r--r--screen.c64
-rw-r--r--search.c10
-rw-r--r--util.c8
-rw-r--r--xevents.c20
-rw-r--r--xmalloc.c12
-rw-r--r--xutil.c172
15 files changed, 540 insertions, 521 deletions
diff --git a/calmwm.c b/calmwm.c
index 4111d95..04acc2e 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -44,6 +44,7 @@ struct screen_q		 Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
 struct conf		 Conf;
 volatile sig_atomic_t	 cwm_status;
 
+__dead void	usage(void);
 static void	sighdlr(int);
 static int	x_errorhandler(Display *, XErrorEvent *);
 static int	x_init(const char *);
@@ -88,11 +89,12 @@ main(int argc, char **argv)
 	argc -= optind;
 	argv += optind;
 
-	if (signal(SIGCHLD, sighdlr) == SIG_ERR)
+	if (signal(SIGCHLD, sighdlr) == SIG_ERR ||
+	    signal(SIGHUP, sighdlr) == SIG_ERR ||
+	    signal(SIGINT, sighdlr) == SIG_ERR ||
+	    signal(SIGTERM, sighdlr) == SIG_ERR)
 		err(1, "signal");
-	if (signal(SIGHUP, sighdlr) == SIG_ERR)
-		err(1, "signal");
-
+ 
 	if (parse_config(Conf.conf_file, &Conf) == -1) {
 		warnx("error parsing config file");
 		if (nflag)
@@ -126,7 +128,7 @@ main(int argc, char **argv)
 		u_exec(fallback);
 	}
 
-	return(0);
+	return 0;
 }
 
 static int
@@ -144,7 +146,7 @@ x_init(const char *dpyname)
 
 	Conf.xrandr = XRRQueryExtension(X_Dpy, &Conf.xrandr_event_base, &i);
 
-	conf_atoms();
+	xu_atom_init();
 	conf_cursor(&Conf);
 
 	for (i = 0; i < ScreenCount(X_Dpy); i++)
@@ -182,7 +184,7 @@ static int
 x_wmerrorhandler(Display *dpy, XErrorEvent *e)
 {
 	errx(1, "root window unavailable - perhaps another wm is running?");
-	return(0);
+	return 0;
 }
 
 static int
@@ -198,7 +200,7 @@ x_errorhandler(Display *dpy, XErrorEvent *e)
 
 	warnx("%s(0x%x): %s", req, (unsigned int)e->resourceid, msg);
 #endif
-	return(0);
+	return 0;
 }
 
 static void
@@ -217,6 +219,10 @@ sighdlr(int sig)
 	case SIGHUP:
 		cwm_status = CWM_EXEC_WM;
 		break;
+	case SIGINT:
+	case SIGTERM:
+		cwm_status = CWM_QUIT;
+		break;
 	}
 
 	errno = save_errno;
diff --git a/calmwm.h b/calmwm.h
index bd34cb2..95e3c8d 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -84,7 +84,6 @@ size_t strlcpy(char *, const char *, size_t);
 #define CWM_UP_LEFT		(CWM_UP | CWM_LEFT)
 #define CWM_DOWN_RIGHT		(CWM_DOWN | CWM_RIGHT)
 #define CWM_DOWN_LEFT		(CWM_DOWN | CWM_LEFT)
-#define DIRECTIONMASK		(CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)
 
 #define CWM_CYCLE_FORWARD	0x0001
 #define CWM_CYCLE_REVERSE	0x0002
@@ -121,11 +120,6 @@ struct geom {
 	int		 w;
 	int		 h;
 };
-
-enum apply_gap {
-	CWM_NOGAP = 0,
-	CWM_GAP
-};
 struct gap {
 	int		 top;
 	int		 bottom;
@@ -197,8 +191,9 @@ struct client_ctx {
 	struct name_q		 nameq;
 	char			*name;
 	char			*label;
-	XClassHint		ch;
-	XWMHints		*wmh;
+	char			*res_class; /* class hint */
+	char			*res_name; /* class hint */
+	int			 initial_state; /* wm hint */
 };
 TAILQ_HEAD(client_q, client_ctx);
 
@@ -317,6 +312,8 @@ struct conf {
 	int			 bwidth;
 	int			 mamount;
 	int			 snapdist;
+	int			 htile;
+	int			 vtile;
 	struct gap		 gap;
 	char			*color[CWM_COLOR_NITEMS];
 	char			*font;
@@ -413,38 +410,36 @@ extern struct conf			 Conf;
 
 void			 usage(void);
 
-void			 client_applysizehints(struct client_ctx *);
+void			 client_apply_sizehints(struct client_ctx *);
+void			 client_close(struct client_ctx *);
 void			 client_config(struct client_ctx *);
 struct client_ctx	*client_current(struct screen_ctx *);
-void			 client_cycle(struct screen_ctx *, int);
-void			 client_remove(struct client_ctx *);
 void			 client_draw_border(struct client_ctx *);
 struct client_ctx	*client_find(Window);
-long			 client_get_wm_state(struct client_ctx *);
-void			 client_getsizehints(struct client_ctx *);
+void			 client_get_sizehints(struct client_ctx *);
 void			 client_hide(struct client_ctx *);
 void 			 client_htile(struct client_ctx *);
+int			 client_inbound(struct client_ctx *, int, int);
+struct client_ctx	*client_init(Window, struct screen_ctx *);
 void			 client_lower(struct client_ctx *);
-void			 client_msg(struct client_ctx *, Atom, Time);
 void			 client_move(struct client_ctx *);
 void			 client_mtf(struct client_ctx *);
-int			 client_inbound(struct client_ctx *, int, int);
-struct client_ctx	*client_init(Window, struct screen_ctx *, int);
+struct client_ctx	*client_next(struct client_ctx *);
+struct client_ctx	*client_prev(struct client_ctx *);
 void			 client_ptr_inbound(struct client_ctx *, int);
-void			 client_ptrsave(struct client_ctx *);
-void			 client_ptrwarp(struct client_ctx *);
+void			 client_ptr_save(struct client_ctx *);
+void			 client_ptr_warp(struct client_ctx *);
 void			 client_raise(struct client_ctx *);
+void			 client_remove(struct client_ctx *);
 void			 client_resize(struct client_ctx *, int);
-void			 client_close(struct client_ctx *);
-void			 client_set_wm_state(struct client_ctx *, long);
-void			 client_setactive(struct client_ctx *);
-void			 client_setname(struct client_ctx *);
+void			 client_set_active(struct client_ctx *);
+void			 client_set_name(struct client_ctx *);
 void			 client_show(struct client_ctx *);
 int			 client_snapcalc(int, int, int, int, int);
-void			 client_toggle_freeze(struct client_ctx *);
-void			 client_toggle_fullscreen(struct client_ctx *);
 void			 client_toggle_hidden(struct client_ctx *);
 void			 client_toggle_hmaximize(struct client_ctx *);
+void			 client_toggle_fullscreen(struct client_ctx *);
+void			 client_toggle_freeze(struct client_ctx *);
 void			 client_toggle_maximize(struct client_ctx *);
 void			 client_toggle_skip_pager(struct client_ctx *);
 void			 client_toggle_skip_taskbar(struct client_ctx *);
@@ -474,16 +469,16 @@ void			 group_update_names(struct screen_ctx *);
 
 void			 search_match_client(struct menu_q *, struct menu_q *,
 			     char *);
+void			 search_match_cmd(struct menu_q *, struct menu_q *,
+			     char *);
 void			 search_match_exec(struct menu_q *, struct menu_q *,
 			     char *);
+void			 search_match_group(struct menu_q *, struct menu_q *,
+			     char *);
 void			 search_match_path(struct menu_q *, struct menu_q *,
 			     char *);
 void			 search_match_text(struct menu_q *, struct menu_q *,
 			     char *);
-void			 search_match_cmd(struct menu_q *, struct menu_q *,
-			     char *);
-void			 search_match_group(struct menu_q *, struct menu_q *,
-			     char *);
 void			 search_match_wm(struct menu_q *, struct menu_q *,
 			     char *);
 void			 search_print_client(struct menu *, int);
@@ -493,19 +488,18 @@ void			 search_print_text(struct menu *, int);
 void			 search_print_wm(struct menu *, int);
 
 struct region_ctx	*region_find(struct screen_ctx *, int, int);
+void			 screen_assert_clients_within(struct screen_ctx *);
+struct geom		 screen_area(struct screen_ctx *, int, int, int);
 struct screen_ctx	*screen_find(Window);
-struct geom		 screen_area(struct screen_ctx *, int, int,
-			     enum apply_gap);
 void			 screen_init(int);
-void			 screen_update_geometry(struct screen_ctx *);
-void			 screen_updatestackingorder(struct screen_ctx *);
-void			 screen_assert_clients_within(struct screen_ctx *);
 void			 screen_prop_win_create(struct screen_ctx *, Window);
 void			 screen_prop_win_destroy(struct screen_ctx *);
 void			 screen_prop_win_draw(struct screen_ctx *,
 			     const char *, ...)
 			    __attribute__((__format__ (printf, 2, 3)))
 			    __attribute__((__nonnull__ (2)));
+void			 screen_update_geometry(struct screen_ctx *);
+void			 screen_updatestackingorder(struct screen_ctx *);
 
 void			 kbfunc_cwm_status(void *, struct cargs *);
 void			 kbfunc_ptrmove(void *, struct cargs *);
@@ -554,7 +548,6 @@ void			 menuq_clear(struct menu_q *);
 
 int			 parse_config(const char *, struct conf *);
 
-void			 conf_atoms(void);
 void			 conf_autogroup(struct conf *, int, const char *,
 			     const char *);
 int			 conf_bind_key(struct conf *, const char *,
@@ -577,12 +570,16 @@ void			 conf_group(struct screen_ctx *);
 
 void			 xev_process(void);
 
-int			 xu_getprop(Window, Atom, Atom, long, unsigned char **);
-int			 xu_getstrprop(Window, Atom, char **);
-void			 xu_ptr_getpos(Window, int *, int *);
-void			 xu_ptr_setpos(Window, int, int);
+int			 xu_get_prop(Window, Atom, Atom, long, unsigned char **);
+int			 xu_get_strprop(Window, Atom, char **);
+void			 xu_ptr_get(Window, int *, int *);
+void			 xu_ptr_set(Window, int, int);
+void			 xu_get_wm_state(Window, long *);
+void			 xu_set_wm_state(Window, long);
+void			 xu_send_clientmsg(Window, Atom, Time);
 void 			 xu_xorcolor(XftColor, XftColor, XftColor *);
 
+void			 xu_atom_init(void);
 void			 xu_ewmh_net_supported(struct screen_ctx *);
 void			 xu_ewmh_net_supported_wm_check(struct screen_ctx *);
 void			 xu_ewmh_net_desktop_geometry(struct screen_ctx *);
@@ -591,14 +588,13 @@ void			 xu_ewmh_net_workarea(struct screen_ctx *);
 void			 xu_ewmh_net_client_list(struct screen_ctx *);
 void			 xu_ewmh_net_client_list_stacking(struct screen_ctx *);
 void			 xu_ewmh_net_active_window(struct screen_ctx *, Window);
-Window			 xu_ewmh_get_net_active_window(struct screen_ctx *);
 void			 xu_ewmh_net_number_of_desktops(struct screen_ctx *);
 void			 xu_ewmh_net_showing_desktop(struct screen_ctx *);
 void			 xu_ewmh_net_virtual_roots(struct screen_ctx *);
 void			 xu_ewmh_net_current_desktop(struct screen_ctx *);
 void			 xu_ewmh_net_desktop_names(struct screen_ctx *);
-
-void			 xu_ewmh_net_wm_desktop(struct client_ctx *);
+int			 xu_ewmh_get_net_wm_desktop(struct client_ctx *, long *);
+void			 xu_ewmh_set_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);
diff --git a/client.c b/client.c
index 2384f59..821da25 100644
--- a/client.c
+++ b/client.c
@@ -31,34 +31,31 @@
 
 #include "calmwm.h"
 
-static struct client_ctx	*client_next(struct client_ctx *);
-static struct client_ctx	*client_prev(struct client_ctx *);
-static void			 client_placecalc(struct client_ctx *);
-static void			 client_wm_protocols(struct client_ctx *);
+static void			 client_class_hint(struct client_ctx *);
+static void			 client_placement(struct client_ctx *);
 static void			 client_mwm_hints(struct client_ctx *);
+static void			 client_wm_protocols(struct client_ctx *);
 
 struct client_ctx *
-client_init(Window win, struct screen_ctx *sc, int active)
+client_init(Window win, struct screen_ctx *sc)
 {
 	struct client_ctx	*cc;
 	XWindowAttributes	 wattr;
 	int			 mapped;
-	Window			 rwin, cwin;
-	int			 x, y, wx, wy;
-	unsigned int		 mask;
+	long			 state;
 
 	if (win == None)
-		return(NULL);
+		return NULL;
 	if (!XGetWindowAttributes(X_Dpy, win, &wattr))
-		return(NULL);
+		return NULL;
 
 	if (sc == NULL) {
 		if ((sc = screen_find(wattr.root)) == NULL)
-			return(NULL);
+			return NULL;
 		mapped = 1;
 	} else {
 		if (wattr.override_redirect || wattr.map_state != IsViewable)
-			return(NULL);
+			return NULL;
 		mapped = wattr.map_state != IsUnmapped;
 	}
 
@@ -67,12 +64,15 @@ client_init(Window win, struct screen_ctx *sc, int active)
 	cc = xmalloc(sizeof(*cc));
 	cc->sc = sc;
 	cc->win = win;
+	cc->name = NULL;
 	cc->label = NULL;
 	cc->gc = NULL;
+	cc->res_class = NULL;
+	cc->res_name = NULL;
 	cc->flags = 0;
 	cc->stackingorder = 0;
+	cc->initial_state = 0;
 	memset(&cc->hint, 0, sizeof(cc->hint));
-	memset(&cc->ch, 0, sizeof(cc->ch));
 	TAILQ_INIT(&cc->nameq);
 
 	cc->geom.x = wattr.x;
@@ -83,13 +83,13 @@ client_init(Window win, struct screen_ctx *sc, int active)
 	cc->obwidth = wattr.border_width;
 	cc->bwidth = Conf.bwidth;
 
-	client_setname(cc);
+	client_set_name(cc);
 	conf_client(cc);
 
-	XGetClassHint(X_Dpy, cc->win, &cc->ch);
 	client_wm_hints(cc);
+	client_class_hint(cc);
 	client_wm_protocols(cc);
-	client_getsizehints(cc);
+	client_get_sizehints(cc);
 	client_transient(cc);
 	client_mwm_hints(cc);
 
@@ -101,18 +101,14 @@ client_init(Window win, struct screen_ctx *sc, int active)
 	cc->ptr.y = cc->geom.h / 2;
 
 	if (wattr.map_state != IsViewable) {
-		client_placecalc(cc);
+		client_placement(cc);
 		client_resize(cc, 0);
-		if ((cc->wmh) && (cc->wmh->flags & StateHint))
-			client_set_wm_state(cc, cc->wmh->initial_state);
-	} else {
-		if ((active == 0) && (XQueryPointer(X_Dpy, cc->win, &rwin,
-		    &cwin, &x, &y, &wx, &wy, &mask)) && (cwin != None))
-			active = 1;
+		if (cc->initial_state)
+			xu_set_wm_state(cc->win, cc->initial_state);
 	}
 
-	XSelectInput(X_Dpy, cc->win, ColormapChangeMask | EnterWindowMask |
-	    PropertyChangeMask | KeyReleaseMask);
+	XSelectInput(X_Dpy, cc->win,
+	    EnterWindowMask | PropertyChangeMask | KeyReleaseMask);
 
 	XAddToSaveSet(X_Dpy, cc->win);
 
@@ -125,7 +121,8 @@ client_init(Window win, struct screen_ctx *sc, int active)
 	xu_ewmh_net_client_list_stacking(sc);
 	xu_ewmh_restore_net_wm_state(cc);
 
-	if (client_get_wm_state(cc) == IconicState)
+	xu_get_wm_state(cc->win, &state);
+	if (state == IconicState)
 		client_hide(cc);
 	else
 		client_show(cc);
@@ -148,10 +145,29 @@ out:
 	XSync(X_Dpy, False);
 	XUngrabServer(X_Dpy);
 
-	if (active)
-		client_setactive(cc);
+	return cc;
+}
+
+struct client_ctx *
+client_current(struct screen_ctx *sc)
+{
+	struct screen_ctx	*_sc;
+	struct client_ctx	*cc;
 
-	return(cc);
+	if (sc) {
+		TAILQ_FOREACH(cc, &sc->clientq, entry) {
+			if (cc->flags & CLIENT_ACTIVE)
+				return cc;
+		}
+	} else {
+		TAILQ_FOREACH(_sc, &Screenq, entry) {
+			TAILQ_FOREACH(cc, &_sc->clientq, entry) {
+				if (cc->flags & CLIENT_ACTIVE)
+					return cc;
+			}
+		}
+	}
+	return NULL;
 }
 
 struct client_ctx *
@@ -163,10 +179,30 @@ client_find(Window win)
 	TAILQ_FOREACH(sc, &Screenq, entry) {
 		TAILQ_FOREACH(cc, &sc->clientq, entry) {
 			if (cc->win == win)
-				return(cc);
+				return cc;
 		}
 	}
-	return(NULL);
+	return NULL;
+}
+
+struct client_ctx *
+client_next(struct client_ctx *cc)
+{
+	struct screen_ctx	*sc = cc->sc;
+	struct client_ctx	*newcc;
+
+	return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ?
+	    newcc : TAILQ_FIRST(&sc->clientq));
+}
+
+struct client_ctx *
+client_prev(struct client_ctx *cc)
+{
+	struct screen_ctx	*sc = cc->sc;
+	struct client_ctx	*newcc;
+
+	return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ?
+	    newcc : TAILQ_LAST(&sc->clientq, client_q));
 }
 
 void
@@ -189,18 +225,15 @@ client_remove(struct client_ctx *cc)
 		free(wn);
 	}
 
-	if (cc->ch.res_class)
-		XFree(cc->ch.res_class);
-	if (cc->ch.res_name)
-		XFree(cc->ch.res_name);
-	if (cc->wmh)
-		XFree(cc->wmh);
-
+	free(cc->name);
+	free(cc->label);
+	free(cc->res_class);
+	free(cc->res_name);
 	free(cc);
 }
 
 void
-client_setactive(struct client_ctx *cc)
+client_set_active(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
 	struct client_ctx	*oldcc;
@@ -216,7 +249,7 @@ client_setactive(struct client_ctx *cc)
 		    RevertToPointerRoot, CurrentTime);
 	}
 	if (cc->flags & CLIENT_WM_TAKE_FOCUS)
-		client_msg(cc, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
+		xu_send_clientmsg(cc->win, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
 
 	if ((oldcc = client_current(sc)) != NULL) {
 		oldcc->flags &= ~CLIENT_ACTIVE;
@@ -234,28 +267,6 @@ client_setactive(struct client_ctx *cc)
 	xu_ewmh_net_active_window(sc, cc->win);
 }
 
-struct client_ctx *
-client_current(struct screen_ctx *sc)
-{
-	struct screen_ctx	*_sc;
-	struct client_ctx	*cc;
-
-	if (sc) {
-		TAILQ_FOREACH(cc, &sc->clientq, entry) {
-			if (cc->flags & CLIENT_ACTIVE)
-				return(cc);
-		}
-	} else {
-		TAILQ_FOREACH(_sc, &Screenq, entry) {
-			TAILQ_FOREACH(cc, &_sc->clientq, entry) {
-				if (cc->flags & CLIENT_ACTIVE)
-					return(cc);
-			}
-		}
-	}
-	return(NULL);
-}
-
 void
 client_toggle_freeze(struct client_ctx *cc)
 {
@@ -316,7 +327,7 @@ client_toggle_fullscreen(struct client_ctx *cc)
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_NOGAP);
+	    cc->geom.y + cc->geom.h / 2, 0);
 
 	cc->bwidth = 0;
 	cc->geom = area;
@@ -354,7 +365,7 @@ client_toggle_maximize(struct client_ctx *cc)
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 
 	cc->geom.x = area.x;
 	cc->geom.y = area.y;
@@ -388,7 +399,7 @@ client_toggle_vmaximize(struct client_ctx *cc)
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 
 	cc->geom.y = area.y;
 	cc->geom.h = area.h - (cc->bwidth * 2);
@@ -420,7 +431,7 @@ client_toggle_hmaximize(struct client_ctx *cc)
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 
 	cc->geom.x = area.x;
 	cc->geom.w = area.w - (cc->bwidth * 2);
@@ -491,7 +502,7 @@ void
 client_ptr_inbound(struct client_ctx *cc, int getpos)
 {
 	if (getpos)
-		xu_ptr_getpos(cc->win, &cc->ptr.x, &cc->ptr.y);
+		xu_ptr_get(cc->win, &cc->ptr.x, &cc->ptr.y);
 
 	if (cc->ptr.x < 0)
 		cc->ptr.x = 0;
@@ -502,21 +513,21 @@ client_ptr_inbound(struct client_ctx *cc, int getpos)
 	else if (cc->ptr.y > cc->geom.h - 1)
 		cc->ptr.y = cc->geom.h - 1;
 
-	client_ptrwarp(cc);
+	client_ptr_warp(cc);
 }
 
 void
-client_ptrwarp(struct client_ctx *cc)
+client_ptr_warp(struct client_ctx *cc)
 {
-	xu_ptr_setpos(cc->win, cc->ptr.x, cc->ptr.y);
+	xu_ptr_set(cc->win, cc->ptr.x, cc->ptr.y);
 }
 
 void
-client_ptrsave(struct client_ctx *cc)
+client_ptr_save(struct client_ctx *cc)
 {
 	int	 x, y;
 
-	xu_ptr_getpos(cc->win, &x, &y);
+	xu_ptr_get(cc->win, &x, &y);
 	if (client_inbound(cc, x, y)) {
 		cc->ptr.x = x;
 		cc->ptr.y = y;
@@ -536,7 +547,7 @@ client_hide(struct client_ctx *cc)
 		xu_ewmh_net_active_window(cc->sc, None);
 	}
 	cc->flags |= CLIENT_HIDDEN;
-	client_set_wm_state(cc, IconicState);
+	xu_set_wm_state(cc->win, IconicState);
 }
 
 void
@@ -545,7 +556,7 @@ client_show(struct client_ctx *cc)
 	XMapRaised(X_Dpy, cc->win);
 
 	cc->flags &= ~CLIENT_HIDDEN;
-	client_set_wm_state(cc, NormalState);
+	xu_set_wm_state(cc->win, NormalState);
 	client_draw_border(cc);
 }
 
@@ -585,6 +596,23 @@ client_draw_border(struct client_ctx *cc)
 }
 
 static void
+client_class_hint(struct client_ctx *cc)
+{
+	XClassHint	ch;
+
+	if (XGetClassHint(X_Dpy, cc->win, &ch)) {
+		if (ch.res_class) {
+			cc->res_class = xstrdup(ch.res_class);
+			XFree(ch.res_class);
+		}
+		if (ch.res_name) {
+			cc->res_name = xstrdup(ch.res_name);
+			XFree(ch.res_name);
+		}
+	}
+}
+
+static void
 client_wm_protocols(struct client_ctx *cc)
 {
 	Atom	*p;
@@ -604,149 +632,62 @@ client_wm_protocols(struct client_ctx *cc)
 void
 client_wm_hints(struct client_ctx *cc)
 {
-	if ((cc->wmh = XGetWMHints(X_Dpy, cc->win)) == NULL)
-		return;
-
-	if ((cc->wmh->flags & InputHint) && (cc->wmh->input))
-		cc->flags |= CLIENT_INPUT;
-
-	if ((cc->wmh->flags & XUrgencyHint))
-		client_urgency(cc);
-}
-
-void
-client_msg(struct client_ctx *cc, Atom proto, Time ts)
-{
-	XClientMessageEvent	 cm;
-
-	(void)memset(&cm, 0, sizeof(cm));
-	cm.type = ClientMessage;
-	cm.window = cc->win;
-	cm.message_type = cwmh[WM_PROTOCOLS];
-	cm.format = 32;
-	cm.data.l[0] = proto;
-	cm.data.l[1] = ts;
-
-	XSendEvent(X_Dpy, cc->win, False, NoEventMask, (XEvent *)&cm);
+	XWMHints	*wmh;
+ 
+	if ((wmh = XGetWMHints(X_Dpy, cc->win)) != NULL) {
+		if ((wmh->flags & InputHint) && (wmh->input))
+			cc->flags |= CLIENT_INPUT;
+		if ((wmh->flags & XUrgencyHint))
+			client_urgency(cc);
+		if ((wmh->flags & StateHint))
+			cc->initial_state = wmh->initial_state;
+		XFree(wmh);
+	}
 }
 
 void
 client_close(struct client_ctx *cc)
 {
 	if (cc->flags & CLIENT_WM_DELETE_WINDOW)
-		client_msg(cc, cwmh[WM_DELETE_WINDOW], CurrentTime);
+		xu_send_clientmsg(cc->win, cwmh[WM_DELETE_WINDOW], CurrentTime);
 	else
 		XKillClient(X_Dpy, cc->win);
 }
 
 void
-client_setname(struct client_ctx *cc)
+client_set_name(struct client_ctx *cc)
 {
-	struct winname	*wn;
-	char		*newname;
+	struct winname	*wn, *wnnxt;
 	int		 i = 0;
 
-	if (!xu_getstrprop(cc->win, ewmh[_NET_WM_NAME], &newname))
-		if (!xu_getstrprop(cc->win, XA_WM_NAME, &newname))
-			newname = xstrdup("");
+	free(cc->name);
+	if (!xu_get_strprop(cc->win, ewmh[_NET_WM_NAME], &cc->name))
+		if (!xu_get_strprop(cc->win, XA_WM_NAME, &cc->name))
+			cc->name = xstrdup("");
 
-	TAILQ_FOREACH(wn, &cc->nameq, entry) {
-		if (strcmp(wn->name, newname) == 0) {
-			/* Move to the last since we got a hit. */
+	TAILQ_FOREACH_SAFE(wn, &cc->nameq, entry, wnnxt) {
+		if (strcmp(wn->name, cc->name) == 0) {
 			TAILQ_REMOVE(&cc->nameq, wn, entry);
-			TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
-			goto match;
+			free(wn->name);
+			free(wn);
 		}
+		i++;
 	}
 	wn = xmalloc(sizeof(*wn));
-	wn->name = newname;
+	wn->name = xstrdup(cc->name);
 	TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
-match:
-	cc->name = wn->name;
 
-	/* Do some garbage collection. */
-	TAILQ_FOREACH(wn, &cc->nameq, entry)
-		i++;
-	if (i > Conf.nameqlen) {
+	/* Garbage collection. */
+	if ((i + 1) > Conf.nameqlen) {
 		wn = TAILQ_FIRST(&cc->nameq);
 		TAILQ_REMOVE(&cc->nameq, wn, entry);
 		free(wn->name);
 		free(wn);
-		i--;
 	}
 }
 
-void
-client_cycle(struct screen_ctx *sc, int flags)
-{
-	struct client_ctx	*newcc, *oldcc, *prevcc;
-	int			 again = 1;
-
-	if (TAILQ_EMPTY(&sc->clientq))
-		return;
-
-	prevcc = TAILQ_FIRST(&sc->clientq);
-	oldcc = client_current(sc);
-	if (oldcc == NULL)
-		oldcc = (flags & CWM_CYCLE_REVERSE) ?
-		    TAILQ_LAST(&sc->clientq, client_q) :
-		    TAILQ_FIRST(&sc->clientq);
-
-	newcc = oldcc;
-	while (again) {
-		again = 0;
-
-		newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) :
-		    client_next(newcc);
-
-		/* Only cycle visible and non-ignored windows. */
-		if ((newcc->flags & (CLIENT_SKIP_CYCLE)) ||
-		    ((flags & CWM_CYCLE_INGROUP) &&
-		    (newcc->gc != oldcc->gc)))
-			again = 1;
-
-		/* Is oldcc the only non-hidden window? */
-		if (newcc == oldcc) {
-			if (again)
-				return;	/* No windows visible. */
-			break;
-		}
-	}
-
-	/* Reset when cycling mod is released. XXX I hate this hack */
-	sc->cycling = 1;
-	client_ptrsave(oldcc);
-	client_raise(prevcc);
-	client_raise(newcc);
-	if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) {
-		newcc->ptr.x = newcc->geom.w / 2;
-		newcc->ptr.y = newcc->geom.h / 2;
-	}
-	client_ptrwarp(newcc);
-}
-
-static struct client_ctx *
-client_next(struct client_ctx *cc)
-{
-	struct screen_ctx	*sc = cc->sc;
-	struct client_ctx	*newcc;
-
-	return(((newcc = TAILQ_NEXT(cc, entry)) != NULL) ?
-	    newcc : TAILQ_FIRST(&sc->clientq));
-}
-
-static struct client_ctx *
-client_prev(struct client_ctx *cc)
-{
-	struct screen_ctx	*sc = cc->sc;
-	struct client_ctx	*newcc;
-
-	return(((newcc = TAILQ_PREV(cc, client_q, entry)) != NULL) ?
-	    newcc : TAILQ_LAST(&sc->clientq, client_q));
-}
-
 static void
-client_placecalc(struct client_ctx *cc)
+client_placement(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
 
@@ -769,8 +710,8 @@ client_placecalc(struct client_ctx *cc)
 		struct geom	 area;
 		int		 xmouse, ymouse, xslack, yslack;
 
-		xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
-		area = screen_area(sc, xmouse, ymouse, CWM_GAP);
+		xu_ptr_get(sc->rootwin, &xmouse, &ymouse);
+		area = screen_area(sc, xmouse, ymouse, 1);
 
 		xmouse = MAX(MAX(xmouse, area.x) - cc->geom.w / 2, area.x);
 		ymouse = MAX(MAX(ymouse, area.y) - cc->geom.h / 2, area.y);
@@ -803,7 +744,7 @@ client_mtf(struct client_ctx *cc)
 }
 
 void
-client_getsizehints(struct client_ctx *cc)
+client_get_sizehints(struct client_ctx *cc)
 {
 	long		 tmp;
 	XSizeHints	 size;
@@ -851,7 +792,7 @@ client_getsizehints(struct client_ctx *cc)
 }
 
 void
-client_applysizehints(struct client_ctx *cc)
+client_apply_sizehints(struct client_ctx *cc)
 {
 	Bool		 baseismin;
 
@@ -902,7 +843,7 @@ client_mwm_hints(struct client_ctx *cc)
 {
 	struct mwm_hints	*mwmh;
 
-	if (xu_getprop(cc->win, cwmh[_MOTIF_WM_HINTS],
+	if (xu_get_prop(cc->win, cwmh[_MOTIF_WM_HINTS],
 	    cwmh[_MOTIF_WM_HINTS], MWM_HINTS_ELEMENTS,
 	    (unsigned char **)&mwmh) == MWM_HINTS_ELEMENTS) {
 		if (mwmh->flags & MWM_FLAGS_DECORATIONS &&
@@ -952,15 +893,15 @@ client_snapcalc(int n0, int n1, int e0, int e1, int snapdist)
 	/* possible to snap in both directions */
 	if (s0 != 0 && s1 != 0)
 		if (abs(s0) < abs(s1))
-			return(s0);
+			return s0;
 		else
-			return(s1);
+			return s1;
 	else if (s0 != 0)
-		return(s0);
+		return s0;
 	else if (s1 != 0)
-		return(s1);
+		return s1;
 	else
-		return(0);
+		return 0;
 }
 
 void
@@ -974,7 +915,7 @@ client_htile(struct client_ctx *cc)
 	i = n = 0;
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 
 	TAILQ_FOREACH(ci, &sc->clientq, entry) {
 		if (ci->gc != cc->gc)
@@ -999,9 +940,10 @@ client_htile(struct client_ctx *cc)
 	cc->geom.x = area.x;
 	cc->geom.y = area.y;
 	cc->geom.w = area.w - (cc->bwidth * 2);
-	cc->geom.h = (area.h - (cc->bwidth * 2)) / 2;
+	if (Conf.htile > 0)
+		cc->geom.h = ((area.h - (cc->bwidth * 2)) * Conf.htile) / 100;
 	client_resize(cc, 1);
-	client_ptrwarp(cc);
+	client_ptr_warp(cc);
 
 	mh = cc->geom.h + (cc->bwidth * 2);
 	x = area.x;
@@ -1042,7 +984,7 @@ client_vtile(struct client_ctx *cc)
 	i = n = 0;
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 
 	TAILQ_FOREACH(ci, &sc->clientq, entry) {
 		if (ci->gc != cc->gc)
@@ -1066,10 +1008,11 @@ client_vtile(struct client_ctx *cc)
 	cc->flags &= ~CLIENT_VMAXIMIZED;
 	cc->geom.x = area.x;
 	cc->geom.y = area.y;
-	cc->geom.w = (area.w - (cc->bwidth * 2)) / 2;
+	if (Conf.vtile > 0)
+		cc->geom.w = ((area.w - (cc->bwidth * 2)) * Conf.vtile) / 100;
 	cc->geom.h = area.h - (cc->bwidth * 2);
 	client_resize(cc, 1);
-	client_ptrwarp(cc);
+	client_ptr_warp(cc);
 
 	mw = cc->geom.w + (cc->bwidth * 2);
 	y = area.y;
@@ -1098,25 +1041,3 @@ client_vtile(struct client_ctx *cc)
 		client_resize(ci, 1);
 	}
 }
-
-long
-client_get_wm_state(struct client_ctx *cc)
-{
-	long	*p, state = -1;
-
-	if (xu_getprop(cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
-	    (unsigned char **)&p) > 0) {
-		state = *p;
-		XFree(p);
-	}
-	return(state);
-}
-
-void
-client_set_wm_state(struct client_ctx *cc, long state)
-{
-	long	 data[] = { state, None };
-
-	XChangeProperty(X_Dpy, cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
-	    PropModeReplace, (unsigned char *)data, 2);
-}
diff --git a/conf.c b/conf.c
index 7d1233d..53ca295 100644
--- a/conf.c
+++ b/conf.c
@@ -32,7 +32,7 @@
 
 #include "calmwm.h"
 
-static const char	*conf_bind_getmask(const char *, unsigned int *);
+static const char	*conf_bind_mask(const char *, unsigned int *);
 static void		 conf_unbind_key(struct conf *, struct bind_ctx *);
 static void		 conf_unbind_mouse(struct conf *, struct bind_ctx *);
 
@@ -281,6 +281,8 @@ conf_init(struct conf *c)
 	c->stickygroups = 0;
 	c->bwidth = 1;
 	c->mamount = 1;
+	c->htile = 50;
+	c->vtile = 50;
 	c->snapdist = 0;
 	c->ngroups = 0;
 	c->nameqlen = 5;
@@ -517,7 +519,7 @@ conf_group(struct screen_ctx *sc)
 }
 
 static const char *
-conf_bind_getmask(const char *name, unsigned int *mask)
+conf_bind_mask(const char *name, unsigned int *mask)
 {
 	char		*dash;
 	const char	*ch;
@@ -525,13 +527,13 @@ conf_bind_getmask(const char *name, unsigned int *mask)
 
 	*mask = 0;
 	if ((dash = strchr(name, '-')) == NULL)
-		return(name);
+		return name;
 	for (i = 0; i < nitems(bind_mods); i++) {
 		if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash)
 			*mask |= bind_mods[i].mask;
 	}
 	/* Skip past modifiers. */
-	return(dash + 1);
+	return (dash + 1);
 }
 
 int
@@ -544,20 +546,20 @@ conf_bind_key(struct conf *c, const char *bind, const char *cmd)
 
 	if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
 		conf_unbind_key(c, NULL);
-		return(1);
+		return 1;
 	}
 	kb = xmalloc(sizeof(*kb));
-	key = conf_bind_getmask(bind, &kb->modmask);
+	key = conf_bind_mask(bind, &kb->modmask);
 	kb->press.keysym = XStringToKeysym(key);
 	if (kb->press.keysym == NoSymbol) {
 		warnx("unknown symbol: %s", key);
 		free(kb);
-		return(0);
+		return 0;
 	}
 	conf_unbind_key(c, kb);
 	if (cmd == NULL) {
 		free(kb);
-		return(1);
+		return 1;
 	}
 	cargs = xcalloc(1, sizeof(*cargs));
 	for (i = 0; i < nitems(name_to_func); i++) {
@@ -575,7 +577,7 @@ conf_bind_key(struct conf *c, const char *bind, const char *cmd)
 out:
 	kb->cargs = cargs;
 	TAILQ_INSERT_TAIL(&c->keybindq, kb, entry);
-	return(1);
+	return 1;
 }
 
 static void
@@ -605,20 +607,20 @@ conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
 
 	if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
 		conf_unbind_mouse(c, NULL);
-		return(1);
+		return 1;
 	}
 	mb = xmalloc(sizeof(*mb));
-	button = conf_bind_getmask(bind, &mb->modmask);
+	button = conf_bind_mask(bind, &mb->modmask);
 	mb->press.button = strtonum(button, Button1, Button5, &errstr);
 	if (errstr) {
 		warnx("button number is %s: %s", errstr, button);
 		free(mb);
-		return(0);
+		return 0;
 	}
 	conf_unbind_mouse(c, mb);
 	if (cmd == NULL) {
 		free(mb);
-		return(1);
+		return 1;
 	}
 	cargs = xcalloc(1, sizeof(*cargs));
 	for (i = 0; i < nitems(name_to_func); i++) {
@@ -636,7 +638,7 @@ conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
 out:
 	mb->cargs = cargs;
 	TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry);
-	return(1);
+	return 1;
 }
 
 static void
@@ -696,47 +698,3 @@ conf_grab_mouse(Window win)
 		}
 	}
 }
-
-static char *cwmhints[] = {
-	"WM_STATE",
-	"WM_DELETE_WINDOW",
-	"WM_TAKE_FOCUS",
-	"WM_PROTOCOLS",
-	"_MOTIF_WM_HINTS",
-	"UTF8_STRING",
-	"WM_CHANGE_STATE",
-};
-static char *ewmhints[] = {
-	"_NET_SUPPORTED",
-	"_NET_SUPPORTING_WM_CHECK",
-	"_NET_ACTIVE_WINDOW",
-	"_NET_CLIENT_LIST",
-	"_NET_CLIENT_LIST_STACKING",
-	"_NET_NUMBER_OF_DESKTOPS",
-	"_NET_CURRENT_DESKTOP",
-	"_NET_DESKTOP_VIEWPORT",
-	"_NET_DESKTOP_GEOMETRY",
-	"_NET_VIRTUAL_ROOTS",
-	"_NET_SHOWING_DESKTOP",
-	"_NET_DESKTOP_NAMES",
-	"_NET_WORKAREA",
-	"_NET_WM_NAME",
-	"_NET_WM_DESKTOP",
-	"_NET_CLOSE_WINDOW",
-	"_NET_WM_STATE",
-	"_NET_WM_STATE_STICKY",
-	"_NET_WM_STATE_MAXIMIZED_VERT",
-	"_NET_WM_STATE_MAXIMIZED_HORZ",
-	"_NET_WM_STATE_HIDDEN",
-	"_NET_WM_STATE_FULLSCREEN",
-	"_NET_WM_STATE_DEMANDS_ATTENTION",
-	"_NET_WM_STATE_SKIP_PAGER",
-	"_NET_WM_STATE_SKIP_TASKBAR",
-	"_CWM_WM_STATE_FREEZE",
-};
-void
-conf_atoms(void)
-{
-	XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
-	XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
-}
diff --git a/cwmrc.5 b/cwmrc.5
index 5208d29..ab70d25 100644
--- a/cwmrc.5
+++ b/cwmrc.5
@@ -37,10 +37,8 @@ Arguments containing whitespace should be surrounded by double quotes
 .Pq \&" .
 .Pp
 The following options are accepted:
-.Pp
-.Bl -tag -width Ds -compact
-.It Ic autogroup Ar group windowclass
-.It Ic autogroup Ar group windowname,windowclass
+.Bl -tag -width Ds
+.It Ic autogroup Ar group Oo Ar windowname , Oc Ns Ar windowclass
 Automatically add new windows to
 .Ar group
 if their class property matches
@@ -62,7 +60,6 @@ used to override
 The name and class values, respectively, for existing windows
 are both set in the WM_CLASS property and may be obtained using
 .Xr xprop 1 .
-.Pp
 .It Ic bind-key Ar key function
 Bind or rebind key
 .Ar key
@@ -93,7 +90,6 @@ The
 may either be one from the
 .Sx BIND FUNCTION LIST
 (see below) or the command line that is to be executed.
-.Pp
 .It Ic bind-mouse Ar button function
 Bind or rebind button
 .Ar button
@@ -128,38 +124,27 @@ The
 may be taken from the
 .Sx BIND FUNCTION LIST
 (see below) or the command line that is to be executed.
-.Pp
 .It Ic borderwidth Ar pixels
 Set the window border width to
 .Ar pixels .
-.Pp
 .It Ic color activeborder Ar color
 Set the color of the active border.
-.Pp
 .It Ic color font Ar color
 Set menu font color.
-.Pp
 .It Ic color selfont Ar color
 Set font color for selected menu item.
-.Pp
 .It Ic color groupborder Ar color
 Set the color of the border while grouping a window.
-.Pp
 .It Ic color inactiveborder Ar color
 Set the color of the inactive border.
-.Pp
 .It Ic color menubg Ar color
 Set menu background color.
-.Pp
 .It Ic color menufg Ar color
 Set menu foreground color.
-.Pp
 .It Ic color urgencyborder Ar color
 Set the color of the border of a window indicating urgency.
-.Pp
 .It Ic color ungroupborder Ar color
 Set the color of the border while ungrouping a window.
-.Pp
 .It Ic command Ar name path
 Every
 .Ar name
@@ -183,13 +168,11 @@ The defaults are
 and
 .Xr xlock 1 ,
 respectively.
-.Pp
 .It Ic fontname Ar font
 Change the default
 .Ar font
 for
 .Xr Xft 3 .
-.Pp
 .It Ic gap Ar top bottom left right
 Define a
 .Dq gap
@@ -200,28 +183,30 @@ This
 can be used for applications such as
 .Xr xclock 1 ,
 where the user may wish to remain visible.
-.Pp
+.It Ic htile Ar percent
+Set the percentage of screen the master window should occupy
+after calling
+.Ic window-htile .
+If set to 0, the horizontal size of the master window will
+remain unchanged.
+The default is 50.
 .It Ic ignore Ar windowname
 Ignore, and do not warp to, windows with the name
 .Ar windowname
 when drawing borders and cycling through windows.
-.Pp
 .It Ic moveamount Ar pixels
 Set a default size for the keyboard movement bindings,
 in pixels.
 The default is 1.
-.Pp
 .It Ic snapdist Ar pixels
 Minimum distance to snap-to adjacent edge, in pixels.
 The default is 0.
-.Pp
 .It Ic sticky Ic yes Ns \&| Ns Ic no
 Toggle sticky group mode.
 The default behavior for new windows is to not assign any group.
 By enabling sticky group mode,
 .Xr cwm 1
 will assign new windows to the currently selected group.
-.Pp
 .It Ic unbind-key Ar key
 Unbind function bound to
 .Ar key .
@@ -230,7 +215,6 @@ A special
 keyword
 .Dq all
 can be used to unbind all keys.
-.Pp
 .It Ic unbind-mouse Ar button
 Unbind function bound to
 .Ar button .
@@ -239,6 +223,13 @@ A special
 keyword
 .Dq all
 can be used to unbind all buttons.
+.It Ic vtile Ar percent
+Set the percentage of screen the master window should occupy
+after calling
+.Ic window-vtile .
+If set to 0, the vertical size of the master window will
+remain unchanged.
+The default is 50.
 .It Ic wm Ar name path
 Every
 .Ar name
@@ -326,11 +317,15 @@ Vertically maximize current window (gap + border honored).
 Horizontally maximize current window (gap + border honored).
 .It window-htile
 Current window is placed at the top of the screen, maximized
-horizontally and resized to half of the vertical screen space.
+horizontally and resized to
+.Ar htile
+(default half) of the vertical screen space.
 Other windows in its group share remaining screen space.
 .It window-vtile
 Current window is placed on the left of the screen, maximized vertically
-and resized to half of the horizontal screen space.
+and resized to
+.Ar vtile
+(default half) of the horizontal screen space.
 Other windows in its group share remaining screen space.
 .It window-move
 Move current window.
diff --git a/group.c b/group.c
index 164ef44..59eae73 100644
--- a/group.c
+++ b/group.c
@@ -35,7 +35,7 @@
 static struct group_ctx	*group_next(struct group_ctx *);
 static struct group_ctx	*group_prev(struct group_ctx *);
 static void		 group_restack(struct group_ctx *);
-static void		 group_setactive(struct group_ctx *);
+static void		 group_set_active(struct group_ctx *);
 
 void
 group_assign(struct group_ctx *gc, struct client_ctx *cc)
@@ -45,7 +45,7 @@ group_assign(struct group_ctx *gc, struct client_ctx *cc)
 
 	cc->gc = gc;
 
-	xu_ewmh_net_wm_desktop(cc);
+	xu_ewmh_set_net_wm_desktop(cc);
 }
 
 void
@@ -79,7 +79,7 @@ group_show(struct group_ctx *gc)
 			client_show(cc);
 	}
 	group_restack(gc);
-	group_setactive(gc);
+	group_set_active(gc);
 }
 
 static void
@@ -134,11 +134,11 @@ group_init(struct screen_ctx *sc, int num, const char *name)
 	TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
 
 	if (num == 1)
-		group_setactive(gc);
+		group_set_active(gc);
 }
 
 void
-group_setactive(struct group_ctx *gc)
+group_set_active(struct group_ctx *gc)
 {
 	struct screen_ctx	*sc = gc->sc;
 
@@ -190,9 +190,9 @@ group_holds_only_sticky(struct group_ctx *gc)
 		if (cc->gc != gc)
 			continue;
 		if (!(cc->flags & CLIENT_STICKY))
-			return(0);
+			return 0;
 	}
-	return(1);
+	return 1;
 }
 
 int
@@ -205,9 +205,9 @@ group_holds_only_hidden(struct group_ctx *gc)
 		if (cc->gc != gc)
 			continue;
 		if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
-			return(0);
+			return 0;
 	}
-	return(1);
+	return 1;
 }
 
 void
@@ -297,7 +297,7 @@ group_cycle(struct screen_ctx *sc, int flags)
 	if (group_holds_only_hidden(showgroup))
 		group_show(showgroup);
 	else
-		group_setactive(showgroup);
+		group_set_active(showgroup);
 }
 
 static struct group_ctx *
@@ -326,23 +326,21 @@ group_restore(struct client_ctx *cc)
 	struct screen_ctx	*sc = cc->sc;
 	struct group_ctx	*gc;
 	int			 num;
-	long			*grpnum;
+	long			 grpnum;
 
-	if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
-	    (unsigned char **)&grpnum) <= 0)
-		return(0);
+	if (!xu_ewmh_get_net_wm_desktop(cc, &grpnum))
+		return 0;
 
-	num = (*grpnum == -1) ? 0 : *grpnum;
+	num = (grpnum == -1) ? 0 : grpnum;
 	num = MIN(num, (Conf.ngroups - 1));
-	XFree(grpnum);
 
 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
 		if (gc->num == num) {
 			group_assign(gc, cc);
-			return(1);
+			return 1;
 		}
 	}
-	return(0);
+	return 0;
 }
 
 int
@@ -353,13 +351,13 @@ group_autogroup(struct client_ctx *cc)
 	struct group_ctx	*gc;
 	int			 num = -1, both_match = 0;
 
-	if (cc->ch.res_class == NULL || cc->ch.res_name == NULL)
-		return(0);
+	if (cc->res_class == NULL || cc->res_name == NULL)
+		return 0;
 
 	TAILQ_FOREACH(ag, &Conf.autogroupq, entry) {
-		if (strcmp(ag->class, cc->ch.res_class) == 0) {
+		if (strcmp(ag->class, cc->res_class) == 0) {
 			if ((ag->name != NULL) &&
-			    (strcmp(ag->name, cc->ch.res_name) == 0)) {
+			    (strcmp(ag->name, cc->res_name) == 0)) {
 				num = ag->num;
 				both_match = 1;
 			} else if (ag->name == NULL && !both_match)
@@ -370,8 +368,8 @@ group_autogroup(struct client_ctx *cc)
 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
 		if (gc->num == num) {
 			group_assign(gc, cc);
-			return(1);
+			return 1;
 		}
 	}
-	return(0);
+	return 0;
 }
diff --git a/kbfunc.c b/kbfunc.c
index 551ccc7..2d5d1ce 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -62,7 +62,7 @@ kbfunc_amount(int flags, int amt, int *mx, int *my)
 	if (flags & CWM_BIGAMOUNT)
 		amt *= CWM_FACTOR;
 
-	switch (flags & DIRECTIONMASK) {
+	switch (flags & (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)) {
 	case CWM_UP:
 		*my -= amt;
 		break;
@@ -87,8 +87,8 @@ kbfunc_ptrmove(void *ctx, struct cargs *cargs)
 
 	kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my);
 
-	xu_ptr_getpos(sc->rootwin, &x, &y);
-	xu_ptr_setpos(sc->rootwin, x + mx, y + my);
+	xu_ptr_get(sc->rootwin, &x, &y);
+	xu_ptr_set(sc->rootwin, x + mx, y + my);
 }
 
 void
@@ -135,7 +135,7 @@ kbfunc_client_move_kb(void *ctx, struct cargs *cargs)
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 	cc->geom.x += client_snapcalc(cc->geom.x,
 	    cc->geom.x + cc->geom.w + (cc->bwidth * 2),
 	    area.x, area.x + area.w, sc->snapdist);
@@ -186,7 +186,7 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
 
 			area = screen_area(sc,
 			    cc->geom.x + cc->geom.w / 2,
-			    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+			    cc->geom.y + cc->geom.h / 2, 1);
 			cc->geom.x += client_snapcalc(cc->geom.x,
 			    cc->geom.x + cc->geom.w + (cc->bwidth * 2),
 			    area.x, area.x + area.w, sc->snapdist);
@@ -250,9 +250,9 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
 		return;
 
 	client_raise(cc);
-	client_ptrsave(cc);
+	client_ptr_save(cc);
 
-	xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h);
+	xu_ptr_set(cc->win, cc->geom.w, cc->geom.h);
 
 	if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK,
 	    GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE],
@@ -272,7 +272,7 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
 
 			cc->geom.w = ev.xmotion.x;
 			cc->geom.h = ev.xmotion.y;
-			client_applysizehints(cc);
+			client_apply_sizehints(cc);
 			client_resize(cc, 1);
 			screen_prop_win_draw(sc,
 			    "%4d x %-4d", cc->dim.w, cc->dim.h);
@@ -301,7 +301,7 @@ kbfunc_client_snap(void *ctx, struct cargs *cargs)
 
 	area = screen_area(sc,
 	    cc->geom.x + cc->geom.w / 2,
-	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
+	    cc->geom.y + cc->geom.h / 2, 1);
 
 	flags = cargs->flag;
 	while (flags) {
@@ -337,7 +337,7 @@ kbfunc_client_close(void *ctx, struct cargs *cargs)
 void
 kbfunc_client_lower(void *ctx, struct cargs *cargs)
 {
-	client_ptrsave(ctx);
+	client_ptr_save(ctx);
 	client_lower(ctx);
 }
 
@@ -405,13 +405,55 @@ void
 kbfunc_client_cycle(void *ctx, struct cargs *cargs)
 {
 	struct screen_ctx	*sc = ctx;
+	struct client_ctx	*newcc, *oldcc, *prevcc;
+	int			 again = 1, flags = cargs->flag;
 
 	/* For X apps that ignore/steal events. */
 	if (cargs->xev == CWM_XEV_KEY)
 		XGrabKeyboard(X_Dpy, sc->rootwin, True,
 		    GrabModeAsync, GrabModeAsync, CurrentTime);
 
-	client_cycle(sc, cargs->flag);
+	if (TAILQ_EMPTY(&sc->clientq))
+		return;
+
+	prevcc = TAILQ_FIRST(&sc->clientq);
+	oldcc = client_current(sc);
+	if (oldcc == NULL)
+		oldcc = (flags & CWM_CYCLE_REVERSE) ?
+		    TAILQ_LAST(&sc->clientq, client_q) :
+		    TAILQ_FIRST(&sc->clientq);
+
+	newcc = oldcc;
+	while (again) {
+		again = 0;
+
+		newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) :
+		    client_next(newcc);
+
+		/* Only cycle visible and non-ignored windows. */
+		if ((newcc->flags & (CLIENT_SKIP_CYCLE)) ||
+		    ((flags & CWM_CYCLE_INGROUP) &&
+		    (newcc->gc != oldcc->gc)))
+			again = 1;
+
+		/* Is oldcc the only non-hidden window? */
+		if (newcc == oldcc) {
+			if (again)
+				return;	/* No windows visible. */
+			break;
+		}
+	}
+
+	/* Reset when cycling mod is released. XXX I hate this hack */
+	sc->cycling = 1;
+	client_ptr_save(oldcc);
+	client_raise(prevcc);
+	client_raise(newcc);
+	if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) {
+		newcc->ptr.x = newcc->geom.w / 2;
+		newcc->ptr.y = newcc->geom.h / 2;
+	}
+	client_ptr_warp(newcc);
 }
 
 void
@@ -470,20 +512,15 @@ kbfunc_menu_client(void *ctx, struct cargs *cargs)
 	struct client_ctx	*cc, *old_cc;
 	struct menu		*mi;
 	struct menu_q		 menuq;
-	int			 all = (cargs->flag & CWM_MENU_WINDOW_ALL);
 	int			 mflags = 0;
 
 	if (cargs->xev == CWM_XEV_BTN)
 		mflags |= CWM_MENU_LIST;
 
-	old_cc = client_current(sc);
-
 	TAILQ_INIT(&menuq);
 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
-		if (!all) {
-			if (cc->flags & CLIENT_HIDDEN)
-				menuq_add(&menuq, cc, NULL);
-		} else
+		if ((cargs->flag & CWM_MENU_WINDOW_ALL) ||
+		    (cc->flags & CLIENT_HIDDEN))
 			menuq_add(&menuq, cc, NULL);
 	}
 
@@ -491,9 +528,9 @@ kbfunc_menu_client(void *ctx, struct cargs *cargs)
 	    search_match_client, search_print_client)) != NULL) {
 		cc = (struct client_ctx *)mi->ctx;
 		client_show(cc);
-		if (old_cc)
-			client_ptrsave(old_cc);
-		client_ptrwarp(cc);
+		if ((old_cc = client_current(sc)) != NULL)
+			client_ptr_save(old_cc);
+		client_ptr_warp(cc);
 	}
 
 	menuq_clear(&menuq);
diff --git a/menu.c b/menu.c
index c9b4b0e..1d84ab7 100644
--- a/menu.c
+++ b/menu.c
@@ -94,7 +94,7 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
 
 	TAILQ_INIT(&resultq);
 
-	xu_ptr_getpos(sc->rootwin, &xsave, &ysave);
+	xu_ptr_get(sc->rootwin, &xsave, &ysave);
 
 	(void)memset(&mc, 0, sizeof(mc));
 	mc.sc = sc;
@@ -129,7 +129,7 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
 	    CurrentTime) != GrabSuccess) {
 		XftDrawDestroy(mc.xftdraw);
 		XDestroyWindow(X_Dpy, mc.win);
-		return(NULL);
+		return NULL;
 	}
 
 	XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
@@ -178,14 +178,14 @@ out:
 
 	XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
 	/* restore if user didn't move */
-	xu_ptr_getpos(sc->rootwin, &xcur, &ycur);
+	xu_ptr_get(sc->rootwin, &xcur, &ycur);
 	if (xcur == mc.geom.x && ycur == mc.geom.y)
-		xu_ptr_setpos(sc->rootwin, xsave, ysave);
+		xu_ptr_set(sc->rootwin, xsave, ysave);
 
 	XUngrabPointer(X_Dpy, CurrentTime);
 	XUngrabKeyboard(X_Dpy, CurrentTime);
 
-	return(mi);
+	return mi;
 }
 
 static struct menu *
@@ -213,7 +213,7 @@ menu_complete_path(struct menu_ctx *mc)
 	
 	menuq_clear(&menuq);
 
-	return(mr);
+	return mr;
 }
 
 static struct menu *
@@ -228,7 +228,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
 	wchar_t 	 wc;
 
 	if (menu_keycode(&e->xkey, &ctl, chr) < 0)
-		return(NULL);
+		return NULL;
 
 	switch (ctl) {
 	case CTL_ERASEONE:
@@ -269,7 +269,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
 			mi->dummy = 1;
 		}
 		mi->abort = 0;
-		return(mi);
+		return mi;
 	case CTL_WIPE:
 		mc->searchstr[0] = '\0';
 		mc->changed = 1;
@@ -284,7 +284,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
 			if ((mc->flags & CWM_MENU_FILE) &&
 			    (strncmp(mc->searchstr, mi->text,
 					strlen(mi->text))) == 0)
-				return(menu_complete_path(mc));
+				return menu_complete_path(mc);
 
 			/*
 			 * Put common prefix of the results into searchstr
@@ -309,7 +309,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
 		mi->text[0] = '\0';
 		mi->dummy = 1;
 		mi->abort = 1;
-		return(mi);
+		return mi;
 	default:
 		break;
 	}
@@ -327,7 +327,7 @@ menu_handle_key(XEvent *e, struct menu_ctx *mc, struct menu_q *menuq,
 		mc->listing = 0;
 	}
 
-	return(NULL);
+	return NULL;
 }
 
 static void
@@ -368,7 +368,7 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
 		mc->num++;
 	}
 
-	area = screen_area(sc, mc->geom.x, mc->geom.y, CWM_GAP);
+	area = screen_area(sc, mc->geom.x, mc->geom.y, 1);
 	area.w += area.x - Conf.bwidth * 2;
 	area.h += area.y - Conf.bwidth * 2;
 
@@ -390,7 +390,7 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
 	}
 
 	if (mc->geom.x != xsave || mc->geom.y != ysave)
-		xu_ptr_setpos(sc->rootwin, mc->geom.x, mc->geom.y);
+		xu_ptr_set(sc->rootwin, mc->geom.x, mc->geom.y);
 
 	XClearWindow(X_Dpy, mc->win);
 	XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y,
@@ -478,7 +478,7 @@ menu_handle_release(struct menu_ctx *mc, struct menu_q *resultq, int x, int y)
 		mi->text[0] = '\0';
 		mi->dummy = 1;
 	}
-	return(mi);
+	return mi;
 }
 
 static int
@@ -498,7 +498,7 @@ menu_calc_entry(struct menu_ctx *mc, int x, int y)
 	if (entry == 0)
 		entry = -1;
 
-	return(entry);
+	return entry;
 }
 
 static int
@@ -581,12 +581,12 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
 	}
 
 	if (*ctl != CTL_NONE)
-		return(0);
+		return 0;
 
 	if (XLookupString(ev, chr, 32, &ks, NULL) < 0)
-		return(-1);
+		return -1;
 
-	return(0);
+	return 0;
 }
 
 void
diff --git a/parse.y b/parse.y
index f115cd4..383b2dd 100644
--- a/parse.y
+++ b/parse.y
@@ -73,7 +73,7 @@ typedef struct {
 %token	BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
 %token	FONTNAME STICKY GAP
 %token	AUTOGROUP COMMAND IGNORE WM
-%token	YES NO BORDERWIDTH MOVEAMOUNT
+%token	YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE
 %token	COLOR SNAPDIST
 %token	ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
 %token	GROUPBORDER UNGROUPBORDER
@@ -124,6 +124,20 @@ main		: FONTNAME STRING		{
 			}
 			conf->bwidth = $2;
 		}
+		| HTILE NUMBER {
+			if ($2 < 0 || $2 > 99) {
+				yyerror("invalid htile percent");
+				YYERROR;
+			}
+			conf->htile = $2;
+		}
+		| VTILE NUMBER {
+			if ($2 < 0 || $2 > 99) {
+				yyerror("invalid vtile percent");
+				YYERROR;
+			}
+			conf->vtile = $2;
+		}
 		| MOVEAMOUNT NUMBER {
 			if ($2 < 0 || $2 > INT_MAX) {
 				yyerror("invalid movemount");
@@ -318,6 +332,7 @@ lookup(char *s)
 		{ "fontname",		FONTNAME},
 		{ "gap",		GAP},
 		{ "groupborder",	GROUPBORDER},
+		{ "htile",		HTILE},
 		{ "ignore",		IGNORE},
 		{ "inactiveborder",	INACTIVEBORDER},
 		{ "menubg",		MENUBG},
@@ -331,6 +346,7 @@ lookup(char *s)
 		{ "unbind-mouse",	UNBINDMOUSE},
 		{ "ungroupborder",	UNGROUPBORDER},
 		{ "urgencyborder",	URGENCYBORDER},
+		{ "vtile",		VTILE},
 		{ "wm",			WM},
 		{ "yes",		YES}
 	};
diff --git a/screen.c b/screen.c
index 4ef86d5..59b6eff 100644
--- a/screen.c
+++ b/screen.c
@@ -33,14 +33,13 @@
 #include "calmwm.h"
 
 static struct geom screen_apply_gap(struct screen_ctx *, struct geom);
+static void screen_scan(struct screen_ctx *);
 
 void
 screen_init(int which)
 {
 	struct screen_ctx	*sc;
-	Window			*wins, w0, w1, active = None;
-	XSetWindowAttributes	 rootattr;
-	unsigned int		 nwins, w;
+	XSetWindowAttributes	 attr;
 
 	sc = xmalloc(sizeof(*sc));
 
@@ -67,33 +66,46 @@ screen_init(int which)
 	xu_ewmh_net_number_of_desktops(sc);
 	xu_ewmh_net_showing_desktop(sc);
 	xu_ewmh_net_virtual_roots(sc);
-	active = xu_ewmh_get_net_active_window(sc);
 
-	rootattr.cursor = Conf.cursor[CF_NORMAL];
-	rootattr.event_mask = SubstructureRedirectMask |
-	    SubstructureNotifyMask | PropertyChangeMask | EnterWindowMask |
-	    LeaveWindowMask | ColormapChangeMask | BUTTONMASK;
-
-	XChangeWindowAttributes(X_Dpy, sc->rootwin,
-	    (CWEventMask | CWCursor), &rootattr);
-
-	/* Deal with existing clients. */
-	if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
-		for (w = 0; w < nwins; w++)
-			(void)client_init(wins[w], sc, (active == wins[w]));
-
-		XFree(wins);
-	}
-	screen_updatestackingorder(sc);
+	attr.cursor = Conf.cursor[CF_NORMAL];
+	attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
+	    EnterWindowMask | PropertyChangeMask | ButtonPressMask;
+	XChangeWindowAttributes(X_Dpy, sc->rootwin, (CWEventMask | CWCursor), &attr);
 
 	if (Conf.xrandr)
 		XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
 
+	screen_scan(sc);
+	screen_updatestackingorder(sc);
+
 	TAILQ_INSERT_TAIL(&Screenq, sc, entry);
 
 	XSync(X_Dpy, False);
 }
 
+static void
+screen_scan(struct screen_ctx *sc)
+{
+	struct client_ctx	 *cc, *active = NULL;
+	Window			*wins, w0, w1, rwin, cwin;
+	unsigned int		 nwins, i, mask;
+	int			 rx, ry, wx, wy;
+
+	XQueryPointer(X_Dpy, sc->rootwin, &rwin, &cwin,
+	    &rx, &ry, &wx, &wy, &mask);
+
+	if (XQueryTree(X_Dpy, sc->rootwin, &w0, &w1, &wins, &nwins)) {
+		for (i = 0; i < nwins; i++) {
+			if ((cc = client_init(wins[i], sc)) != NULL)
+				if (cc->win == cwin)
+					active = cc;
+		}
+		XFree(wins);
+	}
+	if (active)
+		client_set_active(active);
+}
+
 struct screen_ctx *
 screen_find(Window win)
 {
@@ -101,10 +113,10 @@ screen_find(Window win)
 
 	TAILQ_FOREACH(sc, &Screenq, entry) {
 		if (sc->rootwin == win)
-			return(sc);
+			return sc;
 	}
 	warnx("%s: failure win 0x%lx", __func__, win);
-	return(NULL);
+	return NULL;
 }
 
 void
@@ -138,11 +150,11 @@ region_find(struct screen_ctx *sc, int x, int y)
 			break;
 		}
 	}
-	return(rc);
+	return rc;
 }
 
 struct geom
-screen_area(struct screen_ctx *sc, int x, int y, enum apply_gap apply_gap)
+screen_area(struct screen_ctx *sc, int x, int y, int apply_gap)
 {
 	struct region_ctx	*rc;
 	struct geom		 area = sc->view;
@@ -156,7 +168,7 @@ screen_area(struct screen_ctx *sc, int x, int y, enum apply_gap apply_gap)
 	}
 	if (apply_gap)
 		area = screen_apply_gap(sc, area);
-	return(area);
+	return area;
 }
 
 void
@@ -226,7 +238,7 @@ screen_apply_gap(struct screen_ctx *sc, struct geom geom)
 	geom.w -= (sc->gap.left + sc->gap.right);
 	geom.h -= (sc->gap.top + sc->gap.bottom);
 
-	return(geom);
+	return geom;
 }
 
 /* Bring back clients which are beyond the screen. */
diff --git a/search.c b/search.c
index 06a08b7..12dde97 100644
--- a/search.c
+++ b/search.c
@@ -46,13 +46,13 @@ match_substr(char *sub, char *str, int zeroidx)
 	unsigned int	 n, flen;
 
 	if (sub == NULL || str == NULL)
-		return(0);
+		return 0;
 
 	len = strlen(str);
 	sublen = strlen(sub);
 
 	if (sublen > len)
-		return(0);
+		return 0;
 
 	if (zeroidx)
 		flen = 0;
@@ -61,9 +61,9 @@ match_substr(char *sub, char *str, int zeroidx)
 
 	for (n = 0; n <= flen; n++)
 		if (strncasecmp(sub, str + n, sublen) == 0)
-			return(1);
+			return 1;
 
-	return(0);
+	return 0;
 }
 
 void
@@ -94,7 +94,7 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
 		}
 
 		/* Match on window resource class. */
-		if ((tier < 0) && match_substr(search, cc->ch.res_class, 0))
+		if ((tier < 0) && match_substr(search, cc->res_class, 0))
 			tier = 2;
 
 		if (tier < 0)
diff --git a/util.c b/util.c
index 781308f..97b5641 100644
--- a/util.c
+++ b/util.c
@@ -53,7 +53,7 @@ u_exec(char *argstr)
 {
 #define MAXARGLEN 20
 	char	*args[MAXARGLEN], **ap = args;
-	char	**end = &args[MAXARGLEN - 1], *tmp;
+	char	**end = &args[MAXARGLEN - 2], *tmp;
 	char	*s = argstr;
 
 	while (ap < end && (*ap = strsep(&argstr, " \t")) != NULL) {
@@ -92,12 +92,12 @@ u_argv(char * const *argv)
 	char	*p;
 
 	if (argv == 0)
-		return(NULL);
+		return NULL;
 
 	for (i = 0; argv[i]; i++)
 		siz += strlen(argv[i]) + 1;
 	if (siz == 0)
-		return(NULL);
+		return NULL;
 
 	p = xmalloc(siz);
 	strlcpy(p, argv[0], siz);
@@ -105,7 +105,7 @@ u_argv(char * const *argv)
 		strlcat(p, " ", siz);
 		strlcat(p, argv[i], siz);
 	}
-	return(p);
+	return p;
 }
 
 static void
diff --git a/xevents.c b/xevents.c
index fbd93c5..5e138aa 100644
--- a/xevents.c
+++ b/xevents.c
@@ -84,13 +84,13 @@ xev_handle_maprequest(XEvent *ee)
 		return;
 
 	if ((old_cc = client_current(sc)) != NULL)
-		client_ptrsave(old_cc);
+		client_ptr_save(old_cc);
 
 	if ((cc = client_find(e->window)) == NULL)
-		cc = client_init(e->window, NULL, 0);
+		cc = client_init(e->window, NULL);
 
 	if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
-		client_ptrwarp(cc);
+		client_ptr_warp(cc);
 }
 
 static void
@@ -103,7 +103,7 @@ xev_handle_unmapnotify(XEvent *ee)
 
 	if ((cc = client_find(e->window)) != NULL) {
 		if (e->send_event) {
-			client_set_wm_state(cc, WithdrawnState);
+			xu_set_wm_state(cc->win, WithdrawnState);
 		} else {
 			if (!(cc->flags & CLIENT_HIDDEN))
 				client_remove(cc);
@@ -191,10 +191,10 @@ xev_handle_propertynotify(XEvent *ee)
 	if ((cc = client_find(e->window)) != NULL) {
 		switch (e->atom) {
 		case XA_WM_NORMAL_HINTS:
-			client_getsizehints(cc);
+			client_get_sizehints(cc);
 			break;
 		case XA_WM_NAME:
-			client_setname(cc);
+			client_set_name(cc);
 			break;
 		case XA_WM_HINTS:
 			client_wm_hints(cc);
@@ -208,7 +208,7 @@ xev_handle_propertynotify(XEvent *ee)
 			break;
 		default:
 			if (e->atom == ewmh[_NET_WM_NAME])
-				client_setname(cc);
+				client_set_name(cc);
 			break;
 		}
 	} else {
@@ -230,7 +230,7 @@ xev_handle_enternotify(XEvent *ee)
 	Last_Event_Time = e->time;
 
 	if ((cc = client_find(e->window)) != NULL)
-		client_setactive(cc);
+		client_set_active(cc);
 }
 
 static void
@@ -399,9 +399,9 @@ xev_handle_clientmessage(XEvent *ee)
 	} else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
 		if ((cc = client_find(e->window)) != NULL) {
 			if ((old_cc = client_current(NULL)) != NULL)
-				client_ptrsave(old_cc);
+				client_ptr_save(old_cc);
 			client_show(cc);
-			client_ptrwarp(cc);
+			client_ptr_warp(cc);
 		}
 	} else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
 		if ((cc = client_find(e->window)) != NULL) {
diff --git a/xmalloc.c b/xmalloc.c
index 1041648..54867ae 100644
--- a/xmalloc.c
+++ b/xmalloc.c
@@ -43,7 +43,7 @@ xmalloc(size_t siz)
 	if ((p = malloc(siz)) == NULL)
 		err(1, "malloc");
 
-	return(p);
+	return p;
 }
 
 void *
@@ -58,7 +58,7 @@ xcalloc(size_t no, size_t siz)
 	if ((p = calloc(no, siz)) == NULL)
 		err(1, "calloc");
 
-	return(p);
+	return p;
 }
 
 void *
@@ -70,7 +70,7 @@ xreallocarray(void *ptr, size_t nmemb, size_t size)
 	if (p == NULL)
 		errx(1, "xreallocarray: out of memory (new_size %zu bytes)",
 		    nmemb * size);
-	return(p);
+	return p;
 }
 
 char *
@@ -81,7 +81,7 @@ xstrdup(const char *str)
 	if ((p = strdup(str)) == NULL)
 		err(1, "strdup");
 
-	return(p);
+	return p;
 }
 
 int
@@ -94,7 +94,7 @@ xasprintf(char **ret, const char *fmt, ...)
 	i = xvasprintf(ret, fmt, ap);
 	va_end(ap);
 
-	return(i);
+	return i;
 }
 
 int
@@ -106,5 +106,5 @@ xvasprintf(char **ret, const char *fmt, va_list ap)
 	if (i == -1)
 		err(1, "vasprintf");
 
-	return(i);
+	return i;
 }
diff --git a/xutil.c b/xutil.c
index 5445357..c8f74fd 100644
--- a/xutil.c
+++ b/xutil.c
@@ -32,7 +32,7 @@
 #include "calmwm.h"
 
 void
-xu_ptr_getpos(Window win, int *x, int *y)
+xu_ptr_get(Window win, int *x, int *y)
 {
 	Window		 w0, w1;
 	int		 tmp0, tmp1;
@@ -42,13 +42,13 @@ xu_ptr_getpos(Window win, int *x, int *y)
 }
 
 void
-xu_ptr_setpos(Window win, int x, int y)
+xu_ptr_set(Window win, int x, int y)
 {
 	XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
 }
 
 int
-xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
+xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p)
 {
 	Atom		 realtype;
 	unsigned long	 n, extra;
@@ -56,16 +56,16 @@ xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
 
 	if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
 	    &realtype, &format, &n, &extra, p) != Success || *p == NULL)
-		return(-1);
+		return -1;
 
 	if (n == 0)
 		XFree(*p);
 
-	return(n);
+	return n;
 }
 
 int
-xu_getstrprop(Window win, Atom atm, char **text) {
+xu_get_strprop(Window win, Atom atm, char **text) {
 	XTextProperty	 prop;
 	char		**list;
 	int		 nitems = 0;
@@ -73,8 +73,10 @@ xu_getstrprop(Window win, Atom atm, char **text) {
 	*text = NULL;
 
 	XGetTextProperty(X_Dpy, win, &prop, atm);
-	if (!prop.nitems)
-		return(0);
+	if (!prop.nitems) {
+		XFree(prop.value);
+		return 0;
+	}
 
 	if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
 	    &nitems) == Success && nitems > 0 && *list) {
@@ -90,10 +92,101 @@ xu_getstrprop(Window win, Atom atm, char **text) {
 		}
 		XFreeStringList(list);
 	}
-
 	XFree(prop.value);
 
-	return(nitems);
+	return nitems;
+}
+
+void
+xu_send_clientmsg(Window win, Atom proto, Time ts)
+{
+	XClientMessageEvent	 cm;
+
+	(void)memset(&cm, 0, sizeof(cm));
+	cm.type = ClientMessage;
+	cm.window = win;
+	cm.message_type = cwmh[WM_PROTOCOLS];
+	cm.format = 32;
+	cm.data.l[0] = proto;
+	cm.data.l[1] = ts;
+
+	XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
+}
+
+void
+xu_get_wm_state(Window win, long *state)
+{
+	long	*p;
+
+	*state = -1;
+	if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
+	    (unsigned char **)&p) > 0) {
+		*state = *p;
+		XFree(p);
+	}
+}
+
+void
+xu_set_wm_state(Window win, long state)
+{
+	long	 data[] = { state, None };
+
+	XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
+	    PropModeReplace, (unsigned char *)data, 2);
+}
+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;
+}
+
+void
+xu_atom_init(void)
+{
+	char *cwmhints[] = {
+		"WM_STATE",
+		"WM_DELETE_WINDOW",
+		"WM_TAKE_FOCUS",
+		"WM_PROTOCOLS",
+		"_MOTIF_WM_HINTS",
+		"UTF8_STRING",
+		"WM_CHANGE_STATE",
+	};
+	char *ewmhints[] = {
+		"_NET_SUPPORTED",
+		"_NET_SUPPORTING_WM_CHECK",
+		"_NET_ACTIVE_WINDOW",
+		"_NET_CLIENT_LIST",
+		"_NET_CLIENT_LIST_STACKING",
+		"_NET_NUMBER_OF_DESKTOPS",
+		"_NET_CURRENT_DESKTOP",
+		"_NET_DESKTOP_VIEWPORT",
+		"_NET_DESKTOP_GEOMETRY",
+		"_NET_VIRTUAL_ROOTS",
+		"_NET_SHOWING_DESKTOP",
+		"_NET_DESKTOP_NAMES",
+		"_NET_WORKAREA",
+		"_NET_WM_NAME",
+		"_NET_WM_DESKTOP",
+		"_NET_CLOSE_WINDOW",
+		"_NET_WM_STATE",
+		"_NET_WM_STATE_STICKY",
+		"_NET_WM_STATE_MAXIMIZED_VERT",
+		"_NET_WM_STATE_MAXIMIZED_HORZ",
+		"_NET_WM_STATE_HIDDEN",
+		"_NET_WM_STATE_FULLSCREEN",
+		"_NET_WM_STATE_DEMANDS_ATTENTION",
+		"_NET_WM_STATE_SKIP_PAGER",
+		"_NET_WM_STATE_SKIP_TASKBAR",
+		"_CWM_WM_STATE_FREEZE",
+	};
+
+	XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
+	XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
 }
 
 /* Root Window Properties */
@@ -205,22 +298,6 @@ xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
 }
 
-Window
-xu_ewmh_get_net_active_window(struct screen_ctx *sc)
-{
-	long		*p;
-	Window		 win;
-
-	if ((xu_getprop(sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
-	    XA_WINDOW, 32, (unsigned char **)&p)) <= 0)
-		return(None);
-
-	win = (Window)*p;
-	XFree(p);
-
-	return(win);
-}
-
 void
 xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
 {
@@ -270,7 +347,7 @@ xu_ewmh_net_desktop_names(struct screen_ctx *sc)
 
 	/* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
 
-	if ((j = xu_getprop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
+	if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
 	    cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
 		prop_ret[j - 1] = '\0'; /* paranoia */
 		while (i < j) {
@@ -312,8 +389,21 @@ xu_ewmh_net_desktop_names(struct screen_ctx *sc)
 }
 
 /* Application Window Properties */
+int
+xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
+{
+	long		*p;
+
+	if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
+	    (unsigned char **)&p) <= 0)
+		return 0;
+	*n = *p;
+	XFree(p);
+	return 1;
+}
+
 void
-xu_ewmh_net_wm_desktop(struct client_ctx *cc)
+xu_ewmh_set_net_wm_desktop(struct client_ctx *cc)
 {
 	long	 num = 0xffffffff;
 
@@ -329,15 +419,15 @@ xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
 {
 	Atom	*state, *p = NULL;
 
-	if ((*n = xu_getprop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
+	if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
 	    (unsigned char **)&p)) <= 0)
-		return(NULL);
+		return NULL;
 
 	state = xreallocarray(NULL, *n, sizeof(Atom));
 	(void)memcpy(state, p, *n * sizeof(Atom));
 	XFree((char *)p);
 
-	return(state);
+	return state;
 }
 
 void
@@ -345,9 +435,9 @@ xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
     Atom first, Atom second)
 {
 	unsigned int i;
-	static struct handlers {
-		int atom;
-		int property;
+	struct handlers {
+		Atom atom;
+		int flag;
 		void (*toggle)(struct client_ctx *);
 	} handlers[] = {
 		{ _NET_WM_STATE_STICKY,
@@ -385,11 +475,11 @@ xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
 			continue;
 		switch (action) {
 		case _NET_WM_STATE_ADD:
-			if (!(cc->flags & handlers[i].property))
+			if (!(cc->flags & handlers[i].flag))
 				handlers[i].toggle(cc);
 			break;
 		case _NET_WM_STATE_REMOVE:
-			if (cc->flags & handlers[i].property)
+			if (cc->flags & handlers[i].flag)
 				handlers[i].toggle(cc);
 			break;
 		case _NET_WM_STATE_TOGGLE:
@@ -476,13 +566,3 @@ xu_ewmh_set_net_wm_state(struct client_ctx *cc)
 		XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
 	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;
-}