mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Support for multiple key tables to commands to be bound to sequences of
keys. The default key bindings become the "prefix" table and -n the "root" table. Keys may be bound in new tables with bind -T and switch-client -T used to specify the table in which the next key should be looked up. Based on a diff from Keith Amling.
This commit is contained in:
		| @@ -33,8 +33,8 @@ enum cmd_retval	 cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int); | ||||
|  | ||||
| const struct cmd_entry cmd_bind_key_entry = { | ||||
| 	"bind-key", "bind", | ||||
| 	"cnrt:", 1, -1, | ||||
| 	"[-cnr] [-t mode-table] key command [arguments]", | ||||
| 	"cnrt:T:", 1, -1, | ||||
| 	"[-cnr] [-t mode-table] [-T key-table] key command [arguments]", | ||||
| 	0, | ||||
| 	cmd_bind_key_exec | ||||
| }; | ||||
| @@ -46,6 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 	char		*cause; | ||||
| 	struct cmd_list	*cmdlist; | ||||
| 	int		 key; | ||||
| 	const char	*tablename; | ||||
|  | ||||
| 	if (args_has(args, 't')) { | ||||
| 		if (args->argc != 2 && args->argc != 3) { | ||||
| @@ -68,6 +69,13 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 	if (args_has(args, 't')) | ||||
| 		return (cmd_bind_key_mode_table(self, cmdq, key)); | ||||
|  | ||||
| 	if (args_has(args, 'T')) | ||||
| 		tablename = args_get(args, 'T'); | ||||
| 	else if (args_has(args, 'n')) | ||||
| 		tablename = "root"; | ||||
| 	else | ||||
| 		tablename = "prefix"; | ||||
|  | ||||
| 	cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, | ||||
| 	    &cause); | ||||
| 	if (cmdlist == NULL) { | ||||
| @@ -76,9 +84,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 		return (CMD_RETURN_ERROR); | ||||
| 	} | ||||
|  | ||||
| 	if (!args_has(args, 'n')) | ||||
| 	    key |= KEYC_PREFIX; | ||||
| 	key_bindings_add(key, args_has(args, 'r'), cmdlist); | ||||
| 	key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist); | ||||
| 	return (CMD_RETURN_NORMAL); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -33,8 +33,8 @@ enum cmd_retval	 cmd_list_keys_commands(struct cmd *, struct cmd_q *); | ||||
|  | ||||
| const struct cmd_entry cmd_list_keys_entry = { | ||||
| 	"list-keys", "lsk", | ||||
| 	"t:", 0, 0, | ||||
| 	"[-t key-table]", | ||||
| 	"t:T:", 0, 0, | ||||
| 	"[-t mode-table] [-T key-table]", | ||||
| 	0, | ||||
| 	cmd_list_keys_exec | ||||
| }; | ||||
| @@ -51,11 +51,12 @@ enum cmd_retval | ||||
| cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| { | ||||
| 	struct args		*args = self->args; | ||||
| 	struct key_table	*table; | ||||
| 	struct key_binding	*bd; | ||||
| 	const char		*key; | ||||
| 	char			 tmp[BUFSIZ], flags[8]; | ||||
| 	const char		*key, *tablename, *r; | ||||
| 	char			 tmp[BUFSIZ]; | ||||
| 	size_t			 used; | ||||
| 	int			 width, keywidth; | ||||
| 	int			 repeat, width, tablewidth, keywidth; | ||||
|  | ||||
| 	if (self->entry == &cmd_list_commands_entry) | ||||
| 		return (cmd_list_keys_commands(self, cmdq)); | ||||
| @@ -63,47 +64,58 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 	if (args_has(args, 't')) | ||||
| 		return (cmd_list_keys_table(self, cmdq)); | ||||
|  | ||||
| 	width = 0; | ||||
|  | ||||
| 	RB_FOREACH(bd, key_bindings, &key_bindings) { | ||||
| 		key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); | ||||
| 		if (key == NULL) | ||||
| 			continue; | ||||
|  | ||||
| 		keywidth = strlen(key); | ||||
| 		if (!(bd->key & KEYC_PREFIX)) { | ||||
| 			if (bd->can_repeat) | ||||
| 				keywidth += 4; | ||||
| 			else | ||||
| 				keywidth += 3; | ||||
| 		} else if (bd->can_repeat) | ||||
| 			keywidth += 3; | ||||
| 		if (keywidth > width) | ||||
| 			width = keywidth; | ||||
| 	tablename = args_get(args, 'T'); | ||||
| 	if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { | ||||
| 		cmdq_error(cmdq, "table %s doesn't exist", tablename); | ||||
| 		return (CMD_RETURN_ERROR); | ||||
| 	} | ||||
|  | ||||
| 	RB_FOREACH(bd, key_bindings, &key_bindings) { | ||||
| 		key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); | ||||
| 	repeat = 0; | ||||
| 	tablewidth = keywidth = 0; | ||||
| 	RB_FOREACH(table, key_tables, &key_tables) { | ||||
| 		if (tablename != NULL && strcmp(table->name, tablename) != 0) | ||||
| 			continue; | ||||
| 		RB_FOREACH(bd, key_bindings, &table->key_bindings) { | ||||
| 			key = key_string_lookup_key(bd->key); | ||||
| 			if (key == NULL) | ||||
| 				continue; | ||||
|  | ||||
| 		*flags = '\0'; | ||||
| 		if (!(bd->key & KEYC_PREFIX)) { | ||||
| 			if (bd->can_repeat) | ||||
| 				xsnprintf(flags, sizeof flags, "-rn "); | ||||
| 			else | ||||
| 				xsnprintf(flags, sizeof flags, "-n "); | ||||
| 		} else if (bd->can_repeat) | ||||
| 			xsnprintf(flags, sizeof flags, "-r "); | ||||
| 				repeat = 1; | ||||
|  | ||||
| 		used = xsnprintf(tmp, sizeof tmp, "%s%*s ", | ||||
| 		    flags, (int) (width - strlen(flags)), key); | ||||
| 		if (used >= sizeof tmp) | ||||
| 			width = strlen(table->name); | ||||
| 			if (width > tablewidth) | ||||
| 				tablewidth =width; | ||||
| 			width = strlen(key); | ||||
| 			if (width > keywidth) | ||||
| 				keywidth = width; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	RB_FOREACH(table, key_tables, &key_tables) { | ||||
| 		if (tablename != NULL && strcmp(table->name, tablename) != 0) | ||||
| 			continue; | ||||
| 		RB_FOREACH(bd, key_bindings, &table->key_bindings) { | ||||
| 			key = key_string_lookup_key(bd->key); | ||||
| 			if (key == NULL) | ||||
| 				continue; | ||||
|  | ||||
| 		cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); | ||||
| 			if (!repeat) | ||||
| 				r = ""; | ||||
| 			else if (bd->can_repeat) | ||||
| 				r = "-r "; | ||||
| 			else | ||||
| 				r = "   "; | ||||
| 			used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r, | ||||
| 			    (int)tablewidth, table->name, (int)keywidth, key); | ||||
| 			if (used < sizeof tmp) { | ||||
| 				cmd_list_print(bd->cmdlist, tmp + used, | ||||
| 				    (sizeof tmp) - used); | ||||
| 			} | ||||
|  | ||||
| 			cmdq_print(cmdq, "bind-key %s", tmp); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return (CMD_RETURN_NORMAL); | ||||
| } | ||||
|   | ||||
| @@ -31,8 +31,8 @@ enum cmd_retval	 cmd_switch_client_exec(struct cmd *, struct cmd_q *); | ||||
|  | ||||
| const struct cmd_entry cmd_switch_client_entry = { | ||||
| 	"switch-client", "switchc", | ||||
| 	"lc:npt:r", 0, 0, | ||||
| 	"[-lnpr] [-c target-client] [-t target-session]", | ||||
| 	"lc:npt:rT:", 0, 0, | ||||
| 	"[-lnpr] [-c target-client] [-t target-session] [-T key-table]", | ||||
| 	CMD_READONLY, | ||||
| 	cmd_switch_client_exec | ||||
| }; | ||||
| @@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 	struct winlink		*wl = NULL; | ||||
| 	struct window 		*w = NULL; | ||||
| 	struct window_pane	*wp = NULL; | ||||
| 	const char		*tflag; | ||||
| 	const char		*tflag, *tablename; | ||||
| 	struct key_table	*table; | ||||
|  | ||||
| 	if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) | ||||
| 		return (CMD_RETURN_ERROR); | ||||
| @@ -58,6 +59,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 			c->flags |= CLIENT_READONLY; | ||||
| 	} | ||||
|  | ||||
| 	tablename = args_get(args, 'T'); | ||||
| 	if (tablename != NULL) { | ||||
| 		table = key_bindings_get_table(tablename, 0); | ||||
| 		if (table == NULL) { | ||||
| 			cmdq_error(cmdq, "table %s doesn't exist", tablename); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 		table->references++; | ||||
| 		key_bindings_unref_table(c->keytable); | ||||
| 		c->keytable = table; | ||||
| 	} | ||||
|  | ||||
| 	tflag = args_get(args, 't'); | ||||
| 	if (args_has(args, 'n')) { | ||||
| 		if ((s = session_next_session(c->session)) == NULL) { | ||||
|   | ||||
| @@ -31,8 +31,8 @@ enum cmd_retval	 cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int); | ||||
|  | ||||
| const struct cmd_entry cmd_unbind_key_entry = { | ||||
| 	"unbind-key", "unbind", | ||||
| 	"acnt:", 0, 1, | ||||
| 	"[-acn] [-t mode-table] key", | ||||
| 	"acnt:T:", 0, 1, | ||||
| 	"[-acn] [-t mode-table] [-T key-table] key", | ||||
| 	0, | ||||
| 	cmd_unbind_key_exec | ||||
| }; | ||||
| @@ -41,8 +41,8 @@ enum cmd_retval | ||||
| cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| { | ||||
| 	struct args	*args = self->args; | ||||
| 	struct key_binding	*bd; | ||||
| 	int		 key; | ||||
| 	const char	*tablename; | ||||
|  | ||||
| 	if (!args_has(args, 'a')) { | ||||
| 		if (args->argc != 1) { | ||||
| @@ -66,16 +66,31 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) | ||||
| 		return (cmd_unbind_key_mode_table(self, cmdq, key)); | ||||
|  | ||||
| 	if (key == KEYC_NONE) { | ||||
| 		while (!RB_EMPTY(&key_bindings)) { | ||||
| 			bd = RB_ROOT(&key_bindings); | ||||
| 			key_bindings_remove(bd->key); | ||||
| 		tablename = args_get(args, 'T'); | ||||
| 		if (tablename == NULL) { | ||||
| 			key_bindings_remove_table("root"); | ||||
| 			key_bindings_remove_table("prefix"); | ||||
| 			return (CMD_RETURN_NORMAL); | ||||
| 		} | ||||
| 		if (key_bindings_get_table(tablename, 0) == NULL) { | ||||
| 			cmdq_error(cmdq, "table %s doesn't exist", tablename); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 		key_bindings_remove_table(tablename); | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	} | ||||
|  | ||||
| 	if (!args_has(args, 'n')) | ||||
| 		key |= KEYC_PREFIX; | ||||
| 	key_bindings_remove(key); | ||||
| 	if (args_has(args, 'T')) { | ||||
| 		tablename = args_get(args, 'T'); | ||||
| 		if (key_bindings_get_table(tablename, 0) == NULL) { | ||||
| 			cmdq_error(cmdq, "table %s doesn't exist", tablename); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 	} else if (args_has(args, 'n')) | ||||
| 		tablename = "root"; | ||||
| 	else | ||||
| 		tablename = "prefix"; | ||||
| 	key_bindings_remove(tablename, key); | ||||
| 	return (CMD_RETURN_NORMAL); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										6
									
								
								format.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								format.c
									
									
									
									
									
								
							| @@ -545,7 +545,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) | ||||
| 	format_add(ft, "client_activity", "%lld", (long long) t); | ||||
| 	format_add(ft, "client_activity_string", "%s", format_time_string(t)); | ||||
|  | ||||
| 	format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); | ||||
| 	if (strcmp(c->keytable->name, "root") == 0) | ||||
| 		format_add(ft, "client_prefix", "%d", 0); | ||||
| 	else | ||||
| 		format_add(ft, "client_prefix", "%d", 1); | ||||
| 	format_add(ft, "client_key_table", "%s", c->keytable->name); | ||||
|  | ||||
| 	if (c->tty.flags & TTY_UTF8) | ||||
| 		format_add(ft, "client_utf8", "%d", 1); | ||||
|   | ||||
							
								
								
									
										112
									
								
								key-bindings.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								key-bindings.c
									
									
									
									
									
								
							| @@ -25,60 +25,120 @@ | ||||
| #include "tmux.h" | ||||
|  | ||||
| RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); | ||||
| RB_GENERATE(key_tables, key_table, entry, key_table_cmp); | ||||
| struct key_tables key_tables = RB_INITIALIZER(&key_tables); | ||||
|  | ||||
| struct key_bindings	key_bindings; | ||||
| int | ||||
| key_table_cmp(struct key_table *e1, struct key_table *e2) | ||||
| { | ||||
| 	return (strcmp(e1->name, e2->name)); | ||||
| } | ||||
|  | ||||
| int | ||||
| key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) | ||||
| { | ||||
| 	int	key1, key2; | ||||
|  | ||||
| 	key1 = bd1->key & ~KEYC_PREFIX; | ||||
| 	key2 = bd2->key & ~KEYC_PREFIX; | ||||
| 	if (key1 != key2) | ||||
| 		return (key1 - key2); | ||||
|  | ||||
| 	if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) | ||||
| 		return (-1); | ||||
| 	if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) | ||||
| 		return (1); | ||||
| 	return (0); | ||||
| 	return (bd1->key - bd2->key); | ||||
| } | ||||
|  | ||||
| struct key_binding * | ||||
| key_bindings_lookup(int key) | ||||
| struct key_table * | ||||
| key_bindings_get_table(const char *name, int create) | ||||
| { | ||||
| 	struct key_binding	bd; | ||||
| 	struct key_table	table_find, *table; | ||||
|  | ||||
| 	bd.key = key; | ||||
| 	return (RB_FIND(key_bindings, &key_bindings, &bd)); | ||||
| 	table_find.name = name; | ||||
| 	table = RB_FIND(key_tables, &key_tables, &table_find); | ||||
| 	if (table != NULL || !create) | ||||
| 		return (table); | ||||
|  | ||||
| 	table = xmalloc(sizeof *table); | ||||
| 	table->name = xstrdup(name); | ||||
| 	RB_INIT(&table->key_bindings); | ||||
|  | ||||
| 	table->references = 1; /* one reference in key_tables */ | ||||
| 	RB_INSERT(key_tables, &key_tables, table); | ||||
|  | ||||
| 	return (table); | ||||
| } | ||||
|  | ||||
| void | ||||
| key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) | ||||
| key_bindings_unref_table(struct key_table *table) | ||||
| { | ||||
| 	struct key_binding	*bd; | ||||
|  | ||||
| 	key_bindings_remove(key); | ||||
| 	if (--table->references != 0) | ||||
| 		return; | ||||
|  | ||||
| 	while (!RB_EMPTY(&table->key_bindings)) { | ||||
| 		bd = RB_ROOT(&table->key_bindings); | ||||
| 		RB_REMOVE(key_bindings, &table->key_bindings, bd); | ||||
| 		cmd_list_free(bd->cmdlist); | ||||
| 		free(bd); | ||||
| 	} | ||||
|  | ||||
| 	free((void *)table->name); | ||||
| 	free(table); | ||||
| } | ||||
|  | ||||
| void | ||||
| key_bindings_add(const char *name, int key, int can_repeat, | ||||
|     struct cmd_list *cmdlist) | ||||
| { | ||||
| 	struct key_table	*table; | ||||
| 	struct key_binding	 bd_find, *bd; | ||||
|  | ||||
| 	table = key_bindings_get_table(name, 1); | ||||
|  | ||||
| 	bd_find.key = key; | ||||
| 	bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); | ||||
| 	if (bd != NULL) { | ||||
| 		RB_REMOVE(key_bindings, &table->key_bindings, bd); | ||||
| 		cmd_list_free(bd->cmdlist); | ||||
| 		free(bd); | ||||
| 	} | ||||
|  | ||||
| 	bd = xmalloc(sizeof *bd); | ||||
| 	bd->key = key; | ||||
| 	RB_INSERT(key_bindings, &key_bindings, bd); | ||||
| 	RB_INSERT(key_bindings, &table->key_bindings, bd); | ||||
|  | ||||
| 	bd->can_repeat = can_repeat; | ||||
| 	bd->cmdlist = cmdlist; | ||||
| } | ||||
|  | ||||
| void | ||||
| key_bindings_remove(int key) | ||||
| key_bindings_remove(const char *name, int key) | ||||
| { | ||||
| 	struct key_binding	*bd; | ||||
| 	struct key_table	*table; | ||||
| 	struct key_binding	 bd_find, *bd; | ||||
|  | ||||
| 	if ((bd = key_bindings_lookup(key)) == NULL) | ||||
| 	table = key_bindings_get_table(name, 0); | ||||
| 	if (table == NULL) | ||||
| 		return; | ||||
| 	RB_REMOVE(key_bindings, &key_bindings, bd); | ||||
|  | ||||
| 	bd_find.key = key; | ||||
| 	bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); | ||||
| 	if (bd == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	RB_REMOVE(key_bindings, &table->key_bindings, bd); | ||||
| 	cmd_list_free(bd->cmdlist); | ||||
| 	free(bd); | ||||
|  | ||||
| 	if (RB_EMPTY(&table->key_bindings)) { | ||||
| 		RB_REMOVE(key_tables, &key_tables, table); | ||||
| 		key_bindings_unref_table(table); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| key_bindings_remove_table(const char *name) | ||||
| { | ||||
| 	struct key_table	*table; | ||||
|  | ||||
| 	table = key_bindings_get_table(name, 0); | ||||
| 	if (table != NULL) { | ||||
| 		RB_REMOVE(key_tables, &key_tables, table); | ||||
| 		key_bindings_unref_table(table); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -169,8 +229,6 @@ key_bindings_init(void) | ||||
| 	int		 error; | ||||
| 	struct cmd_q	*cmdq; | ||||
|  | ||||
| 	RB_INIT(&key_bindings); | ||||
|  | ||||
| 	cmdq = cmdq_new(NULL); | ||||
| 	for (i = 0; i < nitems(defaults); i++) { | ||||
| 		error = cmd_string_parse(defaults[i], &cmdlist, | ||||
|   | ||||
							
								
								
									
										146
									
								
								server-client.c
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								server-client.c
									
									
									
									
									
								
							| @@ -30,6 +30,7 @@ | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| void	server_client_key_table(struct client *, const char *); | ||||
| void	server_client_check_focus(struct window_pane *); | ||||
| void	server_client_check_resize(struct window_pane *); | ||||
| int	server_client_check_mouse(struct client *); | ||||
| @@ -45,6 +46,15 @@ void	server_client_msg_command(struct client *, struct imsg *); | ||||
| void	server_client_msg_identify(struct client *, struct imsg *); | ||||
| void	server_client_msg_shell(struct client *); | ||||
|  | ||||
| /* Set client key table. */ | ||||
| void | ||||
| server_client_key_table(struct client *c, const char *name) | ||||
| { | ||||
| 	key_bindings_unref_table(c->keytable); | ||||
| 	c->keytable = key_bindings_get_table(name, 1); | ||||
| 	c->keytable->references++; | ||||
| } | ||||
|  | ||||
| /* Create a new client. */ | ||||
| void | ||||
| server_client_create(int fd) | ||||
| @@ -93,6 +103,9 @@ server_client_create(int fd) | ||||
|  | ||||
| 	c->flags |= CLIENT_FOCUSED; | ||||
|  | ||||
| 	c->keytable = key_bindings_get_table("root", 1); | ||||
| 	c->keytable->references++; | ||||
|  | ||||
| 	evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) { | ||||
| @@ -164,6 +177,8 @@ server_client_lost(struct client *c) | ||||
|  | ||||
| 	evtimer_del(&c->repeat_timer); | ||||
|  | ||||
| 	key_bindings_unref_table(c->keytable); | ||||
|  | ||||
| 	if (event_initialized(&c->identify_timer)) | ||||
| 		evtimer_del(&c->identify_timer); | ||||
|  | ||||
| @@ -527,16 +542,19 @@ void | ||||
| server_client_handle_key(struct client *c, int key) | ||||
| { | ||||
| 	struct mouse_event	*m = &c->tty.mouse; | ||||
| 	struct session		*s; | ||||
| 	struct session		*s = c->session; | ||||
| 	struct window		*w; | ||||
| 	struct window_pane	*wp; | ||||
| 	struct timeval		 tv; | ||||
| 	struct key_binding	*bd; | ||||
| 	int		      	 xtimeout, isprefix, ispaste; | ||||
| 	struct key_table	*table = c->keytable; | ||||
| 	struct key_binding	 bd_find, *bd; | ||||
| 	int			 xtimeout; | ||||
|  | ||||
| 	/* Check the client is good to accept input. */ | ||||
| 	if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) | ||||
| 	if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) | ||||
| 		return; | ||||
| 	w = s->curw->window; | ||||
| 	wp = w->active; | ||||
|  | ||||
| 	/* No session, do nothing. */ | ||||
| 	if (c->session == NULL) | ||||
| @@ -552,7 +570,7 @@ server_client_handle_key(struct client *c, int key) | ||||
| 	    sizeof s->last_activity_time); | ||||
| 	memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); | ||||
|  | ||||
| 	/* Special case: number keys jump to pane in identify mode. */ | ||||
| 	/* Number keys jump to pane in identify mode. */ | ||||
| 	if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { | ||||
| 		if (c->flags & CLIENT_READONLY) | ||||
| 			return; | ||||
| @@ -593,74 +611,88 @@ server_client_handle_key(struct client *c, int key) | ||||
| 	} else | ||||
| 		m->valid = 0; | ||||
|  | ||||
| 	/* Is this a prefix key? */ | ||||
| 	if (key == options_get_number(&s->options, "prefix")) | ||||
| 		isprefix = 1; | ||||
| 	else if (key == options_get_number(&s->options, "prefix2")) | ||||
| 		isprefix = 1; | ||||
| 	else | ||||
| 		isprefix = 0; | ||||
|  | ||||
| 	/* Treat prefix as a regular key when pasting is detected. */ | ||||
| 	ispaste = server_client_assume_paste(s); | ||||
| 	if (ispaste) | ||||
| 		isprefix = 0; | ||||
|  | ||||
| 	/* No previous prefix key. */ | ||||
| 	if (!(c->flags & CLIENT_PREFIX)) { | ||||
| 		if (isprefix) { | ||||
| 			c->flags |= CLIENT_PREFIX; | ||||
| 			server_status_client(c); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		/* Try as a non-prefix key binding. */ | ||||
| 		if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { | ||||
| 	/* Treat everything as a regular key when pasting is detected. */ | ||||
| 	if (server_client_assume_paste(s)) { | ||||
| 		if (!(c->flags & CLIENT_READONLY)) | ||||
| 			window_pane_key(wp, c, s, key, m); | ||||
| 		} else | ||||
| 			key_bindings_dispatch(bd, c, m); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Prefix key already pressed. Reset prefix and lookup key. */ | ||||
| 	c->flags &= ~CLIENT_PREFIX; | ||||
| retry: | ||||
| 	/* Try to see if there is a key binding in the current table. */ | ||||
| 	bd_find.key = key; | ||||
| 	bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); | ||||
| 	if (bd != NULL) { | ||||
| 		/* | ||||
| 		 * Key was matched in this table. If currently repeating but a | ||||
| 		 * non-repeating binding was found, stop repeating and try | ||||
| 		 * again in the root table. | ||||
| 		 */ | ||||
| 		if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { | ||||
| 			server_client_key_table(c, "root"); | ||||
| 			c->flags &= ~CLIENT_REPEAT; | ||||
| 			server_status_client(c); | ||||
| 	if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { | ||||
| 		/* If repeating, treat this as a key, else ignore. */ | ||||
| 		if (c->flags & CLIENT_REPEAT) { | ||||
| 			c->flags &= ~CLIENT_REPEAT; | ||||
| 			if (isprefix) | ||||
| 				c->flags |= CLIENT_PREFIX; | ||||
| 			else if (!(c->flags & CLIENT_READONLY)) | ||||
| 				window_pane_key(wp, c, s, key, m); | ||||
| 		} | ||||
| 		return; | ||||
| 			goto retry; | ||||
| 		} | ||||
|  | ||||
| 	/* If already repeating, but this key can't repeat, skip it. */ | ||||
| 	if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { | ||||
| 		c->flags &= ~CLIENT_REPEAT; | ||||
| 		if (isprefix) | ||||
| 			c->flags |= CLIENT_PREFIX; | ||||
| 		else if (!(c->flags & CLIENT_READONLY)) | ||||
| 			window_pane_key(wp, c, s, key, m); | ||||
| 		return; | ||||
| 	} | ||||
| 		/* | ||||
| 		 * Take a reference to this table to make sure the key binding | ||||
| 		 * doesn't disappear. | ||||
| 		 */ | ||||
| 		table->references++; | ||||
|  | ||||
| 	/* If this key can repeat, reset the repeat flags and timer. */ | ||||
| 		/* | ||||
| 		 * If this is a repeating key, start the timer. Otherwise reset | ||||
| 		 * the client back to the root table. | ||||
| 		 */ | ||||
| 		xtimeout = options_get_number(&s->options, "repeat-time"); | ||||
| 		if (xtimeout != 0 && bd->can_repeat) { | ||||
| 		c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; | ||||
| 			c->flags |= CLIENT_REPEAT; | ||||
|  | ||||
| 			tv.tv_sec = xtimeout / 1000; | ||||
| 			tv.tv_usec = (xtimeout % 1000) * 1000L; | ||||
| 			evtimer_del(&c->repeat_timer); | ||||
| 			evtimer_add(&c->repeat_timer, &tv); | ||||
| 		} else { | ||||
| 			c->flags &= ~CLIENT_REPEAT; | ||||
| 			server_client_key_table(c, "root"); | ||||
| 		} | ||||
| 		server_status_client(c); | ||||
|  | ||||
| 		/* Dispatch the key binding. */ | ||||
| 		key_bindings_dispatch(bd, c, m); | ||||
| 		key_bindings_unref_table(table); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Dispatch the command. */ | ||||
| 	key_bindings_dispatch(bd, c, m); | ||||
| 	/* | ||||
| 	 * No match in this table. If repeating, switch the client back to the | ||||
| 	 * root table and try again. | ||||
| 	 */ | ||||
| 	if (c->flags & CLIENT_REPEAT) { | ||||
| 		server_client_key_table(c, "root"); | ||||
| 		c->flags &= ~CLIENT_REPEAT; | ||||
| 		server_status_client(c); | ||||
| 		goto retry; | ||||
| 	} | ||||
|  | ||||
| 	/* If no match and we're not in the root table, that's it. */ | ||||
| 	if (strcmp(c->keytable->name, "root") != 0) { | ||||
| 		server_client_key_table(c, "root"); | ||||
| 		server_status_client(c); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * No match, but in the root table. Prefix switches to the prefix table | ||||
| 	 * and everything else is passed through. | ||||
| 	 */ | ||||
| 	if (key == options_get_number(&s->options, "prefix") || | ||||
| 	    key == options_get_number(&s->options, "prefix2")) { | ||||
| 		server_client_key_table(c, "prefix"); | ||||
| 		server_status_client(c); | ||||
| 	} else if (!(c->flags & CLIENT_READONLY)) | ||||
| 		window_pane_key(wp, c, s, key, m); | ||||
| } | ||||
|  | ||||
| /* Client functions that need to happen every loop. */ | ||||
| @@ -848,9 +880,9 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) | ||||
| 	struct client	*c = data; | ||||
|  | ||||
| 	if (c->flags & CLIENT_REPEAT) { | ||||
| 		if (c->flags & CLIENT_PREFIX) | ||||
| 		server_client_key_table(c, "root"); | ||||
| 		c->flags &= ~CLIENT_REPEAT; | ||||
| 		server_status_client(c); | ||||
| 		c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										106
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								tmux.1
									
									
									
									
									
								
							| @@ -838,6 +838,7 @@ Suspend a client by sending | ||||
| .Op Fl lnpr | ||||
| .Op Fl c Ar target-client | ||||
| .Op Fl t Ar target-session | ||||
| .Op Fl T Ar key-table | ||||
| .Xc | ||||
| .D1 (alias: Ic switchc ) | ||||
| Switch the current session for client | ||||
| @@ -855,6 +856,22 @@ respectively. | ||||
| toggles whether a client is read-only (see the | ||||
| .Ic attach-session | ||||
| command). | ||||
| .Pp | ||||
| .Fl T | ||||
| sets the client's key table; the next key from the client will be interpreted from | ||||
| .Ar key-table . | ||||
| This may be used to configure multiple prefix keys, or to bind commands to | ||||
| sequences of keys. | ||||
| For example, to make typing | ||||
| .Ql abc | ||||
| run the | ||||
| .Ic list-keys | ||||
| command: | ||||
| .Bd -literal -offset indent | ||||
| bind-key -Ttable2 c list-keys | ||||
| bind-key -Ttable1 b switch-client -Ttable2 | ||||
| bind-key -Troot   a switch-client -Ttable1 | ||||
| .Ed | ||||
| .El | ||||
| .Sh WINDOWS AND PANES | ||||
| A | ||||
| @@ -1931,6 +1948,7 @@ Commands related to key bindings are as follows: | ||||
| .It Xo Ic bind-key | ||||
| .Op Fl cnr | ||||
| .Op Fl t Ar mode-table | ||||
| .Op Fl T Ar key-table | ||||
| .Ar key Ar command Op Ar arguments | ||||
| .Xc | ||||
| .D1 (alias: Ic bind ) | ||||
| @@ -1938,16 +1956,40 @@ Bind key | ||||
| .Ar key | ||||
| to | ||||
| .Ar command . | ||||
| By default (without | ||||
| .Fl t ) | ||||
| the primary key bindings are modified (those normally activated with the prefix | ||||
| key); in this case, if | ||||
| .Fl n | ||||
| is specified, it is not necessary to use the prefix key, | ||||
| .Ar command | ||||
| Keys are bound in a key table. | ||||
| By default (without -T), the key is bound in | ||||
| the | ||||
| .Em prefix | ||||
| key table. | ||||
| This table is used for keys pressed after the prefix key (for example, | ||||
| by default | ||||
| .Ql c | ||||
| is bound to | ||||
| .Ar key | ||||
| alone. | ||||
| .Ic new-window | ||||
| in the | ||||
| .Em prefix | ||||
| table, so | ||||
| .Ql C-b c | ||||
| creates a new window). | ||||
| The | ||||
| .Em root | ||||
| table is used for keys pressed without the prefix key: binding | ||||
| .Ql c | ||||
| to | ||||
| .Ic new-window | ||||
| in the | ||||
| .Em root | ||||
| table (not recommended) means a plain | ||||
| .Ql c | ||||
| will create a new window. | ||||
| .Fl n | ||||
| is an alias | ||||
| for | ||||
| .Fl T Ar root . | ||||
| Keys may also be bound in custom key tables and the | ||||
| .Ic switch-client | ||||
| .Fl T | ||||
| command used to switch to them from a key binding. | ||||
| The | ||||
| .Fl r | ||||
| flag indicates this key may repeat, see the | ||||
| @@ -1962,22 +2004,33 @@ is bound in | ||||
| .Ar mode-table : | ||||
| the binding for command mode with | ||||
| .Fl c | ||||
| or for normal mode without. | ||||
| or for normal mode without. See the | ||||
| .Sx WINDOWS AND PANES | ||||
| section and the | ||||
| .Ic list-keys | ||||
| command for information on mode key bindings. | ||||
| .Pp | ||||
| To view the default bindings and possible commands, see the | ||||
| .Ic list-keys | ||||
| command. | ||||
| .It Ic list-keys Op Fl t Ar key-table | ||||
| .It Xo Ic list-keys | ||||
| .Op Fl t Ar mode-table | ||||
| .Op Fl T Ar key-table | ||||
| .Xc | ||||
| .D1 (alias: Ic lsk ) | ||||
| List all key bindings. | ||||
| Without | ||||
| .Fl t | ||||
| the primary key bindings - those executed when preceded by the prefix key - | ||||
| are printed. | ||||
| .Fl T | ||||
| all key tables are printed. | ||||
| With | ||||
| .Fl T | ||||
| only | ||||
| .Ar key-table . | ||||
| .Pp | ||||
| With | ||||
| .Fl t , | ||||
| the key bindings in | ||||
| .Ar key-table | ||||
| .Ar mode-table | ||||
| are listed; this may be one of: | ||||
| .Em vi-edit , | ||||
| .Em emacs-edit , | ||||
| @@ -2022,31 +2075,22 @@ the secondary prefix key, to a window as if it was pressed. | ||||
| .It Xo Ic unbind-key | ||||
| .Op Fl acn | ||||
| .Op Fl t Ar mode-table | ||||
| .Op Fl T Ar key-table | ||||
| .Ar key | ||||
| .Xc | ||||
| .D1 (alias: Ic unbind ) | ||||
| Unbind the command bound to | ||||
| .Ar key . | ||||
| Without | ||||
| .Fl c , | ||||
| .Fl n , | ||||
| .Fl T | ||||
| and | ||||
| .Fl t | ||||
| the primary key bindings are modified; in this case, if | ||||
| .Fl n | ||||
| is specified, the command bound to | ||||
| .Ar key | ||||
| without a prefix (if any) is removed. | ||||
| are the same as for | ||||
| .Ic bind-key . | ||||
| If | ||||
| .Fl a | ||||
| is present, all key bindings are removed. | ||||
| .Pp | ||||
| If | ||||
| .Fl t | ||||
| is present, | ||||
| .Ar key | ||||
| in | ||||
| .Ar mode-table | ||||
| is unbound: the binding for command mode with | ||||
| .Fl c | ||||
| or for normal mode without. | ||||
| .El | ||||
| .Sh OPTIONS | ||||
| The appearance and behaviour of | ||||
|   | ||||
							
								
								
									
										31
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -89,10 +89,9 @@ extern char   **environ; | ||||
| #define KEYC_ESCAPE 0x2000 | ||||
| #define KEYC_CTRL 0x4000 | ||||
| #define KEYC_SHIFT 0x8000 | ||||
| #define KEYC_PREFIX 0x10000 | ||||
|  | ||||
| /* Mask to obtain key w/o modifiers. */ | ||||
| #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) | ||||
| #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) | ||||
| #define KEYC_MASK_KEY (~KEYC_MASK_MOD) | ||||
|  | ||||
| /* Is this a mouse key? */ | ||||
| @@ -1301,7 +1300,7 @@ struct client { | ||||
| 	struct screen	 status; | ||||
|  | ||||
| #define CLIENT_TERMINAL 0x1 | ||||
| #define CLIENT_PREFIX 0x2 | ||||
| /* 0x2 unused */ | ||||
| #define CLIENT_EXIT 0x4 | ||||
| #define CLIENT_REDRAW 0x8 | ||||
| #define CLIENT_STATUS 0x10 | ||||
| @@ -1320,6 +1319,7 @@ struct client { | ||||
| #define CLIENT_256COLOURS 0x20000 | ||||
| #define CLIENT_IDENTIFIED 0x40000 | ||||
| 	int		 flags; | ||||
| 	struct key_table *keytable; | ||||
|  | ||||
| 	struct event	 identify_timer; | ||||
|  | ||||
| @@ -1440,7 +1440,7 @@ struct cmd_entry { | ||||
| 	enum cmd_retval	 (*exec)(struct cmd *, struct cmd_q *); | ||||
| }; | ||||
|  | ||||
| /* Key binding. */ | ||||
| /* Key binding and key table. */ | ||||
| struct key_binding { | ||||
| 	int			 key; | ||||
| 	struct cmd_list		*cmdlist; | ||||
| @@ -1449,6 +1449,15 @@ struct key_binding { | ||||
| 	RB_ENTRY(key_binding)	 entry; | ||||
| }; | ||||
| RB_HEAD(key_bindings, key_binding); | ||||
| struct key_table { | ||||
| 	const char		 *name; | ||||
| 	struct key_bindings	 key_bindings; | ||||
|  | ||||
| 	u_int			 references; | ||||
|  | ||||
| 	RB_ENTRY(key_table)	 entry; | ||||
| }; | ||||
| RB_HEAD(key_tables, key_table); | ||||
|  | ||||
| /* | ||||
|  * Option table entries. The option table is the user-visible part of the | ||||
| @@ -1876,12 +1885,16 @@ void	cmd_wait_for_flush(void); | ||||
| int	client_main(int, char **, int); | ||||
|  | ||||
| /* key-bindings.c */ | ||||
| extern struct key_bindings key_bindings; | ||||
| int	 key_bindings_cmp(struct key_binding *, struct key_binding *); | ||||
| RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); | ||||
| struct key_binding *key_bindings_lookup(int); | ||||
| void	 key_bindings_add(int, int, struct cmd_list *); | ||||
| void	 key_bindings_remove(int); | ||||
| RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); | ||||
| extern struct key_tables key_tables; | ||||
| int	 key_table_cmp(struct key_table *, struct key_table *); | ||||
| int	 key_bindings_cmp(struct key_binding *, struct key_binding *); | ||||
| struct 	 key_table *key_bindings_get_table(const char *, int); | ||||
| void 	 key_bindings_unref_table(struct key_table *); | ||||
| void	 key_bindings_add(const char *, int, int, struct cmd_list *); | ||||
| void	 key_bindings_remove(const char *, int); | ||||
| void	 key_bindings_remove_table(const char *); | ||||
| void	 key_bindings_init(void); | ||||
| void	 key_bindings_dispatch(struct key_binding *, struct client *, | ||||
| 	     struct mouse_event *); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 nicm
					nicm