summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--calmwm.c2
-rw-r--r--calmwm.h31
-rw-r--r--conf.c272
-rw-r--r--group.c11
-rw-r--r--kbfunc.c36
-rw-r--r--menu.c21
-rw-r--r--mousefunc.c23
-rw-r--r--parse.y9
-rw-r--r--search.c11
-rw-r--r--xevents.c6
10 files changed, 192 insertions, 230 deletions
diff --git a/calmwm.c b/calmwm.c
index 65dc49f..ed18a6a 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -46,7 +46,7 @@ struct client_ctx_q		 Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
 
 int				 HasRandr, Randr_ev;
 struct conf			 Conf;
-char				*homedir;
+const char			*homedir;
 
 static void	sigchld_cb(int);
 static int	x_errorhandler(Display *, XErrorEvent *);
diff --git a/calmwm.h b/calmwm.h
index 831fbe2..0f2bde6 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -95,6 +95,8 @@ size_t strlcat(char *, const char *, size_t);
 #define CWM_GAP			0x0001
 #define CWM_NOGAP		0x0002
 
+#define CWM_WIN			0x0001
+
 union arg {
 	char	*c;
 	int	 i;
@@ -259,7 +261,6 @@ struct keybinding {
 	union arg		 argument;
 	unsigned int		 modmask;
 	KeySym			 keysym;
-#define KBFLAG_NEEDCLIENT	 0x0001
 	int			 flags;
 	int			 argtype;
 };
@@ -271,17 +272,15 @@ struct mousebinding {
 	union arg			argument;
 	unsigned int			modmask;
 	unsigned int		 	button;
-#define MOUSEBIND_CTX_ROOT		0x0001
-#define MOUSEBIND_CTX_WIN		0x0002
 	int				flags;
 };
 TAILQ_HEAD(mousebinding_q, mousebinding);
 
 struct cmd {
 	TAILQ_ENTRY(cmd)	entry;
-	char			image[MAXPATHLEN];
-#define CMD_MAXLABELLEN		256
-	char			label[CMD_MAXLABELLEN];
+#define CMD_MAXNAMELEN		256
+	char			name[CMD_MAXNAMELEN];
+	char			path[MAXPATHLEN];
 };
 TAILQ_HEAD(cmd_q, cmd);
 
@@ -338,7 +337,7 @@ extern Time				 Last_Event_Time;
 extern struct screen_ctx_q		 Screenq;
 extern struct client_ctx_q		 Clientq;
 extern struct conf			 Conf;
-extern char				*homedir;
+extern const char			*homedir;
 extern int				 HasRandr, Randr_ev;
 
 enum {
@@ -507,8 +506,6 @@ void			 mousefunc_client_move(struct client_ctx *,
     			    union arg *);
 void			 mousefunc_client_raise(struct client_ctx *,
     			    union arg *);
-void			 mousefunc_client_rcyclegroup(struct client_ctx *,
-    			   union arg *);
 void			 mousefunc_client_resize(struct client_ctx *,
     			    union arg *);
 void			 mousefunc_menu_cmd(struct client_ctx *, union arg *);
@@ -517,25 +514,29 @@ void			 mousefunc_menu_unhide(struct client_ctx *,
     			    union arg *);
 
 struct menu  		*menu_filter(struct screen_ctx *, struct menu_q *,
-			     char *, char *, int,
+			     const char *, const char *, int,
 			     void (*)(struct menu_q *, struct menu_q *, char *),
 			     void (*)(struct menu *, int));
+void			 menuq_add(struct menu_q *, void *, const char *, ...);
 void			 menuq_clear(struct menu_q *);
 
 int			 parse_config(const char *, struct conf *);
 
 void			 conf_atoms(void);
-void			 conf_autogroup(struct conf *, int, char *);
-void			 conf_bind_kbd(struct conf *, char *, char *);
-int			 conf_bind_mouse(struct conf *, char *, char *);
+void			 conf_autogroup(struct conf *, int, const char *);
+int			 conf_bind_kbd(struct conf *, const char *,
+    			     const char *);
+int			 conf_bind_mouse(struct conf *, const char *,
+    			     const char *);
 void			 conf_clear(struct conf *);
 void			 conf_client(struct client_ctx *);
-void			 conf_cmd_add(struct conf *, char *, char *);
+void			 conf_cmd_add(struct conf *, const char *,
+			     const char *);
 void			 conf_cursor(struct conf *);
 void			 conf_grab_kbd(Window);
 void			 conf_grab_mouse(Window);
 void			 conf_init(struct conf *);
-void			 conf_ignore(struct conf *, char *);
+void			 conf_ignore(struct conf *, const char *);
 void			 conf_screen(struct screen_ctx *);
 
 void			 xev_loop(void);
diff --git a/conf.c b/conf.c
index 55ca659..142ee40 100644
--- a/conf.c
+++ b/conf.c
@@ -37,23 +37,23 @@ static void	 	 conf_unbind_mouse(struct conf *, struct mousebinding *);
 
 /* Add an command menu entry to the end of the menu */
 void
-conf_cmd_add(struct conf *c, char *image, char *label)
+conf_cmd_add(struct conf *c, const char *name, const char *path)
 {
 	/* "term" and "lock" have special meanings. */
-	if (strcmp(label, "term") == 0)
-		(void)strlcpy(c->termpath, image, sizeof(c->termpath));
-	else if (strcmp(label, "lock") == 0)
-		(void)strlcpy(c->lockpath, image, sizeof(c->lockpath));
+	if (strcmp(name, "term") == 0)
+		(void)strlcpy(c->termpath, path, sizeof(c->termpath));
+	else if (strcmp(name, "lock") == 0)
+		(void)strlcpy(c->lockpath, path, sizeof(c->lockpath));
 	else {
 		struct cmd *cmd = xmalloc(sizeof(*cmd));
-		(void)strlcpy(cmd->image, image, sizeof(cmd->image));
-		(void)strlcpy(cmd->label, label, sizeof(cmd->label));
+		(void)strlcpy(cmd->name, name, sizeof(cmd->name));
+		(void)strlcpy(cmd->path, path, sizeof(cmd->path));
 		TAILQ_INSERT_TAIL(&c->cmdq, cmd, entry);
 	}
 }
 
 void
-conf_autogroup(struct conf *c, int no, char *val)
+conf_autogroup(struct conf *c, int no, const char *val)
 {
 	struct autogroupwin	*aw;
 	char			*p;
@@ -74,7 +74,7 @@ conf_autogroup(struct conf *c, int no, char *val)
 }
 
 void
-conf_ignore(struct conf *c, char *val)
+conf_ignore(struct conf *c, const char *val)
 {
 	struct winmatch	*wm;
 
@@ -85,7 +85,7 @@ conf_ignore(struct conf *c, char *val)
 	TAILQ_INSERT_TAIL(&c->ignoreq, wm, entry);
 }
 
-static char *color_binds[] = {
+static const char *color_binds[] = {
 	"#CCCCCC",	/* CWM_COLOR_BORDER_ACTIVE */
 	"#666666",	/* CWM_COLOR_BORDER_INACTIVE */
 	"#FC8814",	/* CWM_COLOR_BORDER_URGENCY */
@@ -147,9 +147,9 @@ conf_screen(struct screen_ctx *sc)
 	conf_grab_kbd(sc->rootwin);
 }
 
-static struct {
-	char	*key;
-	char	*func;
+static const struct {
+	const char	*key;
+	const char	*func;
 } kbd_binds[] = {
 	{ "CM-Return",	"terminal" },
 	{ "CM-Delete",	"lock" },
@@ -318,21 +318,21 @@ conf_client(struct client_ctx *cc)
 	cc->flags |= ignore ? CLIENT_IGNORE : 0;
 }
 
-static struct {
-	char		*tag;
+static const struct {
+	const char	*tag;
 	void		 (*handler)(struct client_ctx *, union arg *);
 	int		 flags;
 	union arg	 argument;
 } name_to_kbfunc[] = {
-	{ "lower", kbfunc_client_lower, KBFLAG_NEEDCLIENT, {0} },
-	{ "raise", kbfunc_client_raise, KBFLAG_NEEDCLIENT, {0} },
+	{ "lower", kbfunc_client_lower, CWM_WIN, {0} },
+	{ "raise", kbfunc_client_raise, CWM_WIN, {0} },
 	{ "search", kbfunc_client_search, 0, {0} },
 	{ "menusearch", kbfunc_menu_search, 0, {0} },
-	{ "hide", kbfunc_client_hide, KBFLAG_NEEDCLIENT, {0} },
+	{ "hide", kbfunc_client_hide, CWM_WIN, {0} },
 	{ "cycle", kbfunc_client_cycle, 0, {.i = CWM_CYCLE} },
 	{ "rcycle", kbfunc_client_cycle, 0, {.i = CWM_RCYCLE} },
-	{ "label", kbfunc_client_label, KBFLAG_NEEDCLIENT, {0} },
-	{ "delete", kbfunc_client_delete, KBFLAG_NEEDCLIENT, {0} },
+	{ "label", kbfunc_client_label, CWM_WIN, {0} },
+	{ "delete", kbfunc_client_delete, CWM_WIN, {0} },
 	{ "group1", kbfunc_client_group, 0, {.i = 1} },
 	{ "group2", kbfunc_client_group, 0, {.i = 2} },
 	{ "group3", kbfunc_client_group, 0, {.i = 3} },
@@ -351,37 +351,28 @@ static struct {
 	{ "grouponly7", kbfunc_client_grouponly, 0, {.i = 7} },
 	{ "grouponly8", kbfunc_client_grouponly, 0, {.i = 8} },
 	{ "grouponly9", kbfunc_client_grouponly, 0, {.i = 9} },
-	{ "movetogroup1", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 1} },
-	{ "movetogroup2", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 2} },
-	{ "movetogroup3", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 3} },
-	{ "movetogroup4", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 4} },
-	{ "movetogroup5", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 5} },
-	{ "movetogroup6", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 6} },
-	{ "movetogroup7", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 7} },
-	{ "movetogroup8", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 8} },
-	{ "movetogroup9", kbfunc_client_movetogroup, KBFLAG_NEEDCLIENT,
-	    {.i = 9} },
+	{ "movetogroup1", kbfunc_client_movetogroup, CWM_WIN, {.i = 1} },
+	{ "movetogroup2", kbfunc_client_movetogroup, CWM_WIN, {.i = 2} },
+	{ "movetogroup3", kbfunc_client_movetogroup, CWM_WIN, {.i = 3} },
+	{ "movetogroup4", kbfunc_client_movetogroup, CWM_WIN, {.i = 4} },
+	{ "movetogroup5", kbfunc_client_movetogroup, CWM_WIN, {.i = 5} },
+	{ "movetogroup6", kbfunc_client_movetogroup, CWM_WIN, {.i = 6} },
+	{ "movetogroup7", kbfunc_client_movetogroup, CWM_WIN, {.i = 7} },
+	{ "movetogroup8", kbfunc_client_movetogroup, CWM_WIN, {.i = 8} },
+	{ "movetogroup9", kbfunc_client_movetogroup, CWM_WIN, {.i = 9} },
 	{ "nogroup", kbfunc_client_nogroup, 0, {0} },
 	{ "cyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_CYCLE} },
 	{ "rcyclegroup", kbfunc_client_cyclegroup, 0, {.i = CWM_RCYCLE} },
-	{ "cycleingroup", kbfunc_client_cycle, KBFLAG_NEEDCLIENT,
+	{ "cycleingroup", kbfunc_client_cycle, CWM_WIN,
 	    {.i = CWM_CYCLE|CWM_INGROUP} },
-	{ "rcycleingroup", kbfunc_client_cycle, KBFLAG_NEEDCLIENT,
+	{ "rcycleingroup", kbfunc_client_cycle, CWM_WIN,
 	    {.i = CWM_RCYCLE|CWM_INGROUP} },
-	{ "grouptoggle", kbfunc_client_grouptoggle, KBFLAG_NEEDCLIENT, {0}},
-	{ "fullscreen", kbfunc_client_fullscreen, KBFLAG_NEEDCLIENT, {0} },
-	{ "maximize", kbfunc_client_maximize, KBFLAG_NEEDCLIENT, {0} },
-	{ "vmaximize", kbfunc_client_vmaximize, KBFLAG_NEEDCLIENT, {0} },
-	{ "hmaximize", kbfunc_client_hmaximize, KBFLAG_NEEDCLIENT, {0} },
-	{ "freeze", kbfunc_client_freeze, KBFLAG_NEEDCLIENT, {0} },
+	{ "grouptoggle", kbfunc_client_grouptoggle, CWM_WIN, {0}},
+	{ "fullscreen", kbfunc_client_fullscreen, CWM_WIN, {0} },
+	{ "maximize", kbfunc_client_maximize, CWM_WIN, {0} },
+	{ "vmaximize", kbfunc_client_vmaximize, CWM_WIN, {0} },
+	{ "hmaximize", kbfunc_client_hmaximize, CWM_WIN, {0} },
+	{ "freeze", kbfunc_client_freeze, CWM_WIN, {0} },
 	{ "restart", kbfunc_restart, 0, {0} },
 	{ "quit", kbfunc_quit_wm, 0, {0} },
 	{ "exec", kbfunc_exec, 0, {.i = CWM_EXEC_PROGRAM} },
@@ -389,37 +380,37 @@ static struct {
 	{ "ssh", kbfunc_ssh, 0, {0} },
 	{ "terminal", kbfunc_term, 0, {0} },
 	{ "lock", kbfunc_lock, 0, {0} },
-	{ "moveup", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "moveup", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_UP|CWM_MOVE)} },
-	{ "movedown", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "movedown", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_DOWN|CWM_MOVE)} },
-	{ "moveright", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "moveright", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_RIGHT|CWM_MOVE)} },
-	{ "moveleft", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "moveleft", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_LEFT|CWM_MOVE)} },
-	{ "bigmoveup", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigmoveup", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_UP|CWM_MOVE|CWM_BIGMOVE)} },
-	{ "bigmovedown", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigmovedown", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_DOWN|CWM_MOVE|CWM_BIGMOVE)} },
-	{ "bigmoveright", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigmoveright", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_RIGHT|CWM_MOVE|CWM_BIGMOVE)} },
-	{ "bigmoveleft", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigmoveleft", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_LEFT|CWM_MOVE|CWM_BIGMOVE)} },
-	{ "resizeup", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "resizeup", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_UP|CWM_RESIZE)} },
-	{ "resizedown", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "resizedown", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_DOWN|CWM_RESIZE)} },
-	{ "resizeright", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "resizeright", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_RIGHT|CWM_RESIZE)} },
-	{ "resizeleft", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "resizeleft", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_LEFT|CWM_RESIZE)} },
-	{ "bigresizeup", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigresizeup", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_UP|CWM_RESIZE|CWM_BIGMOVE)} },
-	{ "bigresizedown", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigresizedown", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_DOWN|CWM_RESIZE|CWM_BIGMOVE)} },
-	{ "bigresizeright", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigresizeright", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_RIGHT|CWM_RESIZE|CWM_BIGMOVE)} },
-	{ "bigresizeleft", kbfunc_client_moveresize, KBFLAG_NEEDCLIENT,
+	{ "bigresizeleft", kbfunc_client_moveresize, CWM_WIN,
 	    {.i = (CWM_LEFT|CWM_RESIZE|CWM_BIGMOVE)} },
 	{ "ptrmoveup", kbfunc_client_moveresize, 0,
 	    {.i = (CWM_UP|CWM_PTRMOVE)} },
@@ -437,15 +428,13 @@ static struct {
 	    {.i = (CWM_LEFT|CWM_PTRMOVE|CWM_BIGMOVE)} },
 	{ "bigptrmoveright", kbfunc_client_moveresize, 0,
 	    {.i = (CWM_RIGHT|CWM_PTRMOVE|CWM_BIGMOVE)} },
-	{ "htile", kbfunc_tile, KBFLAG_NEEDCLIENT,
-	    {.i = CWM_TILE_HORIZ } },
-	{ "vtile", kbfunc_tile, KBFLAG_NEEDCLIENT,
-	    {.i = CWM_TILE_VERT } },
+	{ "htile", kbfunc_tile, CWM_WIN, {.i = CWM_TILE_HORIZ} },
+	{ "vtile", kbfunc_tile, CWM_WIN, {.i = CWM_TILE_VERT} },
 };
 
-static struct {
-	char	ch;
-	int	mask;
+static const struct {
+	const char	ch;
+	int		mask;
 } bind_mods[] = {
 	{ 'C',	ControlMask },
 	{ 'M',	Mod1Mask },
@@ -472,48 +461,50 @@ conf_bind_getmask(const char *name, unsigned int *mask)
 	return (dash + 1);
 }
 
-void
-conf_bind_kbd(struct conf *c, char *name, char *binding)
+int
+conf_bind_kbd(struct conf *c, const char *bind, const char *cmd)
 {
-	struct keybinding	*current_binding;
-	const char		*substring;
-	unsigned int			 i, mask;
-
-	current_binding = xcalloc(1, sizeof(*current_binding));
-	substring = conf_bind_getmask(name, &mask);
-	current_binding->modmask |= mask;
-
-	current_binding->keysym = XStringToKeysym(substring);
-	if (current_binding->keysym == NoSymbol) {
-		free(current_binding);
-		return;
+	struct keybinding	*kb;
+	const char		*key;
+	unsigned int		 i, mask;
+
+	kb = xcalloc(1, sizeof(*kb));
+	key = conf_bind_getmask(bind, &mask);
+	kb->modmask |= mask;
+
+	kb->keysym = XStringToKeysym(key);
+	if (kb->keysym == NoSymbol) {
+		warnx("unknown symbol: %s", key);
+		free(kb);
+		return (0);
 	}
 
 	/* We now have the correct binding, remove duplicates. */
-	conf_unbind_kbd(c, current_binding);
+	conf_unbind_kbd(c, kb);
 
-	if (strcmp("unmap", binding) == 0) {
-		free(current_binding);
-		return;
+	if (strcmp("unmap", cmd) == 0) {
+		free(kb);
+		return (1);
 	}
 
 	for (i = 0; i < nitems(name_to_kbfunc); i++) {
-		if (strcmp(name_to_kbfunc[i].tag, binding) != 0)
+		if (strcmp(name_to_kbfunc[i].tag, cmd) != 0)
 			continue;
 
-		current_binding->callback = name_to_kbfunc[i].handler;
-		current_binding->flags = name_to_kbfunc[i].flags;
-		current_binding->argument = name_to_kbfunc[i].argument;
-		current_binding->argtype |= ARG_INT;
-		TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
-		return;
+		kb->callback = name_to_kbfunc[i].handler;
+		kb->flags = name_to_kbfunc[i].flags;
+		kb->argument = name_to_kbfunc[i].argument;
+		kb->argtype |= ARG_INT;
+		TAILQ_INSERT_TAIL(&c->keybindingq, kb, entry);
+		return (1);
 	}
 
-	current_binding->callback = kbfunc_cmdexec;
-	current_binding->flags = 0;
-	current_binding->argument.c = xstrdup(binding);
-	current_binding->argtype |= ARG_CHAR;
-	TAILQ_INSERT_TAIL(&c->keybindingq, current_binding, entry);
+	kb->callback = kbfunc_cmdexec;
+	kb->flags = 0;
+	kb->argument.c = xstrdup(cmd);
+	kb->argtype |= ARG_CHAR;
+	TAILQ_INSERT_TAIL(&c->keybindingq, kb, entry);
+	return (1);
 }
 
 static void
@@ -534,73 +525,59 @@ conf_unbind_kbd(struct conf *c, struct keybinding *unbind)
 	}
 }
 
-static struct {
-	char		*tag;
+static const struct {
+	const char	*tag;
 	void		 (*handler)(struct client_ctx *, union arg *);
 	int		 flags;
 	union arg	 argument;
 } name_to_mousefunc[] = {
-	{ "window_move", mousefunc_client_move, MOUSEBIND_CTX_WIN, {0} },
-	{ "window_resize", mousefunc_client_resize, MOUSEBIND_CTX_WIN, {0} },
-	{ "window_grouptoggle", mousefunc_client_grouptoggle,
-	    MOUSEBIND_CTX_WIN, {0} },
-	{ "window_lower", mousefunc_client_lower, MOUSEBIND_CTX_WIN, {0} },
-	{ "window_raise", mousefunc_client_raise, MOUSEBIND_CTX_WIN, {0} },
-	{ "window_hide", mousefunc_client_hide, MOUSEBIND_CTX_WIN, {0} },
-	{ "cyclegroup", mousefunc_client_cyclegroup, MOUSEBIND_CTX_ROOT, {0} },
-	{ "rcyclegroup", mousefunc_client_rcyclegroup,
-	    MOUSEBIND_CTX_ROOT, {0} },
-	{ "menu_group", mousefunc_menu_group, MOUSEBIND_CTX_ROOT, {0} },
-	{ "menu_unhide", mousefunc_menu_unhide, MOUSEBIND_CTX_ROOT, {0} },
-	{ "menu_cmd", mousefunc_menu_cmd, MOUSEBIND_CTX_ROOT, {0} },
-};
-
-static unsigned int mouse_btns[] = {
-	Button1, Button2, Button3, Button4, Button5
+	{ "window_move", mousefunc_client_move, CWM_WIN, {0} },
+	{ "window_resize", mousefunc_client_resize, CWM_WIN, {0} },
+	{ "window_grouptoggle", mousefunc_client_grouptoggle, CWM_WIN, {0} },
+	{ "window_lower", mousefunc_client_lower, CWM_WIN, {0} },
+	{ "window_raise", mousefunc_client_raise, CWM_WIN, {0} },
+	{ "window_hide", mousefunc_client_hide, CWM_WIN, {0} },
+	{ "cyclegroup", mousefunc_client_cyclegroup, 0, {.i = CWM_CYCLE} },
+	{ "rcyclegroup", mousefunc_client_cyclegroup, 0, {.i = CWM_RCYCLE} },
+	{ "menu_group", mousefunc_menu_group, 0, {0} },
+	{ "menu_unhide", mousefunc_menu_unhide, 0, {0} },
+	{ "menu_cmd", mousefunc_menu_cmd, 0, {0} },
 };
 
 int
-conf_bind_mouse(struct conf *c, char *name, char *binding)
+conf_bind_mouse(struct conf *c, const char *bind, const char *cmd)
 {
-	struct mousebinding	*current_binding;
-	const char		*errstr, *substring;
-	unsigned int		 button, i, mask;
-
-	current_binding = xcalloc(1, sizeof(*current_binding));
-	substring = conf_bind_getmask(name, &mask);
-	current_binding->modmask |= mask;
+	struct mousebinding	*mb;
+	const char		*button, *errstr;
+	unsigned int		 i, mask;
 
-	button = strtonum(substring, 1, 5, &errstr);
-	if (errstr)
-		warnx("button number is %s: %s", errstr, substring);
+	mb = xcalloc(1, sizeof(*mb));
+	button = conf_bind_getmask(bind, &mask);
+	mb->modmask |= mask;
 
-	for (i = 0; i < nitems(mouse_btns); i++) {
-		if (button == mouse_btns[i]) {
-			current_binding->button = button;
-			break;
-		}
-	}
-	if (!current_binding->button || errstr) {
-		free(current_binding);
+	mb->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, current_binding);
+	conf_unbind_mouse(c, mb);
 
-	if (strcmp("unmap", binding) == 0) {
-		free(current_binding);
+	if (strcmp("unmap", cmd) == 0) {
+		free(mb);
 		return (1);
 	}
 
 	for (i = 0; i < nitems(name_to_mousefunc); i++) {
-		if (strcmp(name_to_mousefunc[i].tag, binding) != 0)
+		if (strcmp(name_to_mousefunc[i].tag, cmd) != 0)
 			continue;
 
-		current_binding->callback = name_to_mousefunc[i].handler;
-		current_binding->flags = name_to_mousefunc[i].flags;
-		current_binding->argument = name_to_mousefunc[i].argument;
-		TAILQ_INSERT_TAIL(&c->mousebindingq, current_binding, entry);
+		mb->callback = name_to_mousefunc[i].handler;
+		mb->flags = name_to_mousefunc[i].flags;
+		mb->argument = name_to_mousefunc[i].argument;
+		TAILQ_INSERT_TAIL(&c->mousebindingq, mb, entry);
 		return (1);
 	}
 
@@ -648,9 +625,8 @@ conf_grab_mouse(Window win)
 	xu_btn_ungrab(win);
 
 	TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
-		if (mb->flags != MOUSEBIND_CTX_WIN)
-			continue;
-		xu_btn_grab(win, mb->modmask, mb->button);
+		if (mb->flags & CWM_WIN)
+			xu_btn_grab(win, mb->modmask, mb->button);
 	}
 }
 
diff --git a/group.c b/group.c
index 12a30e1..fe5eb3a 100644
--- a/group.c
+++ b/group.c
@@ -324,15 +324,8 @@ group_menu(struct screen_ctx *sc)
 		if (TAILQ_EMPTY(&gc->clients))
 			continue;
 
-		mi = xcalloc(1, sizeof(*mi));
-		if (gc->hidden)
-			(void)snprintf(mi->text, sizeof(mi->text), "%d: [%s]",
-			    gc->shortcut, sc->group_names[i]);
-		else
-			(void)snprintf(mi->text, sizeof(mi->text), "%d: %s",
-			    gc->shortcut, sc->group_names[i]);
-		mi->ctx = gc;
-		TAILQ_INSERT_TAIL(&menuq, mi, entry);
+		menuq_add(&menuq, gc, gc->hidden ? "%d: [%s]" : "%d: %s",
+		    gc->shortcut, sc->group_names[i]);
 	}
 
 	if (TAILQ_EMPTY(&menuq))
diff --git a/kbfunc.c b/kbfunc.c
index 1f7d57a..26e6d3d 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -41,6 +41,7 @@ extern sig_atomic_t	xev_quit;
 void
 kbfunc_client_lower(struct client_ctx *cc, union arg *arg)
 {
+	client_ptrsave(cc);
 	client_lower(cc);
 }
 
@@ -151,13 +152,8 @@ kbfunc_client_search(struct client_ctx *cc, union arg *arg)
 	old_cc = client_current();
 
 	TAILQ_INIT(&menuq);
-
-	TAILQ_FOREACH(cc, &Clientq, entry) {
-		mi = xcalloc(1, sizeof(*mi));
-		(void)strlcpy(mi->text, cc->name, sizeof(mi->text));
-		mi->ctx = cc;
-		TAILQ_INSERT_TAIL(&menuq, mi, entry);
-	}
+	TAILQ_FOREACH(cc, &Clientq, entry)
+		menuq_add(&menuq, cc, "%s", cc->name);
 
 	if ((mi = menu_filter(sc, &menuq, "window", NULL, 0,
 	    search_match_client, search_print_client)) != NULL) {
@@ -182,17 +178,12 @@ kbfunc_menu_search(struct client_ctx *cc, union arg *arg)
 	struct menu_q		 menuq;
 
 	TAILQ_INIT(&menuq);
-
-	TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
-		mi = xcalloc(1, sizeof(*mi));
-		(void)strlcpy(mi->text, cmd->label, sizeof(mi->text));
-		mi->ctx = cmd;
-		TAILQ_INSERT_TAIL(&menuq, mi, entry);
-	}
+	TAILQ_FOREACH(cmd, &Conf.cmdq, entry)
+		menuq_add(&menuq, cmd, "%s", cmd->name);
 
 	if ((mi = menu_filter(sc, &menuq, "application", NULL, 0,
 	    search_match_text, NULL)) != NULL)
-		u_spawn(((struct cmd *)mi->ctx)->image);
+		u_spawn(((struct cmd *)mi->ctx)->path);
 
 	menuq_clear(&menuq);
 }
@@ -238,8 +229,9 @@ kbfunc_exec(struct client_ctx *cc, union arg *arg)
 {
 #define NPATHS 256
 	struct screen_ctx	*sc = cc->sc;
-	char			**ap, *paths[NPATHS], *path, *pathcpy, *label;
+	char			**ap, *paths[NPATHS], *path, *pathcpy;
 	char			 tpath[MAXPATHLEN];
+	const char		*label;
 	DIR			*dirp;
 	struct dirent		*dp;
 	struct menu		*mi;
@@ -284,12 +276,8 @@ kbfunc_exec(struct client_ctx *cc, union arg *arg)
 			/* check for truncation etc */
 			if (l == -1 || l >= (int)sizeof(tpath))
 				continue;
-			if (access(tpath, X_OK) == 0) {
-				mi = xcalloc(1, sizeof(*mi));
-				(void)strlcpy(mi->text,
-				    dp->d_name, sizeof(mi->text));
-				TAILQ_INSERT_TAIL(&menuq, mi, entry);
-			}
+			if (access(tpath, X_OK) == 0)
+				menuq_add(&menuq, NULL, "%s", dp->d_name);
 		}
 		(void)closedir(dirp);
 	}
@@ -360,9 +348,7 @@ kbfunc_ssh(struct client_ctx *cc, union arg *arg)
 		if (p - buf + 1 > sizeof(hostbuf))
 			continue;
 		(void)strlcpy(hostbuf, buf, p - buf + 1);
-		mi = xcalloc(1, sizeof(*mi));
-		(void)strlcpy(mi->text, hostbuf, sizeof(mi->text));
-		TAILQ_INSERT_TAIL(&menuq, mi, entry);
+		menuq_add(&menuq, NULL, hostbuf);
 	}
 	free(lbuf);
 	(void)fclose(fp);
diff --git a/menu.c b/menu.c
index 7e3cb7b..fc90e35 100644
--- a/menu.c
+++ b/menu.c
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -77,8 +78,8 @@ static struct menu 	*menu_complete_path(struct menu_ctx *);
 static int		 menu_keycode(XKeyEvent *, enum ctltype *, char *);
 
 struct menu *
-menu_filter(struct screen_ctx *sc, struct menu_q *menuq, char *prompt,
-    char *initial, int flags,
+menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
+    const char *initial, int flags,
     void (*match)(struct menu_q *, struct menu_q *, char *),
     void (*print)(struct menu *, int))
 {
@@ -605,6 +606,22 @@ menu_keycode(XKeyEvent *ev, enum ctltype *ctl, char *chr)
 }
 
 void
+menuq_add(struct menu_q *mq, void *ctx, const char *fmt, ...)
+{
+	va_list		 ap;
+	struct menu	*mi;
+
+	mi = xcalloc(1, sizeof(*mi));
+	mi->ctx = ctx;
+
+	va_start(ap, fmt);
+	(void)vsnprintf(mi->text, sizeof(mi->text), fmt, ap);
+	va_end(ap);
+
+	TAILQ_INSERT_TAIL(mq, mi, entry);
+}
+
+void
 menuq_clear(struct menu_q *mq)
 {
 	struct menu	*mi;
diff --git a/mousefunc.c b/mousefunc.c
index fd66edb..4d77482 100644
--- a/mousefunc.c
+++ b/mousefunc.c
@@ -200,13 +200,7 @@ mousefunc_client_hide(struct client_ctx *cc, union arg *arg)
 void
 mousefunc_client_cyclegroup(struct client_ctx *cc, union arg *arg)
 {
-	group_cycle(cc->sc, CWM_CYCLE);
-}
-
-void
-mousefunc_client_rcyclegroup(struct client_ctx *cc, union arg *arg)
-{
-	group_cycle(cc->sc, CWM_RCYCLE);
+	group_cycle(cc->sc, arg->i);
 }
 
 void
@@ -234,11 +228,8 @@ mousefunc_menu_unhide(struct client_ctx *cc, union arg *arg)
 			if (wname == NULL)
 				continue;
 
-			mi = xcalloc(1, sizeof(*mi));
-			(void)snprintf(mi->text, sizeof(mi->text), "(%d) %s",
+			menuq_add(&menuq, cc, "(%d) %s",
 			    cc->group ? cc->group->shortcut : 0, wname);
-			mi->ctx = cc;
-			TAILQ_INSERT_TAIL(&menuq, mi, entry);
 		}
 
 	if (TAILQ_EMPTY(&menuq))
@@ -267,18 +258,14 @@ mousefunc_menu_cmd(struct client_ctx *cc, union arg *arg)
 
 	TAILQ_INIT(&menuq);
 
-	TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
-		mi = xcalloc(1, sizeof(*mi));
-		(void)strlcpy(mi->text, cmd->label, sizeof(mi->text));
-		mi->ctx = cmd;
-		TAILQ_INSERT_TAIL(&menuq, mi, entry);
-	}
+	TAILQ_FOREACH(cmd, &Conf.cmdq, entry)
+		menuq_add(&menuq, cmd, "%s", cmd->name);
 	if (TAILQ_EMPTY(&menuq))
 		return;
 
 	mi = menu_filter(sc, &menuq, NULL, NULL, 0, NULL, NULL);
 	if (mi != NULL)
-		u_spawn(((struct cmd *)mi->ctx)->image);
+		u_spawn(((struct cmd *)mi->ctx)->path);
 
 	menuq_clear(&menuq);
 }
diff --git a/parse.y b/parse.y
index c7a802a..188c090 100644
--- a/parse.y
+++ b/parse.y
@@ -139,7 +139,7 @@ main		: FONTNAME STRING		{
 			conf->snapdist = $2;
 		}
 		| COMMAND STRING string		{
-			conf_cmd_add(conf, $3, $2);
+			conf_cmd_add(conf, $2, $3);
 			free($2);
 			free($3);
 		}
@@ -157,7 +157,12 @@ main		: FONTNAME STRING		{
 			free($2);
 		}
 		| BIND STRING string		{
-			conf_bind_kbd(conf, $2, $3);
+			if (!conf_bind_kbd(conf, $2, $3)) {
+				yyerror("invalid bind: %s %s", $2, $3);
+				free($2);
+				free($3);
+				YYERROR;
+			}
 			free($2);
 			free($3);
 		}
diff --git a/search.c b/search.c
index c34e0b0..4424a8e 100644
--- a/search.c
+++ b/search.c
@@ -172,10 +172,9 @@ 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)
 {
-	struct menu	*mi;
-	char 		 pattern[MAXPATHLEN];
-	glob_t		 g;
-	int		 i;
+	char 	 pattern[MAXPATHLEN];
+	glob_t	 g;
+	int	 i;
 
 	TAILQ_INIT(resultq);
 
@@ -187,9 +186,7 @@ search_match_path(struct menu_q *menuq, struct menu_q *resultq, char *search, in
 	for (i = 0; i < g.gl_pathc; i++) {
 		if ((flag & PATH_EXEC) && access(g.gl_pathv[i], X_OK))
 			continue;
-		mi = xcalloc(1, sizeof(*mi));
-		(void)strlcpy(mi->text, g.gl_pathv[i], sizeof(mi->text));
-		TAILQ_INSERT_TAIL(resultq, mi, resultentry);
+		menuq_add(resultq, NULL, "%s", g.gl_pathv[i]);
 	}
 	globfree(&g);
 }
diff --git a/xevents.c b/xevents.c
index 4b77e82..55dabab 100644
--- a/xevents.c
+++ b/xevents.c
@@ -235,11 +235,11 @@ xev_handle_buttonpress(XEvent *ee)
 
 	if (mb == NULL)
 		return;
-	if (mb->flags == MOUSEBIND_CTX_WIN) {
+	if (mb->flags & CWM_WIN) {
 		if (((cc = client_find(e->window)) == NULL) &&
 		    (cc = client_current()) == NULL)
 			return;
-	} else { /* (mb->flags == MOUSEBIND_CTX_ROOT) */
+	} else {
 		if (e->window != e->root)
 			return;
 		cc = &fakecc;
@@ -287,7 +287,7 @@ xev_handle_keypress(XEvent *ee)
 
 	if (kb == NULL)
 		return;
-	if (kb->flags & KBFLAG_NEEDCLIENT) {
+	if (kb->flags & CWM_WIN) {
 		if (((cc = client_find(e->window)) == NULL) &&
 		    (cc = client_current()) == NULL)
 			return;