summary refs log tree commit diff
diff options
context:
space:
mode:
authorokan <okan>2019-03-11 15:25:46 +0000
committerokan <okan>2019-03-11 15:25:46 +0000
commit85d88f33042f7c397b75513e1561b361815caad7 (patch)
tree5ce311934b167cd4b8514edf062923fd2910eb84
parent412b0c9ef4429d48c200910ffcc8a18b60f815ea (diff)
parent9a7528f5b99bd234f724aa229cbf75fb6a813282 (diff)
downloadcwm-85d88f33042f7c397b75513e1561b361815caad7.tar.gz
cwm-85d88f33042f7c397b75513e1561b361815caad7.tar.xz
cwm-85d88f33042f7c397b75513e1561b361815caad7.zip
cvsimport
* refs/heads/master: (23 commits)
  Check the atom type on propertynotify before iterating.
  use screen_find() for xrandr crtc changes
  Find the managed screen from the parent window for client_current().
  Print window id in hex; while here, remove unnecessary newline.
  Similar to keypress event, fetch the screen from the event root window in the buttonpress handler; bail if we don't manage the screen. Allows us to find the current client based on the screen/event root.
  extend verbose logging for key/button events
  [keypress event] turns out we've been checking the wrong window for a matching client thus always falling back to client_current(); while the current client is problaby right in most cases, use event's subwindow (not window) to find the client. Bail early if this event came to us from a screen we don't manage. This is result of us grabing all keybindings off the root window instead of selectively.
  add parans for readibility
  Teach client_current() to use a screen to find the current client instead of iterating over all (fallback if no screen provided for now). Initially convert trivial uses of client_current().
  check cc->gc directly
  zip extra lines
  gc clientq inside groups, instead use the better maintained one per-screen
  shuffle deck chairs: rename group actions to match intent for clarity
  same thing as screen_find()
  Separate out the menu window from the client resize/move geom window; in each case, create and destroy on-demand. Isolate more menu specific code.
  fix a few misplaced (and misnamed) ewmh root window functions
  _NET_WORKAREA needs ngroups, so screen_update_geometry() needs to come after conf_group().
  simplify xftcolor config
  Tie group number and name together during config.
  Move the group index (desktop number) check to the only 2 callers that require checking due to ewmh.
  ...
-rw-r--r--calmwm.c18
-rw-r--r--calmwm.h33
-rw-r--r--client.c59
-rw-r--r--conf.c45
-rw-r--r--cwm.15
-rw-r--r--group.c136
-rw-r--r--kbfunc.c39
-rw-r--r--menu.c82
-rw-r--r--screen.c56
-rw-r--r--xevents.c85
-rw-r--r--xutil.c22
11 files changed, 308 insertions, 272 deletions
diff --git a/calmwm.c b/calmwm.c
index c809695..4111d95 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -55,7 +55,7 @@ main(int argc, char **argv)
 {
 	char		*display_name = NULL;
 	char		*fallback;
-	int		 ch, xfd;
+	int		 ch, xfd, nflag = 0;
 	struct pollfd	 pfd[1];
 
 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
@@ -66,7 +66,7 @@ main(int argc, char **argv)
 
 	fallback = u_argv(argv);
 	Conf.wm_argv = u_argv(argv);
-	while ((ch = getopt(argc, argv, "c:d:v")) != -1) {
+	while ((ch = getopt(argc, argv, "c:d:nv")) != -1) {
 		switch (ch) {
 		case 'c':
 			free(Conf.conf_file);
@@ -75,6 +75,9 @@ main(int argc, char **argv)
 		case 'd':
 			display_name = optarg;
 			break;
+		case 'n':
+			nflag = 1;
+			break;
 		case 'v':
 			Conf.debug++;
 			break;
@@ -90,8 +93,13 @@ main(int argc, char **argv)
 	if (signal(SIGHUP, sighdlr) == SIG_ERR)
 		err(1, "signal");
 
-	if (parse_config(Conf.conf_file, &Conf) == -1)
+	if (parse_config(Conf.conf_file, &Conf) == -1) {
 		warnx("error parsing config file");
+		if (nflag)
+			return 1;
+	}
+	if (nflag)
+		return 0;
 
 	xfd = x_init(display_name);
 	cwm_status = CWM_RUNNING;
@@ -159,8 +167,6 @@ x_teardown(void)
 			    DefaultColormap(X_Dpy, sc->which),
 			    &sc->xftcolor[i]);
 		XftFontClose(X_Dpy, sc->xftfont);
-		XftDrawDestroy(sc->menu.xftdraw);
-		XDestroyWindow(X_Dpy, sc->menu.win);
 		XUngrabKey(X_Dpy, AnyKey, AnyModifier, sc->rootwin);
 	}
 	XUngrabPointer(X_Dpy, CurrentTime);
@@ -221,7 +227,7 @@ usage(void)
 {
 	extern char	*__progname;
 
-	(void)fprintf(stderr, "usage: %s [-v] [-c file] [-d display]\n",
+	(void)fprintf(stderr, "usage: %s [-nv] [-c file] [-d display]\n",
 	    __progname);
 	exit(1);
 }
diff --git a/calmwm.h b/calmwm.h
index 01b560a..d15f39d 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -68,9 +68,6 @@ size_t strlcpy(char *, const char *, size_t);
 
 #define BUTTONMASK	(ButtonPressMask | ButtonReleaseMask)
 #define MOUSEMASK	(BUTTONMASK | PointerMotionMask)
-#define MENUMASK 	(MOUSEMASK | ButtonMotionMask | KeyPressMask | \
-			 ExposureMask)
-#define MENUGRABMASK	(MOUSEMASK | ButtonMotionMask | StructureNotifyMask)
 #define IGNOREMODMASK	(LockMask | Mod2Mask | 0x2000)
 
 /* direction/amount */
@@ -145,7 +142,6 @@ TAILQ_HEAD(ignore_q, winname);
 
 struct client_ctx {
 	TAILQ_ENTRY(client_ctx)	 entry;
-	TAILQ_ENTRY(client_ctx)	 group_entry;
 	struct screen_ctx	*sc;
 	struct group_ctx	*gc;
 	Window			 win;
@@ -211,7 +207,6 @@ struct group_ctx {
 	struct screen_ctx	*sc;
 	char			*name;
 	int			 num;
-	struct client_q		 clientq;
 };
 TAILQ_HEAD(group_q, group_ctx);
 
@@ -250,7 +245,7 @@ struct screen_ctx {
 	struct {
 		Window		 win;
 		XftDraw		*xftdraw;
-	} menu;
+	} prop;
 	XftColor		 xftcolor[CWM_COLOR_NITEMS];
 	XftFont			*xftfont;
 };
@@ -420,7 +415,7 @@ void			 usage(void);
 
 void			 client_applysizehints(struct client_ctx *);
 void			 client_config(struct client_ctx *);
-struct client_ctx	*client_current(void);
+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 *);
@@ -456,25 +451,24 @@ void			 client_toggle_skip_taskbar(struct client_ctx *);
 void			 client_toggle_sticky(struct client_ctx *);
 void			 client_toggle_vmaximize(struct client_ctx *);
 void			 client_transient(struct client_ctx *);
-void			 client_unhide(struct client_ctx *);
 void			 client_urgency(struct client_ctx *);
 void 			 client_vtile(struct client_ctx *);
 void			 client_wm_hints(struct client_ctx *);
 
-void			 group_alltoggle(struct screen_ctx *);
 void			 group_assign(struct group_ctx *, struct client_ctx *);
 int			 group_autogroup(struct client_ctx *);
 void			 group_cycle(struct screen_ctx *, int);
 void			 group_hide(struct group_ctx *);
-void			 group_hidetoggle(struct screen_ctx *, int);
 int			 group_holds_only_hidden(struct group_ctx *);
 int			 group_holds_only_sticky(struct group_ctx *);
-void			 group_init(struct screen_ctx *, int);
+void			 group_init(struct screen_ctx *, int, const char *);
 void			 group_movetogroup(struct client_ctx *, int);
 void			 group_only(struct screen_ctx *, int);
 void			 group_close(struct screen_ctx *, int);
 int			 group_restore(struct client_ctx *);
 void			 group_show(struct group_ctx *);
+void			 group_toggle(struct screen_ctx *, int);
+void			 group_toggle_all(struct screen_ctx *);
 void			 group_toggle_membership(struct client_ctx *);
 void			 group_update_names(struct screen_ctx *);
 
@@ -506,6 +500,12 @@ 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			 kbfunc_cwm_status(void *, struct cargs *);
 void			 kbfunc_ptrmove(void *, struct cargs *);
@@ -532,7 +532,7 @@ void			 kbfunc_group_toggle(void *, struct cargs *);
 void			 kbfunc_group_only(void *, struct cargs *);
 void			 kbfunc_group_close(void *, struct cargs *);
 void			 kbfunc_group_cycle(void *, struct cargs *);
-void			 kbfunc_group_alltoggle(void *, struct cargs *);
+void			 kbfunc_group_toggle_all(void *, struct cargs *);
 void			 kbfunc_menu_client(void *, struct cargs *);
 void			 kbfunc_menu_cmd(void *, struct cargs *);
 void			 kbfunc_menu_group(void *, struct cargs *);
@@ -544,10 +544,6 @@ void			 kbfunc_exec_cmd(void *, struct cargs *);
 void			 kbfunc_exec_lock(void *, struct cargs *);
 void			 kbfunc_exec_term(void *, struct cargs *);
 
-void			 menu_windraw(struct screen_ctx *, Window,
-			     const char *, ...)
-			    __attribute__((__format__ (printf, 3, 4)))
-			    __attribute__((__nonnull__ (3)));
 struct menu  		*menu_filter(struct screen_ctx *, struct menu_q *,
 			     const char *, const char *, int,
 			     void (*)(struct menu_q *, struct menu_q *, char *),
@@ -577,6 +573,7 @@ void			 conf_grab_mouse(Window);
 void			 conf_init(struct conf *);
 void			 conf_ignore(struct conf *, const char *);
 void			 conf_screen(struct screen_ctx *);
+void			 conf_group(struct screen_ctx *);
 
 void			 xev_process(void);
 
@@ -589,13 +586,13 @@ void 			 xu_xorcolor(XftColor, XftColor, XftColor *);
 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 *);
+void			 xu_ewmh_net_desktop_viewport(struct screen_ctx *);
 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_wm_desktop_viewport(struct screen_ctx *);
-void			 xu_ewmh_net_wm_number_of_desktops(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 *);
diff --git a/client.c b/client.c
index d7d34c4..39df4d3 100644
--- a/client.c
+++ b/client.c
@@ -128,7 +128,7 @@ client_init(Window win, struct screen_ctx *sc, int active)
 	if (client_get_wm_state(cc) == IconicState)
 		client_hide(cc);
 	else
-		client_unhide(cc);
+		client_show(cc);
 
 	if (mapped) {
 		if (cc->gc) {
@@ -183,9 +183,6 @@ client_remove(struct client_ctx *cc)
 	if (cc->flags & CLIENT_ACTIVE)
 		xu_ewmh_net_active_window(sc, None);
 
-	if (cc->gc != NULL)
-		TAILQ_REMOVE(&cc->gc->clientq, cc, group_entry);
-
 	while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
 		TAILQ_REMOVE(&cc->nameq, wn, entry);
 		free(wn->name);
@@ -221,7 +218,7 @@ client_setactive(struct client_ctx *cc)
 	if (cc->flags & CLIENT_WM_TAKE_FOCUS)
 		client_msg(cc, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
 
-	if ((oldcc = client_current()) != NULL) {
+	if ((oldcc = client_current(sc)) != NULL) {
 		oldcc->flags &= ~CLIENT_ACTIVE;
 		client_draw_border(oldcc);
 	}
@@ -238,16 +235,23 @@ client_setactive(struct client_ctx *cc)
 }
 
 struct client_ctx *
-client_current(void)
+client_current(struct screen_ctx *sc)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*_sc;
 	struct client_ctx	*cc;
 
-	TAILQ_FOREACH(sc, &Screenq, entry) {
+	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);
 }
@@ -527,10 +531,10 @@ client_hide(struct client_ctx *cc)
 {
 	XUnmapWindow(X_Dpy, cc->win);
 
-	if (cc->flags & CLIENT_ACTIVE)
+	if (cc->flags & CLIENT_ACTIVE) {
+		cc->flags &= ~CLIENT_ACTIVE;
 		xu_ewmh_net_active_window(cc->sc, None);
-
-	cc->flags &= ~CLIENT_ACTIVE;
+	}
 	cc->flags |= CLIENT_HIDDEN;
 	client_set_wm_state(cc, IconicState);
 }
@@ -538,15 +542,6 @@ client_hide(struct client_ctx *cc)
 void
 client_show(struct client_ctx *cc)
 {
-	if (cc->flags & CLIENT_HIDDEN)
-		client_unhide(cc);
-	else
-		client_raise(cc);
-}
-
-void
-client_unhide(struct client_ctx *cc)
-{
 	XMapRaised(X_Dpy, cc->win);
 
 	cc->flags &= ~CLIENT_HIDDEN;
@@ -691,7 +686,7 @@ client_cycle(struct screen_ctx *sc, int flags)
 		return;
 
 	prevcc = TAILQ_FIRST(&sc->clientq);
-	oldcc = client_current();
+	oldcc = client_current(sc);
 	if (oldcc == NULL)
 		oldcc = (flags & CWM_CYCLE_REVERSE) ?
 		    TAILQ_LAST(&sc->clientq, client_q) :
@@ -972,12 +967,11 @@ void
 client_htile(struct client_ctx *cc)
 {
 	struct client_ctx	*ci;
-	struct group_ctx 	*gc = cc->gc;
 	struct screen_ctx 	*sc = cc->sc;
 	struct geom 		 area;
 	int 			 i, n, mh, x, w, h;
 
-	if (!gc)
+	if (!cc->gc)
 		return;
 	i = n = 0;
 
@@ -985,7 +979,9 @@ client_htile(struct client_ctx *cc)
 	    cc->geom.x + cc->geom.w / 2,
 	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
 
-	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(ci, &sc->clientq, entry) {
+		if (ci->gc != cc->gc)
+			continue;
 		if (ci->flags & CLIENT_HIDDEN ||
 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
 		    ci->geom.x < area.x ||
@@ -1014,7 +1010,9 @@ client_htile(struct client_ctx *cc)
 	x = area.x;
 	w = area.w / n;
 	h = area.h - mh;
-	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(ci, &sc->clientq, entry) {
+		if (ci->gc != cc->gc)
+			continue;
 		if (ci->flags & CLIENT_HIDDEN ||
 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
 		    ci->geom.x < area.x ||
@@ -1040,12 +1038,11 @@ void
 client_vtile(struct client_ctx *cc)
 {
 	struct client_ctx	*ci;
-	struct group_ctx 	*gc = cc->gc;
 	struct screen_ctx 	*sc = cc->sc;
 	struct geom 		 area;
 	int 			 i, n, mw, y, w, h;
 
-	if (!gc)
+	if (!cc->gc)
 		return;
 	i = n = 0;
 
@@ -1053,7 +1050,9 @@ client_vtile(struct client_ctx *cc)
 	    cc->geom.x + cc->geom.w / 2,
 	    cc->geom.y + cc->geom.h / 2, CWM_GAP);
 
-	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(ci, &sc->clientq, entry) {
+		if (ci->gc != cc->gc)
+			continue;
 		if (ci->flags & CLIENT_HIDDEN ||
 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
 		    ci->geom.x < area.x ||
@@ -1082,7 +1081,9 @@ client_vtile(struct client_ctx *cc)
 	y = area.y;
 	h = area.h / n;
 	w = area.w - mw;
-	TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(ci, &sc->clientq, entry) {
+		if (ci->gc != cc->gc)
+			continue;
 		if (ci->flags & CLIENT_HIDDEN ||
 		    ci->flags & CLIENT_IGNORE || (ci == cc) ||
 		    ci->geom.x < area.x ||
diff --git a/conf.c b/conf.c
index 22d4fc5..e42c18f 100644
--- a/conf.c
+++ b/conf.c
@@ -36,6 +36,21 @@ static const char	*conf_bind_getmask(const char *, unsigned int *);
 static void		 conf_unbind_key(struct conf *, struct bind_ctx *);
 static void		 conf_unbind_mouse(struct conf *, struct bind_ctx *);
 
+static const struct {
+	int		 num;
+	const char	*name;
+} group_binds[] = {
+	{ 0, "nogroup" },
+	{ 1, "one" },
+	{ 2, "two" },
+	{ 3, "three" },
+	{ 4, "four" },
+	{ 5, "five" },
+	{ 6, "six" },
+	{ 7, "seven" },
+	{ 8, "eight" },
+	{ 9, "nine" },
+};
 static int cursor_binds[] = {
 	XC_left_ptr,		/* CF_NORMAL */
 	XC_fleur,		/* CF_MOVE */
@@ -124,7 +139,7 @@ static const struct {
 
 	{ FUNC_SC(group-cycle, group_cycle, (CWM_CYCLE_FORWARD)) },
 	{ FUNC_SC(group-rcycle, group_cycle, (CWM_CYCLE_REVERSE)) },
-	{ FUNC_SC(group-toggle-all, group_alltoggle, 0) },
+	{ FUNC_SC(group-toggle-all, group_toggle_all, 0) },
 	{ FUNC_SC(group-toggle-1, group_toggle, 1) },
 	{ FUNC_SC(group-toggle-2, group_toggle, 2) },
 	{ FUNC_SC(group-toggle-3, group_toggle, 3) },
@@ -266,7 +281,7 @@ conf_init(struct conf *c)
 	c->bwidth = 1;
 	c->mamount = 1;
 	c->snapdist = 0;
-	c->ngroups = 10;
+	c->ngroups = 0;
 	c->nameqlen = 5;
 
 	TAILQ_INIT(&c->ignoreq);
@@ -478,28 +493,26 @@ conf_screen(struct screen_ctx *sc)
 				warnx("XftColorAllocValue: %s", Conf.color[i]);
 			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 {
+		if (!XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
+		    Conf.color[i], &sc->xftcolor[i])) {
 			warnx("XftColorAllocName: %s", Conf.color[i]);
 			XftColorAllocName(X_Dpy, sc->visual, sc->colormap,
 			    color_binds[i], &sc->xftcolor[i]);
 		}
 	}
 
-	sc->menu.win = 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);
+	conf_grab_kbd(sc->rootwin);
+}
 
-	sc->menu.xftdraw = XftDrawCreate(X_Dpy, sc->menu.win,
-	    sc->visual, sc->colormap);
-	if (sc->menu.xftdraw == NULL)
-		errx(1, "%s: XftDrawCreate", __func__);
+void
+conf_group(struct screen_ctx *sc)
+{
+	unsigned int	 i;
 
-	conf_grab_kbd(sc->rootwin);
+	for (i = 0; i < nitems(group_binds); i++) {
+		group_init(sc, group_binds[i].num, group_binds[i].name);
+		Conf.ngroups++;
+	}
 }
 
 static const char *
diff --git a/cwm.1 b/cwm.1
index a603100..7a3f49f 100644
--- a/cwm.1
+++ b/cwm.1
@@ -23,7 +23,7 @@
 .Sh SYNOPSIS
 .\" For a program:  program [-abc] file ...
 .Nm cwm
-.Op Fl v
+.Op Fl nv
 .Op Fl c Ar file
 .Op Fl d Ar display
 .Sh DESCRIPTION
@@ -48,6 +48,9 @@ however,
 will continue to process the rest of the configuration file.
 .It Fl d Ar display
 Specify the display to use.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
 .It Fl v
 Verbose mode.
 Multiple
diff --git a/group.c b/group.c
index 3088ae7..164ef44 100644
--- a/group.c
+++ b/group.c
@@ -37,37 +37,30 @@ static struct group_ctx	*group_prev(struct group_ctx *);
 static void		 group_restack(struct group_ctx *);
 static void		 group_setactive(struct group_ctx *);
 
-const char *num_to_name[] = {
-	"nogroup", "one", "two", "three", "four", "five", "six",
-	"seven", "eight", "nine"
-};
-
 void
 group_assign(struct group_ctx *gc, struct client_ctx *cc)
 {
-	if (cc->gc != NULL)
-		TAILQ_REMOVE(&cc->gc->clientq, cc, group_entry);
-
 	if ((gc != NULL) && (gc->num == 0))
 		gc = NULL;
 
 	cc->gc = gc;
 
-	if (cc->gc != NULL)
-		TAILQ_INSERT_TAIL(&gc->clientq, cc, group_entry);
-
 	xu_ewmh_net_wm_desktop(cc);
 }
 
 void
 group_hide(struct group_ctx *gc)
 {
+	struct screen_ctx	*sc = gc->sc;
 	struct client_ctx	*cc;
 
 	screen_updatestackingorder(gc->sc);
 
-	TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
-		if (!(cc->flags & CLIENT_STICKY))
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
+		if (!(cc->flags & CLIENT_STICKY) &&
+		    !(cc->flags & CLIENT_HIDDEN))
 			client_hide(cc);
 	}
 }
@@ -75,13 +68,16 @@ group_hide(struct group_ctx *gc)
 void
 group_show(struct group_ctx *gc)
 {
+	struct screen_ctx	*sc = gc->sc;
 	struct client_ctx	*cc;
 
-	TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
-		if (!(cc->flags & CLIENT_STICKY))
-			client_unhide(cc);
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
+		if (!(cc->flags & CLIENT_STICKY) &&
+		     (cc->flags & CLIENT_HIDDEN))
+			client_show(cc);
 	}
-
 	group_restack(gc);
 	group_setactive(gc);
 }
@@ -89,19 +85,24 @@ group_show(struct group_ctx *gc)
 static void
 group_restack(struct group_ctx *gc)
 {
+	struct screen_ctx	*sc = gc->sc;
 	struct client_ctx	*cc;
 	Window			*winlist;
 	int			 i, lastempty = -1;
 	int			 nwins = 0, highstack = 0;
 
-	TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
 		if (cc->stackingorder > highstack)
 			highstack = cc->stackingorder;
 	}
 	winlist = xreallocarray(NULL, (highstack + 1), sizeof(*winlist));
 
 	/* Invert the stacking order for XRestackWindows(). */
-	TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
 		winlist[highstack - cc->stackingorder] = cc->win;
 		nwins++;
 	}
@@ -122,16 +123,14 @@ group_restack(struct group_ctx *gc)
 }
 
 void
-group_init(struct screen_ctx *sc, int num)
+group_init(struct screen_ctx *sc, int num, const char *name)
 {
 	struct group_ctx	*gc;
 
 	gc = xmalloc(sizeof(*gc));
 	gc->sc = sc;
-	gc->name = xstrdup(num_to_name[num]);
+	gc->name = xstrdup(name);
 	gc->num = num;
-	TAILQ_INIT(&gc->clientq);
-
 	TAILQ_INSERT_TAIL(&sc->groupq, gc, entry);
 
 	if (num == 1)
@@ -154,19 +153,15 @@ group_movetogroup(struct client_ctx *cc, int idx)
 	struct screen_ctx	*sc = cc->sc;
 	struct group_ctx	*gc;
 
-	if (idx < 0 || idx >= Conf.ngroups)
-		return;
-
 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
-		if (gc->num == idx)
-			break;
+		if (gc->num == idx) {
+			if (cc->gc == gc)
+				return;
+			if (gc->num != 0 && group_holds_only_hidden(gc))
+				client_hide(cc);
+			group_assign(gc, cc);
+		}
 	}
-
-	if (cc->gc == gc)
-		return;
-	if (gc->num != 0 && group_holds_only_hidden(gc))
-		client_hide(cc);
-	group_assign(gc, cc);
 }
 
 void
@@ -175,23 +170,25 @@ group_toggle_membership(struct client_ctx *cc)
 	struct screen_ctx	*sc = cc->sc;
 	struct group_ctx	*gc = sc->group_active;
 
-	if (gc == cc->gc) {
+	if (cc->gc == gc) {
 		group_assign(NULL, cc);
 		cc->flags |= CLIENT_UNGROUP;
 	} else {
 		group_assign(gc, cc);
 		cc->flags |= CLIENT_GROUP;
 	}
-
 	client_draw_border(cc);
 }
 
 int
 group_holds_only_sticky(struct group_ctx *gc)
 {
+	struct screen_ctx	*sc = gc->sc;
 	struct client_ctx	*cc;
 
-	TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
 		if (!(cc->flags & CLIENT_STICKY))
 			return(0);
 	}
@@ -201,9 +198,12 @@ group_holds_only_sticky(struct group_ctx *gc)
 int
 group_holds_only_hidden(struct group_ctx *gc)
 {
+	struct screen_ctx	*sc = gc->sc;
 	struct client_ctx	*cc;
 
-	TAILQ_FOREACH(cc, &gc->clientq, group_entry) {
+	TAILQ_FOREACH(cc, &sc->clientq, entry) {
+		if (cc->gc != gc)
+			continue;
 		if (!(cc->flags & (CLIENT_HIDDEN | CLIENT_STICKY)))
 			return(0);
 	}
@@ -211,42 +211,45 @@ group_holds_only_hidden(struct group_ctx *gc)
 }
 
 void
-group_hidetoggle(struct screen_ctx *sc, int idx)
+group_only(struct screen_ctx *sc, int idx)
 {
 	struct group_ctx	*gc;
 
-	if (idx < 0 || idx >= Conf.ngroups)
-		return;
-
 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
 		if (gc->num == idx)
-			break;
+			group_show(gc);
+		else
+			group_hide(gc);
 	}
+}
 
-	if (group_holds_only_hidden(gc))
-		group_show(gc);
-	else {
-		group_hide(gc);
-		/* make clients stick to empty group */
-		if (TAILQ_EMPTY(&gc->clientq))
-			group_setactive(gc);
+void
+group_toggle(struct screen_ctx *sc, int idx)
+{
+	struct group_ctx	*gc;
+
+	TAILQ_FOREACH(gc, &sc->groupq, entry) {
+		if (gc->num == idx) {
+			if (group_holds_only_hidden(gc))
+				group_show(gc);
+			else
+				group_hide(gc);
+		}
 	}
 }
 
 void
-group_only(struct screen_ctx *sc, int idx)
+group_toggle_all(struct screen_ctx *sc)
 {
 	struct group_ctx	*gc;
 
-	if (idx < 0 || idx >= Conf.ngroups)
-		return;
-
 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
-		if (gc->num == idx)
+		if (sc->hideall)
 			group_show(gc);
 		else
 			group_hide(gc);
 	}
+	sc->hideall = !sc->hideall;
 }
 
 void
@@ -255,13 +258,13 @@ group_close(struct screen_ctx *sc, int idx)
 	struct group_ctx	*gc;
 	struct client_ctx	*cc;
 
-	if (idx < 0 || idx >= Conf.ngroups)
-		return;
-
 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
 		if (gc->num == idx) {
-			TAILQ_FOREACH(cc, &gc->clientq, group_entry)
+			TAILQ_FOREACH(cc, &sc->clientq, entry) {
+				if (cc->gc != gc)
+					continue;
 				client_close(cc);
+			}
 		}
 	}
 }
@@ -286,7 +289,6 @@ group_cycle(struct screen_ctx *sc, int flags)
 		else if (!group_holds_only_hidden(newgc))
 			group_hide(newgc);
 	}
-
 	if (showgroup == NULL)
 		return;
 
@@ -318,20 +320,6 @@ group_prev(struct group_ctx *gc)
 	    newgc : TAILQ_LAST(&sc->groupq, group_q));
 }
 
-void
-group_alltoggle(struct screen_ctx *sc)
-{
-	struct group_ctx	*gc;
-
-	TAILQ_FOREACH(gc, &sc->groupq, entry) {
-		if (sc->hideall)
-			group_show(gc);
-		else
-			group_hide(gc);
-	}
-	sc->hideall = !sc->hideall;
-}
-
 int
 group_restore(struct client_ctx *cc)
 {
diff --git a/kbfunc.c b/kbfunc.c
index 3da28c3..bb5ac37 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -169,8 +169,8 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
 	    CurrentTime) != GrabSuccess)
 		return;
 
-	menu_windraw(sc, cc->win, "%4d, %-4d", cc->geom.x, cc->geom.y);
-
+	screen_prop_win_create(sc, cc->win);
+	screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y);
 	while (move) {
 		XMaskEvent(X_Dpy, MOUSEMASK, &ev);
 		switch (ev.type) {
@@ -193,8 +193,8 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
 			    cc->geom.y + cc->geom.h + (cc->bwidth * 2),
 			    area.y, area.y + area.h, sc->snapdist);
 			client_move(cc);
-			menu_windraw(sc, cc->win,
-			    "%4d, %-4d", cc->geom.x, cc->geom.y);
+			screen_prop_win_draw(sc,
+			    "%+5d%+5d", cc->geom.x, cc->geom.y);
 			break;
 		case ButtonRelease:
 			move = 0;
@@ -203,8 +203,7 @@ kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
 	}
 	if (ltime)
 		client_move(cc);
-	XUnmapWindow(X_Dpy, sc->menu.win);
-	XReparentWindow(X_Dpy, sc->menu.win, sc->rootwin, 0, 0);
+	screen_prop_win_destroy(sc);
 	XUngrabPointer(X_Dpy, CurrentTime);
 }
 
@@ -258,7 +257,8 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
 	    CurrentTime) != GrabSuccess)
 		return;
 
-	menu_windraw(sc, cc->win, "%4d x %-4d", cc->dim.w, cc->dim.h);
+	screen_prop_win_create(sc, cc->win);
+	screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h);
 	while (resize) {
 		XMaskEvent(X_Dpy, MOUSEMASK, &ev);
 		switch (ev.type) {
@@ -272,7 +272,7 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
 			cc->geom.h = ev.xmotion.y;
 			client_applysizehints(cc);
 			client_resize(cc, 1);
-			menu_windraw(sc, cc->win,
+			screen_prop_win_draw(sc,
 			    "%4d x %-4d", cc->dim.w, cc->dim.h);
 			break;
 		case ButtonRelease:
@@ -282,8 +282,7 @@ kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
 	}
 	if (ltime)
 		client_resize(cc, 1);
-	XUnmapWindow(X_Dpy, sc->menu.win);
-	XReparentWindow(X_Dpy, sc->menu.win, sc->rootwin, 0, 0);
+	screen_prop_win_destroy(sc);
 	XUngrabPointer(X_Dpy, CurrentTime);
 
 	/* Make sure the pointer stays within the window. */
@@ -432,15 +431,21 @@ kbfunc_client_movetogroup(void *ctx, struct cargs *cargs)
 }
 
 void
+kbfunc_group_only(void *ctx, struct cargs *cargs)
+{
+	group_only(ctx, cargs->flag);
+}
+
+void
 kbfunc_group_toggle(void *ctx, struct cargs *cargs)
 {
-	group_hidetoggle(ctx, cargs->flag);
+	group_toggle(ctx, cargs->flag);
 }
 
 void
-kbfunc_group_only(void *ctx, struct cargs *cargs)
+kbfunc_group_toggle_all(void *ctx, struct cargs *cargs)
 {
-	group_only(ctx, cargs->flag);
+	group_toggle_all(ctx);
 }
 
 void
@@ -456,12 +461,6 @@ kbfunc_group_cycle(void *ctx, struct cargs *cargs)
 }
 
 void
-kbfunc_group_alltoggle(void *ctx, struct cargs *cargs)
-{
-	group_alltoggle(ctx);
-}
-
-void
 kbfunc_menu_client(void *ctx, struct cargs *cargs)
 {
 	struct screen_ctx	*sc = ctx;
@@ -474,7 +473,7 @@ kbfunc_menu_client(void *ctx, struct cargs *cargs)
 	if (cargs->xev == CWM_XEV_BTN)
 		mflags |= CWM_MENU_LIST;
 
-	old_cc = client_current();
+	old_cc = client_current(sc);
 
 	TAILQ_INIT(&menuq);
 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
diff --git a/menu.c b/menu.c
index c7d2c93..2d6ba27 100644
--- a/menu.c
+++ b/menu.c
@@ -37,6 +37,10 @@
 #define PROMPT_SCHAR	"\xc2\xbb"
 #define PROMPT_ECHAR	"\xc2\xab"
 
+#define MENUMASK 	(MOUSEMASK | ButtonMotionMask | KeyPressMask | \
+			 ExposureMask)
+#define MENUGRABMASK	(MOUSEMASK | ButtonMotionMask | StructureNotifyMask)
+
 enum ctltype {
 	CTL_NONE = -1,
 	CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
@@ -45,6 +49,9 @@ enum ctltype {
 
 struct menu_ctx {
 	struct screen_ctx	*sc;
+	Window			 win;
+	XftDraw			*xftdraw;
+	struct geom		 geom;
 	char			 searchstr[MENU_MAXENTRY + 1];
 	char			 dispstr[MENU_MAXENTRY*2 + 1];
 	char			 promptstr[MENU_MAXENTRY + 1];
@@ -55,7 +62,6 @@ struct menu_ctx {
 	int			 entry;
 	int			 num;
 	int 			 flags;
-	struct geom		 geom;
 	void (*match)(struct menu_q *, struct menu_q *, char *);
 	void (*print)(struct menu *, int);
 };
@@ -108,27 +114,34 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
 	else
 		mc.searchstr[0] = '\0';
 
-	XSelectInput(X_Dpy, sc->menu.win, MENUMASK);
-	XMapRaised(X_Dpy, sc->menu.win);
+	mc.win = 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);
+	mc.xftdraw = XftDrawCreate(X_Dpy, mc.win,
+	    sc->visual, sc->colormap);
 
-	if (XGrabPointer(X_Dpy, sc->menu.win, False, MENUGRABMASK,
+	XSelectInput(X_Dpy, mc.win, MENUMASK);
+	XMapRaised(X_Dpy, mc.win);
+
+	if (XGrabPointer(X_Dpy, mc.win, False, MENUGRABMASK,
 	    GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_QUESTION],
 	    CurrentTime) != GrabSuccess) {
-		XUnmapWindow(X_Dpy, sc->menu.win);
-		return(NULL);
+		XftDrawDestroy(mc.xftdraw);
+		XDestroyWindow(X_Dpy, mc.win);
 	}
 
 	XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
-	XSetInputFocus(X_Dpy, sc->menu.win, RevertToPointerRoot, CurrentTime);
+	XSetInputFocus(X_Dpy, mc.win, RevertToPointerRoot, CurrentTime);
 
 	/* make sure keybindings don't remove keys from the menu stream */
-	XGrabKeyboard(X_Dpy, sc->menu.win, True,
+	XGrabKeyboard(X_Dpy, mc.win, True,
 	    GrabModeAsync, GrabModeAsync, CurrentTime);
 
 	for (;;) {
 		mc.changed = 0;
 
-		XWindowEvent(X_Dpy, sc->menu.win, MENUMASK, &e);
+		XWindowEvent(X_Dpy, mc.win, MENUMASK, &e);
 
 		switch (e.type) {
 		case KeyPress:
@@ -159,16 +172,16 @@ out:
 		mi = NULL;
 	}
 
-	XSelectInput(X_Dpy, sc->menu.win, NoEventMask);
+	XftDrawDestroy(mc.xftdraw);
+	XDestroyWindow(X_Dpy, mc.win);
+
 	XSetInputFocus(X_Dpy, focuswin, focusrevert, CurrentTime);
 	/* restore if user didn't move */
 	xu_ptr_getpos(sc->rootwin, &xcur, &ycur);
 	if (xcur == mc.geom.x && ycur == mc.geom.y)
 		xu_ptr_setpos(sc->rootwin, xsave, ysave);
-	XUngrabPointer(X_Dpy, CurrentTime);
 
-	XMoveResizeWindow(X_Dpy, sc->menu.win, 0, 0, 1, 1);
-	XUnmapWindow(X_Dpy, sc->menu.win);
+	XUngrabPointer(X_Dpy, CurrentTime);
 	XUngrabKeyboard(X_Dpy, CurrentTime);
 
 	return(mi);
@@ -378,12 +391,12 @@ 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);
 
-	XClearWindow(X_Dpy, sc->menu.win);
-	XMoveResizeWindow(X_Dpy, sc->menu.win, mc->geom.x, mc->geom.y,
+	XClearWindow(X_Dpy, mc->win);
+	XMoveResizeWindow(X_Dpy, mc->win, mc->geom.x, mc->geom.y,
 	    mc->geom.w, mc->geom.h);
 
 	n = 1;
-	XftDrawStringUtf8(sc->menu.xftdraw,
+	XftDrawStringUtf8(mc->xftdraw,
 	    &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
 	    0, sc->xftfont->ascent,
 	    (const FcChar8*)mc->dispstr, strlen(mc->dispstr));
@@ -395,7 +408,7 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
 		if (mc->geom.y + y > area.h)
 			break;
 
-		XftDrawStringUtf8(sc->menu.xftdraw,
+		XftDrawStringUtf8(mc->xftdraw,
 		    &sc->xftcolor[CWM_COLOR_MENU_FONT], sc->xftfont,
 		    0, y,
 		    (const FcChar8*)mi->print, strlen(mi->print));
@@ -420,11 +433,11 @@ menu_draw_entry(struct menu_ctx *mc, struct menu_q *resultq,
 		return;
 
 	color = (active) ? CWM_COLOR_MENU_FG : CWM_COLOR_MENU_BG;
-	XftDrawRect(sc->menu.xftdraw, &sc->xftcolor[color], 0,
+	XftDrawRect(mc->xftdraw, &sc->xftcolor[color], 0,
 	    (sc->xftfont->height + 1) * entry, mc->geom.w,
 	    (sc->xftfont->height + 1) + sc->xftfont->descent);
 	color = (active) ? CWM_COLOR_MENU_FONT_SEL : CWM_COLOR_MENU_FONT;
-	XftDrawStringUtf8(sc->menu.xftdraw,
+	XftDrawStringUtf8(mc->xftdraw,
 	    &sc->xftcolor[color], sc->xftfont,
 	    0, (sc->xftfont->height + 1) * entry + sc->xftfont->ascent + 1,
 	    (const FcChar8*)mi->print, strlen(mi->print));
@@ -604,34 +617,3 @@ menuq_clear(struct menu_q *mq)
 		free(mi);
 	}
 }
-
-void
-menu_windraw(struct screen_ctx *sc, Window win, const char *fmt, ...)
-{
-	va_list			 ap;
-	int			 i;
-	char			*text;
-	XGlyphInfo		 extents;
-
-	va_start(ap, fmt);
-	i = vasprintf(&text, fmt, ap);
-	va_end(ap);
-
-	if (i < 0 || text == NULL)
-		err(1, "vasprintf");
-
-	XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
-	    strlen(text), &extents);
-
-	XReparentWindow(X_Dpy, sc->menu.win, win, 0, 0);
-	XMoveResizeWindow(X_Dpy, sc->menu.win, 0, 0,
-	    extents.xOff, sc->xftfont->height);
-	XMapWindow(X_Dpy, sc->menu.win);
-	XClearWindow(X_Dpy, sc->menu.win);
-
-	XftDrawStringUtf8(sc->menu.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
-	    sc->xftfont, 0, sc->xftfont->ascent + 1,
-	    (const FcChar8*)text, strlen(text));
-
-	free(text);
-}
diff --git a/screen.c b/screen.c
index 2c0b8c1..7565703 100644
--- a/screen.c
+++ b/screen.c
@@ -24,6 +24,7 @@
 #include <err.h>
 #include <errno.h>
 #include <limits.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,7 +41,6 @@ screen_init(int which)
 	Window			*wins, w0, w1, active = None;
 	XSetWindowAttributes	 rootattr;
 	unsigned int		 nwins, w;
-	int			 i;
 
 	sc = xmalloc(sizeof(*sc));
 
@@ -60,14 +60,11 @@ screen_init(int which)
 	xu_ewmh_net_supported(sc);
 	xu_ewmh_net_supported_wm_check(sc);
 
+	conf_group(sc);
 	screen_update_geometry(sc);
 
-	for (i = 0; i < Conf.ngroups; i++)
-		group_init(sc, i);
-
 	xu_ewmh_net_desktop_names(sc);
-	xu_ewmh_net_wm_desktop_viewport(sc);
-	xu_ewmh_net_wm_number_of_desktops(sc);
+	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);
@@ -106,7 +103,7 @@ screen_find(Window win)
 		if (sc->rootwin == win)
 			return(sc);
 	}
-	warnx("%s: failure win 0x%lu\n", __func__, win);
+	warnx("%s: failure win 0x%lx", __func__, win);
 	return(NULL);
 }
 
@@ -217,6 +214,7 @@ screen_update_geometry(struct screen_ctx *sc)
 	}
 
 	xu_ewmh_net_desktop_geometry(sc);
+	xu_ewmh_net_desktop_viewport(sc);
 	xu_ewmh_net_workarea(sc);
 }
 
@@ -253,3 +251,47 @@ screen_assert_clients_within(struct screen_ctx *sc)
 		}
 	}
 }
+
+void
+screen_prop_win_create(struct screen_ctx *sc, Window win)
+{
+	sc->prop.win = XCreateSimpleWindow(X_Dpy, win, 0, 0, 1, 1, 0,
+	    sc->xftcolor[CWM_COLOR_MENU_BG].pixel,
+	    sc->xftcolor[CWM_COLOR_MENU_BG].pixel);
+	sc->prop.xftdraw = XftDrawCreate(X_Dpy, sc->prop.win,
+	    sc->visual, sc->colormap);
+
+	XMapWindow(X_Dpy, sc->prop.win);
+}
+
+void
+screen_prop_win_destroy(struct screen_ctx *sc)
+{
+	XftDrawDestroy(sc->prop.xftdraw);
+	XDestroyWindow(X_Dpy, sc->prop.win);
+}
+
+void
+screen_prop_win_draw(struct screen_ctx *sc, const char *fmt, ...)
+{
+	va_list			 ap;
+	int			 i;
+	char			*text;
+	XGlyphInfo		 extents;
+
+	va_start(ap, fmt);
+	i = vasprintf(&text, fmt, ap);
+	va_end(ap);
+	if (i < 0 || text == NULL)
+		err(1, "vasprintf");
+
+	XftTextExtentsUtf8(X_Dpy, sc->xftfont, (const FcChar8*)text,
+	    strlen(text), &extents);
+	XResizeWindow(X_Dpy, sc->prop.win, extents.xOff, sc->xftfont->height);
+	XClearWindow(X_Dpy, sc->prop.win);
+	XftDrawStringUtf8(sc->prop.xftdraw, &sc->xftcolor[CWM_COLOR_MENU_FONT],
+	    sc->xftfont, 0, sc->xftfont->ascent + 1,
+	    (const FcChar8*)text, strlen(text));
+
+	free(text);
+}
diff --git a/xevents.c b/xevents.c
index 8b29315..c8d85d4 100644
--- a/xevents.c
+++ b/xevents.c
@@ -75,11 +75,15 @@ static void
 xev_handle_maprequest(XEvent *ee)
 {
 	XMapRequestEvent	*e = &ee->xmaprequest;
-	struct client_ctx	*cc = NULL, *old_cc;
+	struct screen_ctx	*sc;
+	struct client_ctx	*cc, *old_cc;
 
-	LOG_DEBUG3("window: 0x%lx", e->window);
+	LOG_DEBUG3("parent: 0x%lx window: 0x%lx", e->parent, e->window);
 
-	if ((old_cc = client_current()) != NULL)
+	if ((sc = screen_find(e->parent)) == NULL)
+		return;
+
+	if ((old_cc = client_current(sc)) != NULL)
 		client_ptrsave(old_cc);
 
 	if ((cc = client_find(e->window)) == NULL)
@@ -207,11 +211,9 @@ xev_handle_propertynotify(XEvent *ee)
 			break;
 		}
 	} else {
-		TAILQ_FOREACH(sc, &Screenq, entry) {
-			if (sc->rootwin == e->window) {
-				if (e->atom == ewmh[_NET_DESKTOP_NAMES])
-					xu_ewmh_net_desktop_names(sc);
-			}
+		if (e->atom == ewmh[_NET_DESKTOP_NAMES])  {
+			if ((sc = screen_find(e->window)) != NULL)
+				xu_ewmh_net_desktop_names(sc);
 		}
 	}
 }
@@ -238,7 +240,11 @@ xev_handle_buttonpress(XEvent *ee)
 	struct screen_ctx	*sc;
 	struct bind_ctx		*mb;
 
-	LOG_DEBUG3("window: 0x%lx", e->window);
+	LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
+	    e->root, e->window, e->subwindow);
+
+	if ((sc = screen_find(e->root)) == NULL)
+		return;
 
 	e->state &= ~IGNOREMODMASK;
 
@@ -246,22 +252,17 @@ xev_handle_buttonpress(XEvent *ee)
 		if (e->button == mb->press.button && e->state == mb->modmask)
 			break;
 	}
-
 	if (mb == NULL)
 		return;
 	mb->cargs->xev = CWM_XEV_BTN;
 	switch (mb->context) {
 	case CWM_CONTEXT_CC:
 		if (((cc = client_find(e->window)) == NULL) &&
-		    (cc = client_current()) == NULL)
+		    ((cc = client_current(sc)) == NULL))
 			return;
 		(*mb->callback)(cc, mb->cargs);
 		break;
 	case CWM_CONTEXT_SC:
-		if (e->window != e->root)
-			return;
-		if ((sc = screen_find(e->window)) == NULL)
-			return;
 		(*mb->callback)(sc, mb->cargs);
 		break;
 	case CWM_CONTEXT_NONE:
@@ -276,7 +277,8 @@ xev_handle_buttonrelease(XEvent *ee)
 	XButtonEvent		*e = &ee->xbutton;
 	struct client_ctx	*cc;
 
-	LOG_DEBUG3("window: 0x%lx", ee->xbutton.window);
+	LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
+	    e->root, e->window, e->subwindow);
 
 	if ((cc = client_find(e->window)) != NULL) {
 		if (cc->flags & (CLIENT_ACTIVE | CLIENT_HIGHLIGHT)) {
@@ -296,7 +298,11 @@ xev_handle_keypress(XEvent *ee)
 	KeySym			 keysym, skeysym;
 	unsigned int		 modshift;
 
-	LOG_DEBUG3("window: 0x%lx", e->window);
+	LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
+	    e->root, e->window, e->subwindow);
+
+	if ((sc = screen_find(e->root)) == NULL)
+		return;
 
 	keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
 	skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
@@ -315,20 +321,17 @@ xev_handle_keypress(XEvent *ee)
 		if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym))
 			break;
 	}
-
 	if (kb == NULL)
 		return;
 	kb->cargs->xev = CWM_XEV_KEY;
 	switch (kb->context) {
 	case CWM_CONTEXT_CC:
-		if (((cc = client_find(e->window)) == NULL) &&
-		    (cc = client_current()) == NULL)
+		if (((cc = client_find(e->subwindow)) == NULL) &&
+		    ((cc = client_current(sc)) == NULL))
 			return;
 		(*kb->callback)(cc, kb->cargs);
 		break;
 	case CWM_CONTEXT_SC:
-		if ((sc = screen_find(e->window)) == NULL)
-			return;
 		(*kb->callback)(sc, kb->cargs);
 		break;
 	case CWM_CONTEXT_NONE:
@@ -349,7 +352,8 @@ xev_handle_keyrelease(XEvent *ee)
 	KeySym			 keysym;
 	unsigned int		 i;
 
-	LOG_DEBUG3("window: 0x%lx", e->window);
+	LOG_DEBUG3("root: 0x%lx window: 0x%lx subwindow: 0x%lx",
+	    e->root, e->window, e->subwindow);
 
 	if ((sc = screen_find(e->root)) == NULL)
 		return;
@@ -357,7 +361,7 @@ xev_handle_keyrelease(XEvent *ee)
 	keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
 	for (i = 0; i < nitems(modkeys); i++) {
 		if (keysym == modkeys[i]) {
-			if ((cc = client_current()) != NULL) {
+			if ((cc = client_current(sc)) != NULL) {
 				if (sc->cycling) {
 					sc->cycling = 0;
 					client_mtf(cc);
@@ -393,7 +397,7 @@ 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)
+			if ((old_cc = client_current(NULL)) != NULL)
 				client_ptrsave(old_cc);
 			client_show(cc);
 			client_ptrwarp(cc);
@@ -408,7 +412,9 @@ xev_handle_clientmessage(XEvent *ee)
 			if (e->data.l[0] == (unsigned long)-1)
 				group_movetogroup(cc, 0);
 			else
-				group_movetogroup(cc, e->data.l[0]);
+				if (e->data.l[0] >= 0 &&
+				    e->data.l[0] < Conf.ngroups)
+					group_movetogroup(cc, e->data.l[0]);
 		}
 	} else if (e->message_type == ewmh[_NET_WM_STATE]) {
 		if ((cc = client_find(e->window)) != NULL) {
@@ -417,7 +423,9 @@ xev_handle_clientmessage(XEvent *ee)
 		}
 	} else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) {
 		if ((sc = screen_find(e->window)) != NULL) {
-			group_only(sc, e->data.l[0]);
+			if (e->data.l[0] >= 0 &&
+			    e->data.l[0] < Conf.ngroups)
+				group_only(sc, e->data.l[0]);
 		}
 	}
 }
@@ -425,20 +433,17 @@ xev_handle_clientmessage(XEvent *ee)
 static void
 xev_handle_randr(XEvent *ee)
 {
-	XRRScreenChangeNotifyEvent	*rev = (XRRScreenChangeNotifyEvent *)ee;
+	XRRScreenChangeNotifyEvent	*e = (XRRScreenChangeNotifyEvent *)ee;
 	struct screen_ctx		*sc;
-	int				 i;
 
-	LOG_DEBUG3("new size: %d/%d", rev->width, rev->height);
+	LOG_DEBUG3("size: %d/%d", e->width, e->height);
 
-	i = XRRRootToScreen(X_Dpy, rev->root);
-	TAILQ_FOREACH(sc, &Screenq, entry) {
-		if (sc->which == i) {
-			XRRUpdateConfiguration(ee);
-			screen_update_geometry(sc);
-			screen_assert_clients_within(sc);
-		}
-	}
+	if ((sc = screen_find(e->root)) == NULL)
+		return;
+
+	XRRUpdateConfiguration(ee);
+	screen_update_geometry(sc);
+	screen_assert_clients_within(sc);
 }
 
 /*
@@ -479,9 +484,9 @@ xev_process(void)
 
 	while (XPending(X_Dpy)) {
 		XNextEvent(X_Dpy, &e);
-		if (e.type - Conf.xrandr_event_base == RRScreenChangeNotify)
+		if ((e.type - Conf.xrandr_event_base) == RRScreenChangeNotify)
 			xev_handle_randr(&e);
-		else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
+		else if ((e.type < LASTEvent) && (xev_handlers[e.type] != NULL))
 			(*xev_handlers[e.type])(&e);
 	}
 }
diff --git a/xutil.c b/xutil.c
index 1b604c7..5445357 100644
--- a/xutil.c
+++ b/xutil.c
@@ -129,6 +129,16 @@ xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
 }
 
 void
+xu_ewmh_net_desktop_viewport(struct screen_ctx *sc)
+{
+	long	 viewports[2] = {0, 0};
+
+	/* We don't support large desktops, so this is (0, 0). */
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
+}
+
+void
 xu_ewmh_net_workarea(struct screen_ctx *sc)
 {
 	unsigned long	*workarea;
@@ -212,17 +222,7 @@ xu_ewmh_get_net_active_window(struct screen_ctx *sc)
 }
 
 void
-xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *sc)
-{
-	long	 viewports[2] = {0, 0};
-
-	/* We don't support large desktops, so this is (0, 0). */
-	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
-	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
-}
-
-void
-xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
+xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
 {
 	long	 ndesks = Conf.ngroups;