Here's the latest diff. It actually seems to work fine, although I haven't done a lot of testing.
IIRC I wasn't wild about the cmd-list-keys.c and cmd-bind-key.c changes; certainly lsk -T should error on an unknown table, same as bind -T. I think the manpage bits could do with some improvement too. Index: cmd-bind-key.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-bind-key.c,v retrieving revision 1.20 diff -u -p -r1.20 cmd-bind-key.c --- cmd-bind-key.c 10 Apr 2015 16:00:08 -0000 1.20 +++ cmd-bind-key.c 13 Apr 2015 21:29:21 -0000 @@ -33,8 +33,8 @@ enum cmd_retval cmd_bind_key_mode_table 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, stru 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, stru 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, stru 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); } Index: cmd-list-keys.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-list-keys.c,v retrieving revision 1.25 diff -u -p -r1.25 cmd-list-keys.c --- cmd-list-keys.c 20 Oct 2014 23:27:14 -0000 1.25 +++ cmd-list-keys.c 13 Apr 2015 21:29:21 -0000 @@ -33,8 +33,8 @@ enum cmd_retval cmd_list_keys_commands( 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,58 +51,65 @@ 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)); if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); + tablename = args_get(args, 'T'); - width = 0; - - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) + 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; - 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; + repeat = 1; + + width = strlen(table->name); + if (width > tablewidth) + tablewidth =width; + width = strlen(key); + if (width > keywidth) + keywidth = width; + } } - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; - - *flags = '\0'; - if (!(bd->key & KEYC_PREFIX)) { - if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-rn "); + RB_FOREACH(bd, key_bindings, &(table->key_bindings)) { + key = key_string_lookup_key(bd->key); + if (key == NULL) + continue; + + if (!repeat) + r = ""; + else if (bd->can_repeat) + r = "-r "; else - xsnprintf(flags, sizeof flags, "-n "); - } else if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-r "); - - used = xsnprintf(tmp, sizeof tmp, "%s%*s ", - flags, (int) (width - strlen(flags)), key); - if (used >= sizeof tmp) - continue; + 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); + } - cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); - cmdq_print(cmdq, "bind-key %s", tmp); + cmdq_print(cmdq, "bind-key %s", tmp); + } } return (CMD_RETURN_NORMAL); Index: cmd-switch-client.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-switch-client.c,v retrieving revision 1.22 diff -u -p -r1.22 cmd-switch-client.c --- cmd-switch-client.c 20 Oct 2014 22:29:25 -0000 1.22 +++ cmd-switch-client.c 13 Apr 2015 21:29:21 -0000 @@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec( 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 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); @@ -56,6 +57,18 @@ cmd_switch_client_exec(struct cmd *self, c->flags &= ~CLIENT_READONLY; else 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'); Index: cmd-unbind-key.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-unbind-key.c,v retrieving revision 1.20 diff -u -p -r1.20 cmd-unbind-key.c --- cmd-unbind-key.c 20 Oct 2014 22:29:25 -0000 1.20 +++ cmd-unbind-key.c 13 Apr 2015 21:29:21 -0000 @@ -31,8 +31,8 @@ enum cmd_retval cmd_unbind_key_mode_tab 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 }; @@ -40,9 +40,9 @@ const struct cmd_entry cmd_unbind_key_en 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; + struct args *args = self->args; + int key; + const char *tablename; if (!args_has(args, 'a')) { if (args->argc != 1) { @@ -66,16 +66,23 @@ cmd_unbind_key_exec(struct cmd *self, st 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); + if (args_has(args, 'T')) { + key_bindings_remove_table(args_get(args, 'T')); + return (CMD_RETURN_NORMAL); } + key_bindings_remove_table("root"); + key_bindings_remove_table("prefix"); 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'); + else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; + + key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } Index: format.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/format.c,v retrieving revision 1.60 diff -u -p -r1.60 format.c --- format.c 31 Mar 2015 17:58:36 -0000 1.60 +++ format.c 13 Apr 2015 21:29:21 -0000 @@ -545,7 +545,11 @@ format_defaults_client(struct format_tre 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); Index: key-bindings.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/key-bindings.c,v retrieving revision 1.43 diff -u -p -r1.43 key-bindings.c --- key-bindings.c 22 Oct 2014 23:18:53 -0000 1.43 +++ key-bindings.c 13 Apr 2015 21:29:21 -0000 @@ -25,60 +25,121 @@ #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; + return (bd1->key - bd2->key); +} + +struct key_table * +key_bindings_get_table(const char *name, int create) +{ + struct key_table table_search, *table; + + table_search.name = name; + table = RB_FIND(key_tables, &key_tables, &table_search); + if (table != NULL || !create) + return (table); - key1 = bd1->key & ~KEYC_PREFIX; - key2 = bd2->key & ~KEYC_PREFIX; - if (key1 != key2) - return (key1 - key2); + table = xmalloc(sizeof *table); + table->name = xstrdup(name); + RB_INIT(&table->key_bindings); - if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) - return (-1); - if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) - return (1); - return (0); + table->references = 1; /* one reference in key_tables */ + RB_INSERT(key_tables, &key_tables, table); + + return (table); } -struct key_binding * -key_bindings_lookup(int key) +void +key_bindings_unref_table(struct key_table *table) { - struct key_binding bd; + struct key_binding *bd; - bd.key = key; - return (RB_FIND(key_bindings, &key_bindings, &bd)); + 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(int key, int can_repeat, struct cmd_list *cmdlist) +key_bindings_add(const char *name, int key, int can_repeat, + struct cmd_list *cmdlist) { - struct key_binding *bd; + struct key_table *table; + struct key_binding bd_search, *bd; - key_bindings_remove(key); + table = key_bindings_get_table(name, 1); + + bd_search.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_search); + 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_search, *bd; + + table = key_bindings_get_table(name, 0); + if (table == NULL) + return; - if ((bd = key_bindings_lookup(key)) == NULL) + bd_search.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_search); + if (bd == NULL) return; - RB_REMOVE(key_bindings, &key_bindings, bd); + + 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) + return; + + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); } void @@ -164,8 +225,6 @@ key_bindings_init(void) char* cause; int error; struct cmd_q *cmdq; - - RB_INIT(&key_bindings); cmdq = cmdq_new(NULL); for (i = 0; i < nitems(defaults); i++) { Index: server-client.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/server-client.c,v retrieving revision 1.129 diff -u -p -r1.129 server-client.c --- server-client.c 31 Mar 2015 17:45:10 -0000 1.129 +++ server-client.c 13 Apr 2015 21:29:21 -0000 @@ -100,6 +100,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++) { @@ -171,6 +174,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); @@ -362,33 +367,28 @@ server_client_assume_paste(struct sessio void server_client_handle_key(struct client *c, int key) { - 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_search, *bd; + int xtimeout; /* Check the client is good to accept input. */ - if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) - return; - - if (c->session == NULL) + if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; - s = c->session; + w = c->session->curw->window; + wp = w->active; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); - memcpy(&s->last_activity_time, &s->activity_time, sizeof s->last_activity_time); memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); - w = c->session->curw->window; - wp = w->active; - - /* 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; @@ -419,74 +419,89 @@ server_client_handle_key(struct client * return; } - /* 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 everything as a regular key when pasting is detected. */ + if (server_client_assume_paste(s)) { + if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, s, key); + return; + } - /* 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; +retry: + /* Try to see if there is a key binding in the current table. */ + bd_search.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_search); + 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_set_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; server_status_client(c); - return; + goto retry; } - /* Try as a non-prefix key binding. */ - if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); - } else - key_bindings_dispatch(bd, c); - return; - } - - /* Prefix key already pressed. Reset prefix and lookup key. */ - c->flags &= ~CLIENT_PREFIX; - 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) { + /* + * Take a reference to this table to make sure the key binding + * doesn't disappear. + */ + table->references++; + + /* + * 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_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; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); + server_set_key_table(c, "root"); } + server_status_client(c); + + /* Dispatch the key binding. */ + key_bindings_dispatch(bd, c); + key_bindings_unref_table(table); + return; } - /* If already repeating, but this key can't repeat, skip it. */ - if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { + /* + * No match in this table. If repeating, switch the client back to the + * root table and try again. + */ + if (c->flags & CLIENT_REPEAT) { + server_set_key_table(c, "root"); c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); - return; + server_status_client(c); + goto retry; } - /* If this key can repeat, reset the repeat flags and timer. */ - xtimeout = options_get_number(&s->options, "repeat-time"); - if (xtimeout != 0 && bd->can_repeat) { - c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; - - tv.tv_sec = xtimeout / 1000; - tv.tv_usec = (xtimeout % 1000) * 1000L; - evtimer_del(&c->repeat_timer); - evtimer_add(&c->repeat_timer, &tv); + /* If no match and we're not in the root table, that's it. */ + if (strcmp(c->keytable->name, "root") != 0) { + server_set_key_table(c, "root"); + server_status_client(c); + return; } - /* Dispatch the command. */ - key_bindings_dispatch(bd, c); + /* + * 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_set_key_table(c, "prefix"); + server_status_client(c); + } else if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, s, key); } /* Client functions that need to happen every loop. */ @@ -692,9 +707,9 @@ server_client_repeat_timer(unused int fd struct client *c = data; if (c->flags & CLIENT_REPEAT) { - if (c->flags & CLIENT_PREFIX) - server_status_client(c); - c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + server_set_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; + server_status_client(c); } } Index: server-fn.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/server-fn.c,v retrieving revision 1.79 diff -u -p -r1.79 server-fn.c --- server-fn.c 31 Mar 2015 17:45:10 -0000 1.79 +++ server-fn.c 13 Apr 2015 21:29:22 -0000 @@ -101,6 +101,14 @@ server_status_client(struct client *c) } void +server_set_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++; +} + +void server_redraw_session(struct session *s) { struct client *c; Index: tmux.1 =================================================================== RCS file: /cvs/src/usr.bin/tmux/tmux.1,v retrieving revision 1.417 diff -u -p -r1.417 tmux.1 --- tmux.1 10 Apr 2015 16:00:08 -0000 1.417 +++ tmux.1 13 Apr 2015 21:29:23 -0000 @@ -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,9 @@ respectively. toggles whether a client is read-only (see the .Ic attach-session command). +.Fl T +sets the client's key table; the next key from the client will be interpreted from +.Ar key-table . .El .Sh WINDOWS AND PANES A @@ -1905,6 +1909,7 @@ Commands related to key bindings are as .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 ) @@ -1940,18 +1945,41 @@ or for normal mode without. To view the default bindings and possible commands, see the .Ic list-keys command. -.It Ic list-keys Op Fl t Ar key-table +.Pp +If +.Fl T +is present, +.Ar key +is bound in +.Ar key-table : +.Em prefix +corresponds to the default, +.Em root +corresponds to +.Fl n , +and custom values may be used with +.Ic switch-client +.Fl T . +.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. +or +.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 , @@ -1992,6 +2020,7 @@ the secondary prefix key, to a window as .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 ) @@ -2017,6 +2046,22 @@ in is unbound: the binding for command mode with .Fl c or for normal mode without. +.Pp +If +.Fl T +is present, +.Ar key +in +.Ar key-table +is unbound: +.Em prefix +corresponds to the default, +.Em root +corresponds to +.Fl n , +and custom values may be used with +.Ic switch-client +.Fl T . .El .Sh OPTIONS The appearance and behaviour of Index: tmux.h =================================================================== RCS file: /cvs/src/usr.bin/tmux/tmux.h,v retrieving revision 1.490 diff -u -p -r1.490 tmux.h --- tmux.h 6 Feb 2015 17:21:08 -0000 1.490 +++ tmux.h 13 Apr 2015 21:29:23 -0000 @@ -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) /* Other key codes. */ @@ -1287,7 +1286,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 @@ -1306,6 +1305,7 @@ struct client { #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 int flags; + struct key_table *keytable; struct event identify_timer; @@ -1423,15 +1423,24 @@ 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; - int can_repeat; + int key; + struct cmd_list *cmdlist; + int can_repeat; - RB_ENTRY(key_binding) entry; + 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 @@ -1848,12 +1857,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 *); @@ -1889,6 +1902,7 @@ void server_write_session(struct sessio size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); +void server_set_key_table(struct client *, const char *); void server_redraw_session(struct session *); void server_redraw_session_group(struct session *); void server_status_session(struct session *); On Mon, Apr 13, 2015 at 03:21:52PM +0000, Ross Hadden wrote: > Keith Amling <amling <at> palantir.com> writes: > > > > > > > > Or are you thinking of creating the table after running the set > command? > > > > > This shouldn't work - you shouldn't be able to set a client to a > > > > > nonexistent table. > > > > > > > > I guess I don't super care what happens since I won't personally be > > > > writing any switch-client -T into an empty table but I had assumed we > > > > would allow switching to a new [and ephemeral] table since otherwise > > > > switch-client -T can fail which just seems weird to me. > > > > > > I'd say it should fail if the table doesn't exist. > > > > > > I can't think of a case where I would want it to automatically create a > > > table, except to confuse people who make a typo in the table name. Do > > > you have a use in mind? > > > > Not specifically, I just prefer to see tools degrade what might be > > described as "uniformly" where possible. Nonuniform behaviour makes > > tools dangerous for automated use because when writing a script you're > > unlikely to think of things like "what if this table is empty" or "what > > if this command barfs on no args when really no args means I just didn't > > need it to do anything". > > > > I'll switch it and the next set of patches will be up shortly. > > > > Keith > > > > -------------------------------------------------------------------------- > ---- > > "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE > > Instantly run your Selenium tests across 300+ browser/OS combos. > > Get unparalleled scalability from the best Selenium testing platform > available > > Simple to use. Nothing to install. Get started now for free." > > http://p.sf.net/sfu/SauceLabs > > > > > Has work on this continued? I made an issue about it > (https://sourceforge.net/p/tmux/tickets/172/) in an effort to bring > attention to this patch, but Thomas said it's pretty much not going anywhere > unless the original patch author continues or makes an effort to get it > merged himself. > > I would love to see this landed in tmux! My current workaround can be seen > at https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85 > and https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux- > cords.sh. > > > ------------------------------------------------------------------------------ > BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT > Develop your own process in accordance with the BPMN 2 standard > Learn Process modeling best practices with Bonita BPM through live exercises > http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_ > source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF > _______________________________________________ > tmux-users mailing list > tmux-users@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/tmux-users ------------------------------------------------------------------------------ BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT Develop your own process in accordance with the BPMN 2 standard Learn Process modeling best practices with Bonita BPM through live exercises http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_ source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users