summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-12-16 13:42:52 +0100
committerChristian Neukirchen <chneukirchen@gmail.com>2016-12-16 13:48:38 +0100
commit775ba78311768b7a944db95273822c0a567dc171 (patch)
tree13ea9cb9db8e9121cc80e9cce4186a925fb889e2
parentdcb741d2b1378432e226bbeea274c2e5e2508c2d (diff)
parent8b4666cb926121481905b68bf93d6e81c307e9e3 (diff)
downloadcwm-775ba78311768b7a944db95273822c0a567dc171.tar.gz
cwm-775ba78311768b7a944db95273822c0a567dc171.tar.xz
cwm-775ba78311768b7a944db95273822c0a567dc171.zip
cvsimport
* refs/heads/master:
  stray newlines
  Add search_print_text(), a default callback for mi->print in menu_filter(). While here, normalize the remaining search_print_* argument paramters.
  Consistent use of menuq_add for ssh menu.
  Now that dim.{x,y} are available early, use them before requiring a MotionNotify event.
  Set dim.{x,y} during client_init and update on resize, instead of (re)calculating only when applying hints.
  'window-search' is spelled 'menu-window'; the former snuck in during the conversion('menu-window' already existed and was properlly documented); found the hard way by sthen@ while trying to convert.
  Fold unbinding functions into one for each, key and mouse; plugs a leak when unbinding a mouse button bound to a command.
  use the correct type
  Tame the number of 'exec' and 'path' search_match wrappers. No functional change now, though more can likely go later, losing the (paritally complete or incomplete/broken) argument completion bits.
  Switch ssh menu to search_match_text; like group/window/cmd menus, use only a substring match. The previous matching is only intended for the exec menus.
  Change 'menu-window' to display all windows; then add 'menu-window-hidden' for the previous behaviour of 'menu-window'.  'menu-window' becomes the default binding; use 'bind-mouse "1" menu-window-hidden' to restore old behaviour for those who prefer.
  Normalize bind function names, based on a few categories: window, group, menu and pointer.
  Use an additional check with lstat(2) when d_type is unknown.
  revert previous; upcoming changes will hopefully deal with these more naturally.
  Add a wrapper based upon xevent handlers around client move/resize for key and mouse bindings.
  Define callbacks, then default bindings.
  Reorganize for upcoming changes.
  Remove the (8) default bindings for pointer move since they conflict with default bindings for emacs, which wins; the feature remains and can be bound to whatever users wish with cwmrc(5).
-rw-r--r--calmwm.h9
-rw-r--r--client.c8
-rw-r--r--conf.c786
-rw-r--r--cwm.19
-rw-r--r--cwmrc.5315
-rw-r--r--kbfunc.c21
-rw-r--r--menu.c10
-rw-r--r--mousefunc.c4
-rw-r--r--parse.y51
-rw-r--r--search.c36
10 files changed, 614 insertions, 635 deletions
diff --git a/calmwm.h b/calmwm.h
index faac8bb..eb64dd7 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -282,6 +282,8 @@ enum menu_exec {
 #define CWM_MENU_DUMMY		0x0001
 #define CWM_MENU_FILE		0x0002
 #define CWM_MENU_LIST		0x0004
+#define CWM_MENU_WINDOW_ALL	0x0008
+#define CWM_MENU_WINDOW_HIDDEN	0x0010
 
 struct menu {
 	TAILQ_ENTRY(menu)	 entry;
@@ -461,15 +463,14 @@ void			 search_match_client(struct menu_q *, struct menu_q *,
 			     char *);
 void			 search_match_exec(struct menu_q *, struct menu_q *,
 			     char *);
-void			 search_match_exec_path(struct menu_q *,
-			     struct menu_q *, char *);
-void			 search_match_path_any(struct menu_q *, struct menu_q *,
+void			 search_match_path(struct menu_q *, struct menu_q *,
 			     char *);
 void			 search_match_text(struct menu_q *, struct menu_q *,
 			     char *);
 void			 search_print_client(struct menu *, int);
 void			 search_print_cmd(struct menu *, int);
 void			 search_print_group(struct menu *, int);
+void			 search_print_text(struct menu *, int);
 
 struct region_ctx	*region_find(struct screen_ctx *, int, int);
 struct geom		 screen_apply_gap(struct screen_ctx *, struct geom);
@@ -539,7 +540,7 @@ int			 parse_config(const char *, struct conf *);
 void			 conf_atoms(void);
 void			 conf_autogroup(struct conf *, int, const char *,
 			     const char *);
-int			 conf_bind_kbd(struct conf *, const char *,
+int			 conf_bind_key(struct conf *, const char *,
     			     const char *);
 int			 conf_bind_mouse(struct conf *, const char *,
     			     const char *);
diff --git a/client.c b/client.c
index 7975bb9..377abdb 100644
--- a/client.c
+++ b/client.c
@@ -91,6 +91,8 @@ client_init(Window win, struct screen_ctx *sc, int active)
 	cc->geom.y = wattr.y;
 	cc->geom.w = wattr.width;
 	cc->geom.h = wattr.height;
+	cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
+	cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
 	cc->ptr.x = cc->geom.w / 2;
 	cc->ptr.y = cc->geom.h / 2;
 
@@ -424,6 +426,8 @@ client_resize(struct client_ctx *cc, int reset)
 
 	XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x,
 	    cc->geom.y, cc->geom.w, cc->geom.h);
+	cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
+	cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
 	client_config(cc);
 }
 
@@ -877,9 +881,6 @@ client_applysizehints(struct client_ctx *cc)
 		cc->geom.w = MIN(cc->geom.w, cc->hint.maxw);
 	if (cc->hint.maxh)
 		cc->geom.h = MIN(cc->geom.h, cc->hint.maxh);
-
-	cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
-	cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
 }
 
 static void
@@ -1085,4 +1086,3 @@ client_set_wm_state(struct client_ctx *cc, long state)
 	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 cebfc07..15d3217 100644
--- a/conf.c
+++ b/conf.c
@@ -34,27 +34,330 @@
 
 static const char	*conf_bind_getmask(const char *, unsigned int *);
 static void		 conf_cmd_remove(struct conf *, const char *);
-static void		 conf_unbind_kbd(struct conf *, struct bind_ctx *);
+static void		 conf_unbind_key(struct conf *, struct bind_ctx *);
 static void		 conf_unbind_mouse(struct conf *, struct bind_ctx *);
 
+static int cursor_binds[] = {
+	XC_left_ptr,		/* CF_NORMAL */
+	XC_fleur,		/* CF_MOVE */
+	XC_bottom_right_corner,	/* CF_RESIZE */
+	XC_question_arrow,	/* CF_QUESTION */
+};
+static const char *color_binds[] = {
+	"#CCCCCC",		/* CWM_COLOR_BORDER_ACTIVE */
+	"#666666",		/* CWM_COLOR_BORDER_INACTIVE */
+	"#FC8814",		/* CWM_COLOR_BORDER_URGENCY */
+	"blue",			/* CWM_COLOR_BORDER_GROUP */
+	"red",			/* CWM_COLOR_BORDER_UNGROUP */
+	"black",		/* CWM_COLOR_MENU_FG */
+	"white",		/* CWM_COLOR_MENU_BG */
+	"black",		/* CWM_COLOR_MENU_FONT */
+	"",			/* CWM_COLOR_MENU_FONT_SEL */
+};
+static const struct {
+	const char	*tag;
+	void		 (*handler)(void *, union arg *, enum xev);
+	enum context	 context;
+	union arg	 argument;
+} name_to_func[] = {
+	{ "window-menu-label", kbfunc_menu_client_label, CWM_CONTEXT_CC, {0} },
+	{ "window-lower", kbfunc_client_lower, CWM_CONTEXT_CC, {0} },
+	{ "window-raise", kbfunc_client_raise, CWM_CONTEXT_CC, {0} },
+	{ "window-hide", kbfunc_client_hide, CWM_CONTEXT_CC, {0} },
+	{ "window-delete", kbfunc_client_delete, CWM_CONTEXT_CC, {0} },
+	{ "window-htile", kbfunc_client_htile, CWM_CONTEXT_CC, {0} },
+	{ "window-vtile", kbfunc_client_vtile, CWM_CONTEXT_CC, {0} },
+	{ "window-stick", kbfunc_client_toggle_sticky, CWM_CONTEXT_CC, {0} },
+	{ "window-fullscreen", kbfunc_client_toggle_fullscreen, CWM_CONTEXT_CC, {0} },
+	{ "window-maximize", kbfunc_client_toggle_maximize, CWM_CONTEXT_CC, {0} },
+	{ "window-vmaximize", kbfunc_client_toggle_vmaximize, CWM_CONTEXT_CC, {0} },
+	{ "window-hmaximize", kbfunc_client_toggle_hmaximize, CWM_CONTEXT_CC, {0} },
+	{ "window-freeze", kbfunc_client_toggle_freeze, CWM_CONTEXT_CC, {0} },
+	{ "window-cycle", kbfunc_client_cycle, CWM_CONTEXT_SC,
+	    {.i = (CWM_CYCLE_FORWARD)} },
+	{ "window-rcycle", kbfunc_client_cycle, CWM_CONTEXT_SC,
+	    {.i = (CWM_CYCLE_REVERSE)} },
+	{ "window-cycle-ingroup", kbfunc_client_cycle, CWM_CONTEXT_SC,
+	    {.i = (CWM_CYCLE_FORWARD | CWM_CYCLE_INGROUP)} },
+	{ "window-rcycle-ingroup", kbfunc_client_cycle, CWM_CONTEXT_SC,
+	    {.i = (CWM_CYCLE_REVERSE | CWM_CYCLE_INGROUP)} },
+	{ "window-group", kbfunc_client_toggle_group, CWM_CONTEXT_CC, {0} },
+	{ "window-movetogroup-1", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 1} },
+	{ "window-movetogroup-2", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 2} },
+	{ "window-movetogroup-3", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 3} },
+	{ "window-movetogroup-4", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 4} },
+	{ "window-movetogroup-5", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 5} },
+	{ "window-movetogroup-6", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 6} },
+	{ "window-movetogroup-7", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 7} },
+	{ "window-movetogroup-8", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 8} },
+	{ "window-movetogroup-9", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 9} },
+
+	{ "window-move", mousefunc_client_move, CWM_CONTEXT_CC, {0} },
+	{ "window-move-up", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_UP)} },
+	{ "window-move-down", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_DOWN)} },
+	{ "window-move-right", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_RIGHT)} },
+	{ "window-move-left", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_LEFT)} },
+	{ "window-move-up-big", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_UP | CWM_BIGAMOUNT)} },
+	{ "window-move-down-big", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_DOWN | CWM_BIGAMOUNT)} },
+	{ "window-move-right-big", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_RIGHT | CWM_BIGAMOUNT)} },
+	{ "window-move-left-big", kbfunc_client_move, CWM_CONTEXT_CC,
+	    {.i = (CWM_LEFT | CWM_BIGAMOUNT)} },
+	{ "window-resize", mousefunc_client_resize, CWM_CONTEXT_CC, {0} },
+	{ "window-resize-up", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_UP)} },
+	{ "window-resize-down", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_DOWN)} },
+	{ "window-resize-right", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_RIGHT)} },
+	{ "window-resize-left", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_LEFT)} },
+	{ "window-resize-up-big", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_UP | CWM_BIGAMOUNT)} },
+	{ "window-resize-down-big", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_DOWN | CWM_BIGAMOUNT)} },
+	{ "window-resize-right-big", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_RIGHT | CWM_BIGAMOUNT)} },
+	{ "window-resize-left-big", kbfunc_client_resize, CWM_CONTEXT_CC,
+	    {.i = (CWM_LEFT | CWM_BIGAMOUNT)} },
+
+	{ "group-cycle", kbfunc_group_cycle, CWM_CONTEXT_SC,
+	    {.i = (CWM_CYCLE_FORWARD)} },
+	{ "group-rcycle", kbfunc_group_cycle, CWM_CONTEXT_SC,
+	    {.i = (CWM_CYCLE_REVERSE)} },
+	{ "group-toggle-all", kbfunc_group_alltoggle, CWM_CONTEXT_SC, {0} },
+	{ "group-toggle-1", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 1} },
+	{ "group-toggle-2", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 2} },
+	{ "group-toggle-3", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 3} },
+	{ "group-toggle-4", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 4} },
+	{ "group-toggle-5", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 5} },
+	{ "group-toggle-6", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 6} },
+	{ "group-toggle-7", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 7} },
+	{ "group-toggle-8", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 8} },
+	{ "group-toggle-9", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 9} },
+	{ "group-only-1", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 1} },
+	{ "group-only-2", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 2} },
+	{ "group-only-3", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 3} },
+	{ "group-only-4", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 4} },
+	{ "group-only-5", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 5} },
+	{ "group-only-6", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 6} },
+	{ "group-only-7", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 7} },
+	{ "group-only-8", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 8} },
+	{ "group-only-9", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 9} },
+
+	{ "pointer-move-up", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_UP)} },
+	{ "pointer-move-down", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_DOWN)} },
+	{ "pointer-move-left", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_LEFT)} },
+	{ "pointer-move-right", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_RIGHT)} },
+	{ "pointer-move-up-big", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_UP | CWM_BIGAMOUNT)} },
+	{ "pointer-move-down-big", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_DOWN | CWM_BIGAMOUNT)} },
+	{ "pointer-move-left-big", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_LEFT | CWM_BIGAMOUNT)} },
+	{ "pointer-move-right-big", kbfunc_ptrmove, CWM_CONTEXT_SC,
+	    {.i = (CWM_RIGHT | CWM_BIGAMOUNT)} },
+
+	{ "menu-cmd", kbfunc_menu_cmd, CWM_CONTEXT_SC, {0} },
+	{ "menu-group", kbfunc_menu_group, CWM_CONTEXT_SC, {0} },
+	{ "menu-ssh", kbfunc_menu_ssh, CWM_CONTEXT_SC, {0} },
+	{ "menu-window", kbfunc_menu_client, CWM_CONTEXT_SC,
+	    {.i = CWM_MENU_WINDOW_ALL} },
+	{ "menu-window-hidden", kbfunc_menu_client, CWM_CONTEXT_SC,
+	    {.i = CWM_MENU_WINDOW_HIDDEN} },
+	{ "menu-exec", kbfunc_menu_exec, CWM_CONTEXT_SC,
+	    {.i = CWM_MENU_EXEC_EXEC} },
+	{ "menu-exec-wm", kbfunc_menu_exec, CWM_CONTEXT_SC,
+	    {.i = CWM_MENU_EXEC_WM} },
+
+	{ "terminal", kbfunc_exec_term, CWM_CONTEXT_SC, {0} },
+	{ "lock", kbfunc_exec_lock, CWM_CONTEXT_SC, {0} },
+	{ "restart", kbfunc_cwm_status, CWM_CONTEXT_SC, {.i = CWM_EXEC_WM} },
+	{ "quit", kbfunc_cwm_status, CWM_CONTEXT_SC, {.i = CWM_QUIT} },
+
+};
+static unsigned int ignore_mods[] = {
+	0, LockMask, Mod2Mask, Mod2Mask | LockMask
+};
+static const struct {
+	const char	ch;
+	int		mask;
+} bind_mods[] = {
+	{ 'C',	ControlMask },
+	{ 'M',	Mod1Mask },
+	{ '4',	Mod4Mask },
+	{ 'S',	ShiftMask },
+};
+static const struct {
+	const char	*key;
+	const char	*func;
+} key_binds[] = {
+	{ "CM-Return",	"terminal" },
+	{ "CM-Delete",	"lock" },
+	{ "M-question",	"menu-exec" },
+	{ "CM-w",	"menu-exec-wm" },
+	{ "M-period",	"menu-ssh" },
+	{ "M-Return",	"window-hide" },
+	{ "M-Down",	"window-lower" },
+	{ "M-Up",	"window-raise" },
+	{ "M-slash",	"menu-window" },
+	{ "C-slash",	"menu-cmd" },
+	{ "M-Tab",	"window-cycle" },
+	{ "MS-Tab",	"window-rcycle" },
+	{ "CM-n",	"window-menu-label" },
+	{ "CM-x",	"window-delete" },
+	{ "CM-a",	"group-toggle-all" },
+	{ "CM-0",	"group-toggle-all" },
+	{ "CM-1",	"group-toggle-1" },
+	{ "CM-2",	"group-toggle-2" },
+	{ "CM-3",	"group-toggle-3" },
+	{ "CM-4",	"group-toggle-4" },
+	{ "CM-5",	"group-toggle-5" },
+	{ "CM-6",	"group-toggle-6" },
+	{ "CM-7",	"group-toggle-7" },
+	{ "CM-8",	"group-toggle-8" },
+	{ "CM-9",	"group-toggle-9" },
+	{ "M-Right",	"group-cycle" },
+	{ "M-Left",	"group-rcycle" },
+	{ "CM-g",	"window-group" },
+	{ "CM-f",	"window-fullscreen" },
+	{ "CM-m",	"window-maximize" },
+	{ "CM-s",	"window-stick" },
+	{ "CM-equal",	"window-vmaximize" },
+	{ "CMS-equal",	"window-hmaximize" },
+	{ "CMS-f",	"window-freeze" },
+	{ "CMS-r",	"restart" },
+	{ "CMS-q",	"quit" },
+	{ "M-h",	"window-move-left" },
+	{ "M-j",	"window-move-down" },
+	{ "M-k",	"window-move-up" },
+	{ "M-l",	"window-move-right" },
+	{ "MS-h",	"window-move-left-big" },
+	{ "MS-j",	"window-move-down-big" },
+	{ "MS-k",	"window-move-up-big" },
+	{ "MS-l",	"window-move-right-big" },
+	{ "CM-h",	"window-resize-left" },
+	{ "CM-j",	"window-resize-down" },
+	{ "CM-k",	"window-resize-up" },
+	{ "CM-l",	"window-resize-right" },
+	{ "CMS-h",	"window-resize-left-big" },
+	{ "CMS-j",	"window-resize-down-big" },
+	{ "CMS-k",	"window-resize-up-big" },
+	{ "CMS-l",	"window-resize-right-big" },
+},
+mouse_binds[] = {
+	{ "1",		"menu-window" },
+	{ "2",		"menu-group" },
+	{ "3",		"menu-cmd" },
+	{ "M-1",	"window-move" },
+	{ "CM-1",	"window-group" },
+	{ "M-2",	"window-resize" },
+	{ "M-3",	"window-lower" },
+	{ "CMS-3",	"window-hide" },
+};
+
+void
+conf_init(struct conf *c)
+{
+	unsigned int	i;
+
+	c->stickygroups = 0;
+	c->bwidth = 1;
+	c->mamount = 1;
+	c->snapdist = 0;
+	c->ngroups = 10;
+	c->nameqlen = 5;
+
+	TAILQ_INIT(&c->ignoreq);
+	TAILQ_INIT(&c->cmdq);
+	TAILQ_INIT(&c->keybindq);
+	TAILQ_INIT(&c->autogroupq);
+	TAILQ_INIT(&c->mousebindq);
+
+	for (i = 0; i < nitems(key_binds); i++)
+		conf_bind_key(c, key_binds[i].key, key_binds[i].func);
+
+	for (i = 0; i < nitems(mouse_binds); i++)
+		conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func);
+
+	for (i = 0; i < nitems(color_binds); i++)
+		c->color[i] = xstrdup(color_binds[i]);
+
+	conf_cmd_add(c, "lock", "xlock");
+	conf_cmd_add(c, "term", "xterm");
+
+	(void)snprintf(c->known_hosts, sizeof(c->known_hosts), "%s/%s",
+	    homedir, ".ssh/known_hosts");
+
+	c->font = xstrdup("sans-serif:pixelsize=14:bold");
+	c->wmname = xstrdup("CWM");
+}
+
+void
+conf_clear(struct conf *c)
+{
+	struct autogroup	*ag;
+	struct bind_ctx		*kb, *mb;
+	struct winname		*wn;
+	struct cmd_ctx		*cmd;
+	int			 i;
+
+	while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) {
+		TAILQ_REMOVE(&c->cmdq, cmd, entry);
+		free(cmd->name);
+		free(cmd);
+	}
+	while ((kb = TAILQ_FIRST(&c->keybindq)) != NULL) {
+		TAILQ_REMOVE(&c->keybindq, kb, entry);
+		free(kb);
+	}
+	while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) {
+		TAILQ_REMOVE(&c->autogroupq, ag, entry);
+		free(ag->class);
+		free(ag->name);
+		free(ag);
+	}
+	while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) {
+		TAILQ_REMOVE(&c->ignoreq, wn, entry);
+		free(wn->name);
+		free(wn);
+	}
+	while ((mb = TAILQ_FIRST(&c->mousebindq)) != NULL) {
+		TAILQ_REMOVE(&c->mousebindq, mb, entry);
+		free(mb);
+	}
+	for (i = 0; i < CWM_COLOR_NITEMS; i++)
+		free(c->color[i]);
+
+	free(c->font);
+	free(c->wmname);
+}
+
 int
 conf_cmd_add(struct conf *c, const char *name, const char *path)
 {
 	struct cmd_ctx	*cmd;
 
 	cmd = xmalloc(sizeof(*cmd));
-
 	cmd->name = xstrdup(name);
 	if (strlcpy(cmd->path, path, sizeof(cmd->path)) >= sizeof(cmd->path)) {
 		free(cmd->name);
 		free(cmd);
 		return(0);
 	}
-
 	conf_cmd_remove(c, name);
 
 	TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry);
-
 	return(1);
 }
 
@@ -71,6 +374,7 @@ conf_cmd_remove(struct conf *c, const char *name)
 		}
 	}
 }
+
 void
 conf_autogroup(struct conf *c, int num, const char *name, const char *class)
 {
@@ -78,7 +382,6 @@ conf_autogroup(struct conf *c, int num, const char *name, const char *class)
 	char			*p;
 
 	ag = xmalloc(sizeof(*ag));
-
 	if ((p = strchr(class, ',')) == NULL) {
 		if (name == NULL)
 			ag->name = NULL;
@@ -88,7 +391,6 @@ conf_autogroup(struct conf *c, int num, const char *name, const char *class)
 		ag->class = xstrdup(class);
 	} else {
 		*(p++) = '\0';
-
 		if (name == NULL)
 			ag->name = xstrdup(class);
 		else
@@ -97,7 +399,6 @@ conf_autogroup(struct conf *c, int num, const char *name, const char *class)
 		ag->class = xstrdup(p);
 	}
 	ag->num = num;
-
 	TAILQ_INSERT_TAIL(&c->autogroupq, ag, entry);
 }
 
@@ -111,17 +412,30 @@ conf_ignore(struct conf *c, const char *name)
 	TAILQ_INSERT_TAIL(&c->ignoreq, wn, entry);
 }
 
-static const char *color_binds[] = {
-	"#CCCCCC",	/* CWM_COLOR_BORDER_ACTIVE */
-	"#666666",	/* CWM_COLOR_BORDER_INACTIVE */
-	"#FC8814",	/* CWM_COLOR_BORDER_URGENCY */
-	"blue",		/* CWM_COLOR_BORDER_GROUP */
-	"red",		/* CWM_COLOR_BORDER_UNGROUP */
-	"black",	/* CWM_COLOR_MENU_FG */
-	"white",	/* CWM_COLOR_MENU_BG */
-	"black",	/* CWM_COLOR_MENU_FONT */
-	"",		/* CWM_COLOR_MENU_FONT_SEL */
-};
+void
+conf_cursor(struct conf *c)
+{
+	unsigned int	 i;
+
+	for (i = 0; i < nitems(cursor_binds); i++)
+		c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]);
+}
+
+void
+conf_client(struct client_ctx *cc)
+{
+	struct winname	*wn;
+	int		 ignore = 0;
+
+	TAILQ_FOREACH(wn, &Conf.ignoreq, entry) {
+		if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) {
+			ignore = 1;
+			break;
+		}
+	}
+	cc->bwidth = (ignore) ? 0 : Conf.bwidth;
+	cc->flags |= (ignore) ? CLIENT_IGNORE : 0;
+}
 
 void
 conf_screen(struct screen_ctx *sc)
@@ -174,322 +488,6 @@ conf_screen(struct screen_ctx *sc)
 	conf_grab_kbd(sc->rootwin);
 }
 
-static const struct {
-	const char	*key;
-	const char	*func;
-} kbd_binds[] = {
-	{ "CM-Return",	"terminal" },
-	{ "CM-Delete",	"lock" },
-	{ "M-question",	"exec" },
-	{ "CM-w",	"exec_wm" },
-	{ "M-period",	"ssh" },
-	{ "M-Return",	"hide" },
-	{ "M-Down",	"lower" },
-	{ "M-Up",	"raise" },
-	{ "M-slash",	"search" },
-	{ "C-slash",	"menusearch" },
-	{ "M-Tab",	"cycle" },
-	{ "MS-Tab",	"rcycle" },
-	{ "CM-n",	"label" },
-	{ "CM-x",	"delete" },
-	{ "CM-a",	"nogroup" },
-	{ "CM-0",	"nogroup" },
-	{ "CM-1",	"group1" },
-	{ "CM-2",	"group2" },
-	{ "CM-3",	"group3" },
-	{ "CM-4",	"group4" },
-	{ "CM-5",	"group5" },
-	{ "CM-6",	"group6" },
-	{ "CM-7",	"group7" },
-	{ "CM-8",	"group8" },
-	{ "CM-9",	"group9" },
-	{ "M-Right",	"cyclegroup" },
-	{ "M-Left",	"rcyclegroup" },
-	{ "CM-g",	"grouptoggle" },
-	{ "CM-f",	"fullscreen" },
-	{ "CM-m",	"maximize" },
-	{ "CM-s",	"stick" },
-	{ "CM-equal",	"vmaximize" },
-	{ "CMS-equal",	"hmaximize" },
-	{ "CMS-f",	"freeze" },
-	{ "CMS-r",	"restart" },
-	{ "CMS-q",	"quit" },
-	{ "M-h",	"moveleft" },
-	{ "M-j",	"movedown" },
-	{ "M-k",	"moveup" },
-	{ "M-l",	"moveright" },
-	{ "MS-h",	"bigmoveleft" },
-	{ "MS-j",	"bigmovedown" },
-	{ "MS-k",	"bigmoveup" },
-	{ "MS-l",	"bigmoveright" },
-	{ "CM-h",	"resizeleft" },
-	{ "CM-j",	"resizedown" },
-	{ "CM-k",	"resizeup" },
-	{ "CM-l",	"resizeright" },
-	{ "CMS-h",	"bigresizeleft" },
-	{ "CMS-j",	"bigresizedown" },
-	{ "CMS-k",	"bigresizeup" },
-	{ "CMS-l",	"bigresizeright" },
-	{ "C-Left",	"ptrmoveleft" },
-	{ "C-Down",	"ptrmovedown" },
-	{ "C-Up",	"ptrmoveup" },
-	{ "C-Right",	"ptrmoveright" },
-	{ "CS-Left",	"bigptrmoveleft" },
-	{ "CS-Down",	"bigptrmovedown" },
-	{ "CS-Up",	"bigptrmoveup" },
-	{ "CS-Right",	"bigptrmoveright" },
-},
-mouse_binds[] = {
-	{ "1",		"menu_unhide" },
-	{ "2",		"menu_group" },
-	{ "3",		"menu_cmd" },
-	{ "M-1",	"window_move" },
-	{ "CM-1",	"window_grouptoggle" },
-	{ "M-2",	"window_resize" },
-	{ "M-3",	"window_lower" },
-	{ "CMS-3",	"window_hide" },
-};
-
-void
-conf_init(struct conf *c)
-{
-	unsigned int	i;
-
-	c->stickygroups = 0;
-	c->bwidth = 1;
-	c->mamount = 1;
-	c->snapdist = 0;
-	c->ngroups = 10;
-	c->nameqlen = 5;
-
-	TAILQ_INIT(&c->ignoreq);
-	TAILQ_INIT(&c->cmdq);
-	TAILQ_INIT(&c->keybindq);
-	TAILQ_INIT(&c->autogroupq);
-	TAILQ_INIT(&c->mousebindq);
-
-	for (i = 0; i < nitems(kbd_binds); i++)
-		conf_bind_kbd(c, kbd_binds[i].key, kbd_binds[i].func);
-
-	for (i = 0; i < nitems(mouse_binds); i++)
-		conf_bind_mouse(c, mouse_binds[i].key, mouse_binds[i].func);
-
-	for (i = 0; i < nitems(color_binds); i++)
-		c->color[i] = xstrdup(color_binds[i]);
-
-	conf_cmd_add(c, "lock", "xlock");
-	conf_cmd_add(c, "term", "xterm");
-
-	(void)snprintf(c->known_hosts, sizeof(c->known_hosts), "%s/%s",
-	    homedir, ".ssh/known_hosts");
-
-	c->font = xstrdup("sans-serif:pixelsize=14:bold");
-	c->wmname = xstrdup("CWM");
-}
-
-void
-conf_clear(struct conf *c)
-{
-	struct autogroup	*ag;
-	struct bind_ctx		*kb, *mb;
-	struct winname		*wn;
-	struct cmd_ctx		*cmd;
-	int			 i;
-
-	while ((cmd = TAILQ_FIRST(&c->cmdq)) != NULL) {
-		TAILQ_REMOVE(&c->cmdq, cmd, entry);
-		free(cmd->name);
-		free(cmd);
-	}
-
-	while ((kb = TAILQ_FIRST(&c->keybindq)) != NULL) {
-		TAILQ_REMOVE(&c->keybindq, kb, entry);
-		free(kb);
-	}
-
-	while ((ag = TAILQ_FIRST(&c->autogroupq)) != NULL) {
-		TAILQ_REMOVE(&c->autogroupq, ag, entry);
-		free(ag->class);
-		free(ag->name);
-		free(ag);
-	}
-
-	while ((wn = TAILQ_FIRST(&c->ignoreq)) != NULL) {
-		TAILQ_REMOVE(&c->ignoreq, wn, entry);
-		free(wn->name);
-		free(wn);
-	}
-
-	while ((mb = TAILQ_FIRST(&c->mousebindq)) != NULL) {
-		TAILQ_REMOVE(&c->mousebindq, mb, entry);
-		free(mb);
-	}
-
-	for (i = 0; i < CWM_COLOR_NITEMS; i++)
-		free(c->color[i]);
-
-	free(c->font);
-	free(c->wmname);
-}
-
-void
-conf_client(struct client_ctx *cc)
-{
-	struct winname	*wn;
-	int		 ignore = 0;
-
-	TAILQ_FOREACH(wn, &Conf.ignoreq, entry) {
-		if (strncasecmp(wn->name, cc->name, strlen(wn->name)) == 0) {
-			ignore = 1;
-			break;
-		}
-	}
-
-	cc->bwidth = (ignore) ? 0 : Conf.bwidth;
-	cc->flags |= (ignore) ? CLIENT_IGNORE : 0;
-}
-
-static const struct {
-	const char	*tag;
-	void		 (*handler)(void *, union arg *, enum xev);
-	int		 context;
-	union arg	 argument;
-} name_to_func[] = {
-	{ "lower", kbfunc_client_lower, CWM_CONTEXT_CC, {0} },
-	{ "raise", kbfunc_client_raise, CWM_CONTEXT_CC, {0} },
-	{ "search", kbfunc_menu_client, CWM_CONTEXT_SC, {0} },
-	{ "menusearch", kbfunc_menu_cmd, CWM_CONTEXT_SC, {0} },
-	{ "groupsearch", kbfunc_menu_group, CWM_CONTEXT_SC, {0} },
-	{ "hide", kbfunc_client_hide, CWM_CONTEXT_CC, {0} },
-	{ "cycle", kbfunc_client_cycle, CWM_CONTEXT_SC,
-	    {.i = (CWM_CYCLE_FORWARD)} },
-	{ "rcycle", kbfunc_client_cycle, CWM_CONTEXT_SC,
-	    {.i = (CWM_CYCLE_REVERSE)} },
-	{ "label", kbfunc_menu_client_label, CWM_CONTEXT_CC, {0} },
-	{ "delete", kbfunc_client_delete, CWM_CONTEXT_CC, {0} },
-	{ "group1", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 1} },
-	{ "group2", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 2} },
-	{ "group3", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 3} },
-	{ "group4", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 4} },
-	{ "group5", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 5} },
-	{ "group6", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 6} },
-	{ "group7", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 7} },
-	{ "group8", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 8} },
-	{ "group9", kbfunc_group_toggle, CWM_CONTEXT_SC, {.i = 9} },
-	{ "grouponly1", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 1} },
-	{ "grouponly2", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 2} },
-	{ "grouponly3", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 3} },
-	{ "grouponly4", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 4} },
-	{ "grouponly5", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 5} },
-	{ "grouponly6", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 6} },
-	{ "grouponly7", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 7} },
-	{ "grouponly8", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 8} },
-	{ "grouponly9", kbfunc_group_only, CWM_CONTEXT_SC, {.i = 9} },
-	{ "movetogroup1", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 1} },
-	{ "movetogroup2", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 2} },
-	{ "movetogroup3", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 3} },
-	{ "movetogroup4", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 4} },
-	{ "movetogroup5", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 5} },
-	{ "movetogroup6", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 6} },
-	{ "movetogroup7", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 7} },
-	{ "movetogroup8", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 8} },
-	{ "movetogroup9", kbfunc_client_movetogroup, CWM_CONTEXT_CC, {.i = 9} },
-	{ "nogroup", kbfunc_group_alltoggle, CWM_CONTEXT_SC, {0} },
-	{ "cyclegroup", kbfunc_group_cycle, CWM_CONTEXT_SC,
-	    {.i = (CWM_CYCLE_FORWARD)} },
-	{ "rcyclegroup", kbfunc_group_cycle, CWM_CONTEXT_SC,
-	    {.i = (CWM_CYCLE_REVERSE)} },
-	{ "cycleingroup", kbfunc_client_cycle, CWM_CONTEXT_SC,
-	    {.i = (CWM_CYCLE_FORWARD | CWM_CYCLE_INGROUP)} },
-	{ "rcycleingroup", kbfunc_client_cycle, CWM_CONTEXT_SC,
-	    {.i = (CWM_CYCLE_REVERSE | CWM_CYCLE_INGROUP)} },
-	{ "grouptoggle", kbfunc_client_toggle_group, CWM_CONTEXT_CC, {0} },
-	{ "stick", kbfunc_client_toggle_sticky, CWM_CONTEXT_CC, {0} },
-	{ "fullscreen", kbfunc_client_toggle_fullscreen, CWM_CONTEXT_CC, {0} },
-	{ "maximize", kbfunc_client_toggle_maximize, CWM_CONTEXT_CC, {0} },
-	{ "vmaximize", kbfunc_client_toggle_vmaximize, CWM_CONTEXT_CC, {0} },
-	{ "hmaximize", kbfunc_client_toggle_hmaximize, CWM_CONTEXT_CC, {0} },
-	{ "freeze", kbfunc_client_toggle_freeze, CWM_CONTEXT_CC, {0} },
-	{ "restart", kbfunc_cwm_status, CWM_CONTEXT_SC, {.i = CWM_EXEC_WM} },
-	{ "quit", kbfunc_cwm_status, CWM_CONTEXT_SC, {.i = CWM_QUIT} },
-	{ "exec", kbfunc_menu_exec, CWM_CONTEXT_SC,
-	    {.i = CWM_MENU_EXEC_EXEC} },
-	{ "exec_wm", kbfunc_menu_exec, CWM_CONTEXT_SC,
-	    {.i = CWM_MENU_EXEC_WM} },
-	{ "ssh", kbfunc_menu_ssh, CWM_CONTEXT_SC, {0} },
-	{ "terminal", kbfunc_exec_term, CWM_CONTEXT_SC, {0} },
-	{ "lock", kbfunc_exec_lock, CWM_CONTEXT_SC, {0} },
-	{ "moveup", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_UP)} },
-	{ "movedown", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_DOWN)} },
-	{ "moveright", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_RIGHT)} },
-	{ "moveleft", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_LEFT)} },
-	{ "bigmoveup", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_UP | CWM_BIGAMOUNT)} },
-	{ "bigmovedown", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_DOWN | CWM_BIGAMOUNT)} },
-	{ "bigmoveright", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_RIGHT | CWM_BIGAMOUNT)} },
-	{ "bigmoveleft", kbfunc_client_move, CWM_CONTEXT_CC,
-	    {.i = (CWM_LEFT | CWM_BIGAMOUNT)} },
-	{ "resizeup", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_UP)} },
-	{ "resizedown", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_DOWN)} },
-	{ "resizeright", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_RIGHT)} },
-	{ "resizeleft", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_LEFT)} },
-	{ "bigresizeup", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_UP | CWM_BIGAMOUNT)} },
-	{ "bigresizedown", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_DOWN | CWM_BIGAMOUNT)} },
-	{ "bigresizeright", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_RIGHT | CWM_BIGAMOUNT)} },
-	{ "bigresizeleft", kbfunc_client_resize, CWM_CONTEXT_CC,
-	    {.i = (CWM_LEFT | CWM_BIGAMOUNT)} },
-	{ "ptrmoveup", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_UP)} },
-	{ "ptrmovedown", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_DOWN)} },
-	{ "ptrmoveleft", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_LEFT)} },
-	{ "ptrmoveright", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_RIGHT)} },
-	{ "bigptrmoveup", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_UP | CWM_BIGAMOUNT)} },
-	{ "bigptrmovedown", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_DOWN | CWM_BIGAMOUNT)} },
-	{ "bigptrmoveleft", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_LEFT | CWM_BIGAMOUNT)} },
-	{ "bigptrmoveright", kbfunc_ptrmove, CWM_CONTEXT_SC,
-	    {.i = (CWM_RIGHT | CWM_BIGAMOUNT)} },
-	{ "htile", kbfunc_client_htile, CWM_CONTEXT_CC, {0} },
-	{ "vtile", kbfunc_client_vtile, CWM_CONTEXT_CC, {0} },
-	{ "window_lower", kbfunc_client_lower, CWM_CONTEXT_CC, {0} },
-	{ "window_raise", kbfunc_client_raise, CWM_CONTEXT_CC, {0} },
-	{ "window_hide", kbfunc_client_hide, CWM_CONTEXT_CC, {0} },
-	{ "window_move", mousefunc_client_move, CWM_CONTEXT_CC, {0} },
-	{ "window_resize", mousefunc_client_resize, CWM_CONTEXT_CC, {0} },
-	{ "window_grouptoggle", kbfunc_client_toggle_group, CWM_CONTEXT_CC, {0} },
-	{ "menu_group", kbfunc_menu_group, CWM_CONTEXT_SC, {0} },
-	{ "menu_unhide", kbfunc_menu_client, CWM_CONTEXT_SC, {0} },
-	{ "menu_cmd", kbfunc_menu_cmd, CWM_CONTEXT_SC, {0} },
-};
-
-static const struct {
-	const char	ch;
-	int		mask;
-} bind_mods[] = {
-	{ 'C',	ControlMask },
-	{ 'M',	Mod1Mask },
-	{ '4',	Mod4Mask },
-	{ 'S',	ShiftMask },
-};
-
 static const char *
 conf_bind_getmask(const char *name, unsigned int *mask)
 {
@@ -504,64 +502,60 @@ conf_bind_getmask(const char *name, unsigned int *mask)
 		if ((ch = strchr(name, bind_mods[i].ch)) != NULL && ch < dash)
 			*mask |= bind_mods[i].mask;
 	}
-
 	/* Skip past modifiers. */
 	return(dash + 1);
 }
 
 int
-conf_bind_kbd(struct conf *c, const char *bind, const char *cmd)
+conf_bind_key(struct conf *c, const char *bind, const char *cmd)
 {
 	struct bind_ctx	*kb;
 	const char	*key;
 	unsigned int	 i;
 
+	if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
+		conf_unbind_key(c, NULL);
+		goto out;
+	}
 	kb = xmalloc(sizeof(*kb));
 	key = conf_bind_getmask(bind, &kb->modmask);
-
 	kb->press.keysym = XStringToKeysym(key);
 	if (kb->press.keysym == NoSymbol) {
 		warnx("unknown symbol: %s", key);
 		free(kb);
 		return(0);
 	}
-
-	/* We now have the correct binding, remove duplicates. */
-	conf_unbind_kbd(c, kb);
-
-	if (strcmp("unmap", cmd) == 0) {
+	conf_unbind_key(c, kb);
+	if (cmd == NULL) {
 		free(kb);
-		return(1);
+		goto out;
 	}
-
 	for (i = 0; i < nitems(name_to_func); i++) {
 		if (strcmp(name_to_func[i].tag, cmd) != 0)
 			continue;
-
 		kb->callback = name_to_func[i].handler;
 		kb->context = name_to_func[i].context;
 		kb->argument = name_to_func[i].argument;
 		TAILQ_INSERT_TAIL(&c->keybindq, kb, entry);
-		return(1);
+		goto out;
 	}
-
 	kb->callback = kbfunc_exec_cmd;
 	kb->context = CWM_CONTEXT_NONE;
 	kb->argument.c = xstrdup(cmd);
 	TAILQ_INSERT_TAIL(&c->keybindq, kb, entry);
+out:
 	return(1);
 }
 
 static void
-conf_unbind_kbd(struct conf *c, struct bind_ctx *unbind)
+conf_unbind_key(struct conf *c, struct bind_ctx *unbind)
 {
 	struct bind_ctx	*key = NULL, *keynxt;
 
 	TAILQ_FOREACH_SAFE(key, &c->keybindq, entry, keynxt) {
-		if (key->modmask != unbind->modmask)
-			continue;
-
-		if (key->press.keysym == unbind->press.keysym) {
+		if ((unbind == NULL) ||
+		    ((key->modmask == unbind->modmask) &&
+		     (key->press.keysym == unbind->press.keysym))) {
 			TAILQ_REMOVE(&c->keybindq, key, entry);
 			if (key->context == CWM_CONTEXT_NONE)
 				free(key->argument.c);
@@ -577,36 +571,38 @@ conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
 	const char	*button, *errstr;
 	unsigned int	 i;
 
+	if ((strcmp(bind, "all") == 0) && (cmd == NULL)) {
+		conf_unbind_mouse(c, NULL);
+		goto out;
+	}
 	mb = xmalloc(sizeof(*mb));
 	button = conf_bind_getmask(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);
 	}
-
-	/* We now have the correct binding, remove duplicates. */
 	conf_unbind_mouse(c, mb);
-
-	if (strcmp("unmap", cmd) == 0) {
+	if (cmd == NULL) {
 		free(mb);
-		return(1);
+		goto out;
 	}
-
 	for (i = 0; i < nitems(name_to_func); i++) {
 		if (strcmp(name_to_func[i].tag, cmd) != 0)
 			continue;
-
 		mb->callback = name_to_func[i].handler;
 		mb->context = name_to_func[i].context;
 		mb->argument = name_to_func[i].argument;
 		TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry);
-		return(1);
+		goto out;
 	}
-
-	return(0);
+	mb->callback = kbfunc_exec_cmd;
+	mb->context = CWM_CONTEXT_NONE;
+	mb->argument.c = xstrdup(cmd);
+	TAILQ_INSERT_TAIL(&c->mousebindq, mb, entry);
+out:
+	return(1);
 }
 
 static void
@@ -615,74 +611,56 @@ conf_unbind_mouse(struct conf *c, struct bind_ctx *unbind)
 	struct bind_ctx		*mb = NULL, *mbnxt;
 
 	TAILQ_FOREACH_SAFE(mb, &c->mousebindq, entry, mbnxt) {
-		if (mb->modmask != unbind->modmask)
-			continue;
-
-		if (mb->press.button == unbind->press.button) {
+		if ((unbind == NULL) || 
+		    ((mb->modmask == unbind->modmask) &&
+		     (mb->press.button == unbind->press.button))) {
 			TAILQ_REMOVE(&c->mousebindq, mb, entry);
+			if (mb->context == CWM_CONTEXT_NONE)
+				free(mb->argument.c);
 			free(mb);
 		}
 	}
 }
 
-static int cursor_binds[] = {
-	XC_left_ptr,		/* CF_NORMAL */
-	XC_fleur,		/* CF_MOVE */
-	XC_bottom_right_corner,	/* CF_RESIZE */
-	XC_question_arrow,	/* CF_QUESTION */
-};
-
 void
-conf_cursor(struct conf *c)
+conf_grab_kbd(Window win)
 {
+	struct bind_ctx	*kb;
+	KeyCode		 kc;
 	unsigned int	 i;
 
-	for (i = 0; i < nitems(cursor_binds); i++)
-		c->cursor[i] = XCreateFontCursor(X_Dpy, cursor_binds[i]);
-}
+	XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
 
-static unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
+	TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
+		kc = XKeysymToKeycode(X_Dpy, kb->press.keysym);
+		if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) &&
+		    (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym))
+			kb->modmask |= ShiftMask;
+
+		for (i = 0; i < nitems(ignore_mods); i++)
+			XGrabKey(X_Dpy, kc, (kb->modmask | ignore_mods[i]), win,
+			    True, GrabModeAsync, GrabModeAsync);
+	}
+}
 
 void
 conf_grab_mouse(Window win)
 {
 	struct bind_ctx	*mb;
-	unsigned int	i;
+	unsigned int	 i;
 
 	XUngrabButton(X_Dpy, AnyButton, AnyModifier, win);
 
 	TAILQ_FOREACH(mb, &Conf.mousebindq, entry) {
 		if (mb->context != CWM_CONTEXT_CC)
 			continue;
-		for (i = 0; i < nitems(ign_mods); i++) {
+		for (i = 0; i < nitems(ignore_mods); i++) {
 			XGrabButton(X_Dpy, mb->press.button,
-			    (mb->modmask | ign_mods[i]), win, False,
+			    (mb->modmask | ignore_mods[i]), win, False,
 			    BUTTONMASK, GrabModeAsync, GrabModeSync,
 			    None, None);
 		}
 	}
-
-}
-
-void
-conf_grab_kbd(Window win)
-{
-	struct bind_ctx	*kb;
-	KeyCode		 kc;
-	unsigned int	 i;
-
-	XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
-
-	TAILQ_FOREACH(kb, &Conf.keybindq, entry) {
-		kc = XKeysymToKeycode(X_Dpy, kb->press.keysym);
-		if ((XkbKeycodeToKeysym(X_Dpy, kc, 0, 0) != kb->press.keysym) &&
-		    (XkbKeycodeToKeysym(X_Dpy, kc, 0, 1) == kb->press.keysym))
-			kb->modmask |= ShiftMask;
-
-		for (i = 0; i < nitems(ign_mods); i++)
-			XGrabKey(X_Dpy, kc, (kb->modmask | ign_mods[i]), win,
-			    True, GrabModeAsync, GrabModeAsync);
-	}
 }
 
 static char *cwmhints[] = {
diff --git a/cwm.1 b/cwm.1
index 68434c3..2898a52 100644
--- a/cwm.1
+++ b/cwm.1
@@ -117,11 +117,6 @@ Toggle maximization of current window.
 Toggle vertical maximization of current window.
 .It Ic CMS-=
 Toggle horizontal maximization of current window.
-.It Ic C-[Up | Down | Left | Right]
-Move pointer by a small amount.
-.It Ic CS-[Up | Down | Left | Right]
-Move pointer by a large amount; see
-.Xr cwmrc 5 .
 .It Ic M-[hjkl]
 Move window by a small amount.
 .It Ic MS-[hjkl]
@@ -224,8 +219,8 @@ Menus are recalled by clicking the mouse on the root window:
 .Pp
 .Bl -tag -width Ds -offset indent -compact
 .It Ic M1
-Show list of currently hidden windows.
-Selecting an item will unhide that window.
+Show list of currently defined windows.
+Selecting an item will warp to that window, unhiding it if necessary.
 .It Ic M2
 Show list of currently defined groups.
 Selecting an item will hide/unhide that group.
diff --git a/cwmrc.5 b/cwmrc.5
index 93f7273..6419abd 100644
--- a/cwmrc.5
+++ b/cwmrc.5
@@ -63,11 +63,15 @@ 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 Ar keys command
-Cause the creation of a key binding, or replacement of a default
-key binding.
+.It Ic bind-key Ar key function
+Bind or rebind key
+.Ar key
+to
+.Ar function .
 The modifier keys come first, followed by a
-.Sq - .
+.Sq - ,
+then a keysym name, taken from
+.Pa /usr/X11R6/include/X11/keysymdef.h .
 .Pp
 The following modifiers are recognised:
 .Pp
@@ -83,22 +87,53 @@ Mod4 (windows) key.
 .El
 .Pp
 The
-.Sq -
-should be followed by a keysym name, taken from
-.Pa /usr/X11R6/include/X11/keysymdef.h .
-The
-.Ar command
+.Ar function
 may either be one from the
-.Sx BIND COMMAND LIST
+.Sx BIND FUNCTION LIST
 (see below) or the command line that is to be executed.
 .Pp
-A special
-.Ar command
-keyword
-.Dq unmap
-can be used to remove the named key binding.
-This can be used to remove a binding which conflicts with an
-application.
+.It Ic bind-mouse Ar button function
+Bind or rebind button
+.Ar button
+to
+.Ar function .
+The modifier keys come first, followed by a
+.Sq - ,
+then the button number.
+.Pp
+The following modifiers are recognised:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It Ic C
+Control key.
+.It Ic M
+Meta key.
+.It Ic S
+Shift key.
+.It Ic 4
+Mod4 (windows) key.
+.El
+.Pp
+The following buttons are recognised:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It Ic 1
+Left mouse button.
+.It Ic 2
+Middle mouse button.
+.It Ic 3
+Right mouse button.
+.It Ic 4
+Scroll up mouse button.
+.It Ic 5
+Scroll down mouse button.
+.El
+.Pp
+The
+.Ar function
+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
@@ -177,48 +212,6 @@ Ignore, and do not warp to, windows with the name
 .Ar windowname
 when drawing borders and cycling through windows.
 .Pp
-.It Ic mousebind Ar buttons command
-Cause the creation of a mouse binding, or replacement of a default
-mouse binding.
-The modifier keys come first, followed by a
-.Sq - .
-.Pp
-The following modifiers are recognised:
-.Pp
-.Bl -tag -width Ds -offset indent -compact
-.It Ic C
-Control key.
-.It Ic M
-Meta key.
-.It Ic S
-Shift key.
-.It Ic 4
-Mod4 (windows) key.
-.El
-.Pp
-The
-.Sq -
-should be followed by number:
-.Pp
-.Bl -tag -width Ds -offset indent -compact
-.It Ic 1
-Left mouse button.
-.It Ic 2
-Middle mouse button.
-.It Ic 3
-Right mouse button.
-.It Ic 4
-Scroll up mouse button.
-.It Ic 5
-Scroll down mouse button.
-.El
-.Pp
-The
-.Ar command
-may be taken from the
-.Sx MOUSEBIND COMMAND LIST
-(see below).
-.Pp
 .It Ic moveamount Ar pixels
 Set a default size for the keyboard movement bindings,
 in pixels.
@@ -234,9 +227,28 @@ 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 .
+A special
+.Ar key
+keyword
+.Dq all
+can be used to unbind all keys.
+.Pp
+.It Ic unbind-mouse Ar button
+Unbind function bound to
+.Ar button .
+A special
+.Ar button
+keyword
+.Dq all
+can be used to unbind all buttons.
+.Pp
 .El
-.Sh BIND COMMAND LIST
-.Bl -tag -width 18n -compact
+.Sh BIND FUNCTION LIST
+.Bl -tag -width 23n -compact
 .It restart
 Restart the running
 .Xr cwm 1 .
@@ -247,197 +259,176 @@ Quit
 Spawn a new terminal.
 .It lock
 Lock the screen.
-.It search
+.It menu-window
 Launch window search menu.
-.It menusearch
+.It menu-window-hidden
+Launch hidden window search menu.
+.It menu-cmd
 Launch application search menu.
-.It groupsearch
+.It menu-group
 Launch group search menu.
-.It exec
+.It menu-exec
 Launch
 .Dq exec program
 menu.
-.It exec_wm
+.It menu-exec-wm
 Launch
 .Dq exec WindowManager
 menu.
-.It ssh
+.It menu-ssh
 Launch
 .Dq ssh
 menu.
-.It group[n]
+.It group-toggle-[n]
 Toggle visibility of group n, where n is 1-9.
-.It grouponly[n]
-Like
-.Ar group[n]
-but also hides the other groups.
-.It nogroup
+.It group-only-[n]
+Show only group n, where n is 1-9, hiding other groups.
+.It window-toggle-all
 Toggle visibility of all groups.
-.It grouptoggle
+.It window-group
 Toggle group membership of current window.
-.It movetogroup[n]
+.It window-movetogroup-[n]
 Hide current window from display and move to group n, where n is 1-9.
-.It cyclegroup
+.It group-cycle
 Forward cycle through groups.
-.It rcyclegroup
+.It group-rcycle
 Reverse cycle through groups.
-.It cycle
+.It window-cycle
 Forward cycle through windows.
-.It rcycle
+.It window-rcycle
 Reverse cycle through windows.
-.It cycleingroup
+.It window-cycle-ingroup
 Forward cycle through windows in current group.
-.It rcycleingroup
+.It window-rcycle-ingroup
 Reverse cycle through windows in current group.
-.It delete
+.It window-delete
 Delete current window.
-.It hide
+.It window-hide
 Hide current window.
-.It lower
+.It window-lower
 Lower current window.
-.It raise
+.It window-raise
 Raise current window.
-.It label
+.It window-menu-label
 Label current window.
-.It freeze
+.It window-freeze
 Freeze current window geometry.
-.It stick
+.It window-stick
 Stick current window to all groups (same as assigning to nogroup).
-.It fullscreen
+.It window-fullscreen
 Full-screen current window (gap + border removed).
-.It maximize
+.It window-maximize
 Maximize current window (gap + border honored).
-.It vmaximize
+.It window-vmaximize
 Vertically maximize current window (gap + border honored).
-.It hmaximize
+.It window-hmaximize
 Horizontally maximize current window (gap + border honored).
-.It moveup
+.It window-htile
+Current window is placed at the top of the screen and maximized
+horizontally, other windows in its group share remaining screen space.
+.It window-vtile
+Current window is placed on the left of the screen and maximized
+vertically, other windows in its group share remaining screen space.
+.It window-move
+Move current window.
+.It window-resize
+Resize current window.
+.It window-move-up
 Move window
 .Ar moveamount
 pixels up.
-.It movedown
+.It window-move-down
 Move window
 .Ar moveamount
 pixels down.
-.It moveright
+.It window-move-right
 Move window
 .Ar moveamount
 pixels right.
-.It moveleft
+.It window-move-left
 Move window
 .Ar moveamount
 pixels left.
-.It bigmoveup
+.It window-move-up-big
 Move window 10 times
 .Ar moveamount
 pixels up.
-.It bigmovedown
+.It window-move-down-big
 Move window 10 times
 .Ar moveamount
 pixels down.
-.It bigmoveright
+.It window-move-right-big
 Move window 10 times
 .Ar moveamount
 pixels right.
-.It bigmoveleft
+.It window-move-left-big
 Move window 10 times
 .Ar moveamount
 pixels left.
-.It resizeup
+.It window-resize-up
 Resize window
 .Ar moveamount
 pixels up.
-.It resizedown
+.It window-resize-down
 Resize window
 .Ar moveamount
 pixels down.
-.It resizeright
+.It window-resize-right
 Resize window
 .Ar moveamount
 pixels right.
-.It resizeleft
+.It window-resize-left
 Resize window
 .Ar moveamount
 pixels left.
-.It bigresizeup
+.It window-resize-up-big
 Resize window 10 times
 .Ar moveamount
 pixels up.
-.It bigresizedown
+.It window-resize-down-big
 Resize window 10 times
 .Ar moveamount
 pixels down.
-.It bigresizeright
+.It window-resize-right-big
 Resize window 10 times
 .Ar moveamount
 pixels right.
-.It bigresizeleft
+.It window-resize-left-big
 Resize window 10 times
 .Ar moveamount
 pixels left.
-.It ptrmoveup
+.It pointer-move-up
 Move pointer
 .Ar moveamount
 pixels up.
-.It ptrmovedown
+.It pointer-move-down
 Move pointer
 .Ar moveamount
 pixels down.
-.It ptrmoveright
+.It pointer-move-right
 Move pointer
 .Ar moveamount
 pixels right.
-.It ptrmoveleft
+.It pointer-move-left
 Move pointer
 .Ar moveamount
 pixels left.
-.It bigptrmoveup
+.It pointer-move-up-big
 Move pointer 10 times
 .Ar moveamount
 pixels up.
-.It bigptrmovedown
+.It pointer-move-down-big
 Move pointer 10 times
 .Ar moveamount
 pixels down.
-.It bigptrmoveright
+.It pointer-move-right-big
 Move pointer 10 times
 .Ar moveamount
 pixels right.
-.It bigptrmoveleft
+.It pointer-move-left-big
 Move pointer 10 times
 .Ar moveamount
 pixels left.
-.It htile
-Current window is placed at the top of the screen and maximized
-horizontally, other windows in its group share remaining screen space.
-.It vtile
-Current window is placed on the left of the screen and maximized
-vertically, other windows in its group share remaining screen space.
-.El
-.Sh MOUSEBIND COMMAND LIST
-.Bl -tag -width 18n -compact
-.It window_move
-Move current window.
-.It window_resize
-Resize current window.
-.It window_lower
-Lower current window.
-.It window_raise
-Raise current window.
-.It window_hide
-Hide current window.
-.It window_grouptoggle
-Toggle group membership of current window.
-.It cyclegroup
-Forward cycle through groups.
-.It rcyclegroup
-Reverse cycle through groups.
-.It menu_group
-Launch group list.
-.It menu_unhide
-Launch hidden window list.
-.It menu_cmd
-Launch command list.
 .El
 .Sh FILES
 .Bl -tag -width "~/.cwmrcXXX" -compact
@@ -470,23 +461,23 @@ ignore xapm
 ignore xclock
 
 # Key bindings
-bind CM-r	label
-bind CS-Return	"xterm -e top"
-bind 4-o	unmap
-bind CM-equal	unmap
-bind CMS-equal	unmap
-bind C4-equal	vmaximize
-bind C4S-equal	hmaximize
-bind M-1	grouponly1
-bind M-2	grouponly2
-bind M-3	grouponly3
-bind MS-1	movetogroup1
-bind MS-2	movetogroup2
-bind MS-3	movetogroup3
+bind-key CM-r		window-menu-label
+bind-key CS-Return	"xterm -e top"
+bind-key C4-equal	window-vmaximize
+bind-key C4S-equal	window-hmaximize
+bind-key M-1		group-only-1
+bind-key M-2		group-only-2
+bind-key M-3		group-only-3
+bind-key MS-1		window-movetogroup-1
+bind-key MS-2		window-movetogroup-2
+bind-key MS-3		window-movetogroup-3
+unbind-key 4-o
+unbind-key CM-equal
+unbind-key CMS-equal
 
 # Mouse bindings
-mousebind M-2	window_lower
-mousebind M-3	window_resize
+bind-mouse M-2		window-lower
+bind-mouse M-3		window-resize
 .Ed
 .Sh SEE ALSO
 .Xr cwm 1
diff --git a/kbfunc.c b/kbfunc.c
index 5f5a163..5edb59d 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -298,12 +298,13 @@ kbfunc_menu_client(void *ctx, union arg *arg, enum xev xev)
 	struct menu		*mi;
 	struct menu_q		 menuq;
 	int			 m = (xev == CWM_XEV_BTN);
+	int			 all = (arg->i & CWM_MENU_WINDOW_ALL);
 
 	old_cc = client_current();
 
 	TAILQ_INIT(&menuq);
 	TAILQ_FOREACH(cc, &sc->clientq, entry) {
-		if (m) {
+		if (!all) {
 			if (cc->flags & CLIENT_HIDDEN)
 				menuq_add(&menuq, cc, NULL);
 		} else
@@ -390,6 +391,7 @@ kbfunc_menu_exec(void *ctx, union arg *arg, enum xev xev)
 	struct screen_ctx	*sc = ctx;
 	char			**ap, *paths[NPATHS], *path, *pathcpy;
 	char			 tpath[PATH_MAX];
+	struct stat		 sb;
 	const char		*label;
 	DIR			*dirp;
 	struct dirent		*dp;
@@ -432,12 +434,13 @@ kbfunc_menu_exec(void *ctx, union arg *arg, enum xev xev)
 			    dp->d_name);
 			if (l == -1 || l >= sizeof(tpath))
 				continue;
-			/* skip everything but regular files and symlinks */
+			/* Skip everything but regular files and symlinks. */
 			if (dp->d_type != DT_REG && dp->d_type != DT_LNK) {
-				/* use an additional stat-based check in case d_type isn't supported */
-				if (lstat(tpath, &sb) < 0)
+				/* lstat(2) in case d_type isn't supported. */
+				if (lstat(tpath, &sb) == -1)
 					continue;
-				if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
+				if (!S_ISREG(sb.st_mode) && 
+				    !S_ISLNK(sb.st_mode))
 					continue;
 			}
 			if (access(tpath, X_OK) == 0)
@@ -449,7 +452,7 @@ kbfunc_menu_exec(void *ctx, union arg *arg, enum xev xev)
 
 	if ((mi = menu_filter(sc, &menuq, label, NULL,
 	    (CWM_MENU_DUMMY | CWM_MENU_FILE),
-	    search_match_exec_path, NULL)) != NULL) {
+	    search_match_exec, search_print_text)) != NULL) {
 		if (mi->text[0] == '\0')
 			goto out;
 		switch (cmd) {
@@ -518,13 +521,13 @@ kbfunc_menu_ssh(void *ctx, union arg *arg, enum xev xev)
 		if (p - buf + 1 > sizeof(hostbuf))
 			continue;
 		(void)strlcpy(hostbuf, buf, p - buf + 1);
-		menuq_add(&menuq, NULL, hostbuf);
+		menuq_add(&menuq, NULL, "%s", hostbuf);
 	}
 	free(lbuf);
 	(void)fclose(fp);
 menu:
 	if ((mi = menu_filter(sc, &menuq, "ssh", NULL, (CWM_MENU_DUMMY),
-	    search_match_exec, NULL)) != NULL) {
+	    search_match_text, search_print_text)) != NULL) {
 		if (mi->text[0] == '\0')
 			goto out;
 		l = snprintf(path, sizeof(path), "%s -T '[ssh] %s' -e ssh %s",
@@ -550,7 +553,7 @@ kbfunc_menu_client_label(void *ctx, union arg *arg, enum xev xev)
 
 	/* dummy is set, so this will always return */
 	mi = menu_filter(cc->sc, &menuq, "label", cc->label, (CWM_MENU_DUMMY),
-	    search_match_text, NULL);
+	    search_match_text, search_print_text);
 
 	if (!mi->abort) {
 		free(cc->label);
diff --git a/menu.c b/menu.c
index 44f76da..af13cbf 100644
--- a/menu.c
+++ b/menu.c
@@ -126,7 +126,6 @@ menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
 	    CurrentTime) != GrabSuccess) {
 		XUnmapWindow(X_Dpy, sc->menu.win);
 		return(NULL);
-
 	}
 
 	XGetInputFocus(X_Dpy, &focuswin, &focusrevert);
@@ -196,7 +195,7 @@ menu_complete_path(struct menu_ctx *mc)
 	TAILQ_INIT(&menuq);
 
 	if ((mi = menu_filter(sc, &menuq, mc->searchstr, NULL,
-	    (CWM_MENU_DUMMY), search_match_path_any, NULL)) != NULL) {
+	    (CWM_MENU_DUMMY), search_match_path, search_print_text)) != NULL) {
 		mr->abort = mi->abort;
 		mr->dummy = mi->dummy;
 		if (mi->text[0] != '\0')
@@ -366,11 +365,7 @@ menu_draw(struct menu_ctx *mc, struct menu_q *menuq, struct menu_q *resultq)
 	}
 
 	TAILQ_FOREACH(mi, resultq, resultentry) {
-		if (mc->print != NULL)
-			(*mc->print)(mi, mc->listing);
-		else
-			(void)snprintf(mi->print, sizeof(mi->print),
-			    "%s", mi->text);
+		(*mc->print)(mi, mc->listing);
 
 		XftTextExtentsUtf8(X_Dpy, sc->xftfont,
 		    (const FcChar8*)mi->print,
@@ -671,4 +666,3 @@ menu_windraw(struct screen_ctx *sc, Window win, const char *fmt, ...)
 
 	free(text);
 }
-
diff --git a/mousefunc.c b/mousefunc.c
index dbec420..0926506 100644
--- a/mousefunc.c
+++ b/mousefunc.c
@@ -53,6 +53,8 @@ mousefunc_client_resize(void *ctx, union arg *arg, enum xev xev)
 	    CurrentTime) != GrabSuccess)
 		return;
 
+	menu_windraw(sc, cc->win, "%4d x %-4d", cc->dim.w, cc->dim.h);
+
 	for (;;) {
 		XWindowEvent(X_Dpy, cc->win, MOUSEMASK, &ev);
 
@@ -120,6 +122,8 @@ mousefunc_client_move(void *ctx, union arg *arg, enum xev xev)
 	    CurrentTime) != GrabSuccess)
 		return;
 
+	menu_windraw(sc, cc->win, "%4d, %-4d", cc->geom.x, cc->geom.y);
+
 	for (;;) {
 		XWindowEvent(X_Dpy, cc->win, MOUSEMASK, &ev);
 
diff --git a/parse.y b/parse.y
index abd3224..c8942d9 100644
--- a/parse.y
+++ b/parse.y
@@ -70,8 +70,9 @@ typedef struct {
 
 %}
 
-%token	FONTNAME STICKY GAP MOUSEBIND
-%token	AUTOGROUP BIND COMMAND IGNORE
+%token	BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
+%token	FONTNAME STICKY GAP
+%token	AUTOGROUP COMMAND IGNORE
 %token	YES NO BORDERWIDTH MOVEAMOUNT
 %token	COLOR SNAPDIST
 %token	ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
@@ -171,16 +172,6 @@ main		: FONTNAME STRING		{
 			conf_ignore(conf, $2);
 			free($2);
 		}
-		| BIND STRING string		{
-			if (!conf_bind_kbd(conf, $2, $3)) {
-				yyerror("invalid bind: %s %s", $2, $3);
-				free($2);
-				free($3);
-				YYERROR;
-			}
-			free($2);
-			free($3);
-		}
 		| GAP NUMBER NUMBER NUMBER NUMBER {
 			if ($2 < 0 || $2 > INT_MAX ||
 			    $3 < 0 || $3 > INT_MAX ||
@@ -194,9 +185,27 @@ main		: FONTNAME STRING		{
 			conf->gap.left = $4;
 			conf->gap.right = $5;
 		}
-		| MOUSEBIND STRING string	{
+		| BINDKEY STRING string {
+			if (!conf_bind_key(conf, $2, $3)) {
+				yyerror("invalid bind-key: %s %s", $2, $3);
+				free($2);
+				free($3);
+				YYERROR;
+			}
+			free($2);
+			free($3);
+		}
+		| UNBINDKEY STRING {
+			if (!conf_bind_key(conf, $2, NULL)) {
+				yyerror("invalid unbind-key: %s", $2);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+		}
+		| BINDMOUSE STRING string {
 			if (!conf_bind_mouse(conf, $2, $3)) {
-				yyerror("invalid mousebind: %s %s", $2, $3);
+				yyerror("invalid bind-mouse: %s %s", $2, $3);
 				free($2);
 				free($3);
 				YYERROR;
@@ -204,6 +213,14 @@ main		: FONTNAME STRING		{
 			free($2);
 			free($3);
 		}
+		| UNBINDMOUSE STRING {
+			if (!conf_bind_mouse(conf, $2, NULL)) {
+				yyerror("invalid unbind-mouse: %s", $2);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+		}
 		;
 
 color		: COLOR colors
@@ -280,7 +297,8 @@ lookup(char *s)
 	static const struct keywords keywords[] = {
 		{ "activeborder",	ACTIVEBORDER},
 		{ "autogroup",		AUTOGROUP},
-		{ "bind",		BIND},
+		{ "bind-key",		BINDKEY},
+		{ "bind-mouse",		BINDMOUSE},
 		{ "borderwidth",	BORDERWIDTH},
 		{ "color",		COLOR},
 		{ "command",		COMMAND},
@@ -292,12 +310,13 @@ lookup(char *s)
 		{ "inactiveborder",	INACTIVEBORDER},
 		{ "menubg",		MENUBG},
 		{ "menufg",		MENUFG},
-		{ "mousebind",		MOUSEBIND},
 		{ "moveamount",		MOVEAMOUNT},
 		{ "no",			NO},
 		{ "selfont", 		FONTSELCOLOR},
 		{ "snapdist",		SNAPDIST},
 		{ "sticky",		STICKY},
+		{ "unbind-key",		UNBINDKEY},
+		{ "unbind-mouse",	UNBINDMOUSE},
 		{ "ungroupborder",	UNGROUPBORDER},
 		{ "urgencyborder",	URGENCYBORDER},
 		{ "yes",		YES}
diff --git a/search.c b/search.c
index c1b3e3d..e50884a 100644
--- a/search.c
+++ b/search.c
@@ -36,10 +36,8 @@
 #define PATH_ANY 	0x0001
 #define PATH_EXEC 	0x0002
 
-static void	search_match_path(struct menu_q *, struct menu_q *,
+static void	search_match_path_type(struct menu_q *, struct menu_q *,
 		    char *, int);
-static void	search_match_path_exec(struct menu_q *, struct menu_q *,
-		    char *);
 static int	strsubmatch(char *, char *, int);
 
 void
@@ -107,7 +105,13 @@ search_match_client(struct menu_q *menuq, struct menu_q *resultq, char *search)
 }
 
 void
-search_print_cmd(struct menu *mi, int i)
+search_print_text(struct menu *mi, int listing)
+{
+	(void)snprintf(mi->print, sizeof(mi->print), "%s", mi->text);
+}
+
+void
+search_print_cmd(struct menu *mi, int listing)
 {
 	struct cmd_ctx	*cmd = (struct cmd_ctx *)mi->ctx;
 
@@ -115,7 +119,7 @@ search_print_cmd(struct menu *mi, int i)
 }
 
 void
-search_print_group(struct menu *mi, int i)
+search_print_group(struct menu *mi, int listing)
 {
 	struct group_ctx	*gc = (struct group_ctx *)mi->ctx;
 
@@ -125,7 +129,7 @@ search_print_group(struct menu *mi, int i)
 }
 
 void
-search_print_client(struct menu *mi, int list)
+search_print_client(struct menu *mi, int listing)
 {
 	struct client_ctx	*cc = (struct client_ctx *)mi->ctx;
 	char			 flag = ' ';
@@ -141,7 +145,8 @@ search_print_client(struct menu *mi, int list)
 }
 
 static void
-search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, int flag)
+search_match_path_type(struct menu_q *menuq, struct menu_q *resultq,
+    char *search, int flag)
 {
 	char 	 pattern[PATH_MAX];
 	glob_t	 g;
@@ -162,16 +167,10 @@ search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, in
 	globfree(&g);
 }
 
-static void
-search_match_path_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
-{
-	return(search_match_path(menuq, resultq, search, PATH_EXEC));
-}
-
 void
-search_match_path_any(struct menu_q *menuq, struct menu_q *resultq, char *search)
+search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
 {
-	return(search_match_path(menuq, resultq, search, PATH_ANY));
+	return(search_match_path_type(menuq, resultq, search, PATH_ANY));
 }
 
 void
@@ -208,14 +207,9 @@ search_match_exec(struct menu_q *menuq, struct menu_q *resultq, char *search)
 		if (mj == NULL)
 			TAILQ_INSERT_TAIL(resultq, mi, resultentry);
 	}
-}
 
-void
-search_match_exec_path(struct menu_q *menuq, struct menu_q *resultq, char *search)
-{
-	search_match_exec(menuq, resultq, search);
 	if (TAILQ_EMPTY(resultq))
-		search_match_path_exec(menuq, resultq, search);
+		search_match_path_type(menuq, resultq, search, PATH_EXEC);
 }
 
 static int