To my great shame that last patch (3 of 3) was very wrong (and
undertested).  This should be less wrong and maybe a little less
confused in e.g.  unbinding.

Keith

---
 cmd-bind-key.c      |  44 +++++++++++------
 cmd-list-keys.c     |  80 +++++++++++++++---------------
 cmd-switch-client.c |  16 +++++-
 cmd-unbind-key.c    |  27 ++++++----
 format.c            |   3 +-
 key-bindings.c      | 139 ++++++++++++++++++++++++++++++++++++++++++----------
 server-client.c     | 127 +++++++++++++++++++++++++++--------------------
 server-fn.c         |   7 +++
 tmux.1              |  37 ++++++++++++++
 tmux.h              |  35 ++++++++++---
 10 files changed, 361 insertions(+), 154 deletions(-)

diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index 27a03ce11ad7..fe4cf4c2fb66 100644
--- a/cmd-bind-key.c
+++ b/cmd-bind-key.c
@@ -31,10 +31,12 @@ enum cmd_retval      cmd_bind_key_exec(struct cmd *, struct 
cmd_q *);
 
 enum cmd_retval         cmd_bind_mode_key_table(struct cmd *, struct cmd_q *, 
int);
 
+enum cmd_retval         cmd_bind_key_table(struct cmd *, const char *, struct 
cmd_q *, int);
+
 const struct cmd_entry cmd_bind_key_entry = {
        "bind-key", "bind",
-       "cnrt:", 1, -1,
-       "[-cnr] [-t mode-key-table] key command [arguments]",
+       "cnrt:T:", 1, -1,
+       "[-cnr] [-t mode-key-table] [-T key-table] key command [arguments]",
        0,
        NULL,
        cmd_bind_key_exec
@@ -44,8 +46,6 @@ enum cmd_retval
 cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
 {
        struct args     *args = self->args;
-       char            *cause;
-       struct cmd_list *cmdlist;
        int              key;
 
        if (args_has(args, 't')) {
@@ -69,18 +69,13 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
        if (args_has(args, 't'))
                return (cmd_bind_mode_key_table(self, cmdq, key));
 
-       cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
-           &cause);
-       if (cmdlist == NULL) {
-               cmdq_error(cmdq, "%s", cause);
-               free(cause);
-               return (CMD_RETURN_ERROR);
-       }
+       if (args_has(args, 'T'))
+               return (cmd_bind_key_table(self, args_get(args, 'T'), cmdq, 
key));
 
-       if (!args_has(args, 'n'))
-           key |= KEYC_PREFIX;
-       key_bindings_add(key, args_has(args, 'r'), cmdlist);
-       return (CMD_RETURN_NORMAL);
+       if (args_has(args, 'n'))
+               return (cmd_bind_key_table(self, "root", cmdq, key));
+
+       return (cmd_bind_key_table(self, "prefix", cmdq, key));
 }
 
 enum cmd_retval
@@ -131,3 +126,22 @@ cmd_bind_mode_key_table(struct cmd *self, struct cmd_q 
*cmdq, int key)
        mbind->arg = arg != NULL ? xstrdup(arg) : NULL;
        return (CMD_RETURN_NORMAL);
 }
+
+enum cmd_retval
+cmd_bind_key_table(struct cmd *self, const char *tablename, struct cmd_q 
*cmdq, int key)
+{
+       struct args     *args = self->args;
+       char            *cause;
+       struct cmd_list *cmdlist;
+
+       cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
+           &cause);
+       if (cmdlist == NULL) {
+               cmdq_error(cmdq, "%s", cause);
+               free(cause);
+               return (CMD_RETURN_ERROR);
+       }
+
+       key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist);
+       return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 615c5ce1fe67..31dc5e19e7bd 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -41,56 +41,56 @@ const struct cmd_entry cmd_list_keys_entry = {
 enum cmd_retval
 cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
 {
-       struct args             *args = self->args;
-       struct key_binding      *bd;
-       const char              *key;
-       char                     tmp[BUFSIZ], flags[8];
-       size_t                   used;
-       int                      width, keywidth;
+       struct args                     *args = self->args;
+       struct key_binding_map_entry    *e;
+       struct key_binding              *bd;
+       const char                      *key;
+       char                             tmp[BUFSIZ];
+       size_t                           used;
+       int                              hasrepeat, maxtablewidth, tablewidth, 
maxkeywidth, keywidth;
 
        if (args_has(args, 't'))
                return (cmd_list_keys_table(self, cmdq));
 
-       width = 0;
+       hasrepeat = 0;
+       maxtablewidth = 0;
+       maxkeywidth = 0;
 
-       RB_FOREACH(bd, key_bindings, &key_bindings) {
-               key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
-               if (key == NULL)
-                       continue;
+       RB_FOREACH(e, key_binding_map, &key_binding_map) {
+               RB_FOREACH(bd, key_bindings, &(e->value->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;
-       }
+                               hasrepeat = 1;
 
-       RB_FOREACH(bd, key_bindings, &key_bindings) {
-               key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
-               if (key == NULL)
-                       continue;
+                       tablewidth = strlen(e->name);
+                       if (tablewidth > maxtablewidth)
+                               maxtablewidth = tablewidth;
 
-               *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 ");
-
-               used = xsnprintf(tmp, sizeof tmp, "%s%*s ",
-                   flags, (int) (width - strlen(flags)), key);
-               if (used >= sizeof tmp)
-                       continue;
+                       keywidth = strlen(key);
+                       if (keywidth > maxkeywidth)
+                               maxkeywidth = keywidth;
+               }
+       }
 
-               cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
-               cmdq_print(cmdq, "bind-key %s", tmp);
+       RB_FOREACH(e, key_binding_map, &key_binding_map) {
+               RB_FOREACH(bd, key_bindings, &(e->value->key_bindings)) {
+                       key = key_string_lookup_key(bd->key);
+                       if (key == NULL)
+                               continue;
+
+                       used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ",
+                               (hasrepeat ? (bd->can_repeat ? "-r " : "   ") : 
""),
+                               (int) maxtablewidth, e->name,
+                               (int) maxkeywidth, key);
+                       if (used >= sizeof tmp)
+                               continue;
+
+                       cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - 
used);
+                       cmdq_print(cmdq, "bind-key %s", tmp);
+               }
        }
 
        return (CMD_RETURN_NORMAL);
diff --git a/cmd-switch-client.c b/cmd-switch-client.c
index 9e7967cd4e3d..bfb6357ee7e8 100644
--- a/cmd-switch-client.c
+++ b/cmd-switch-client.c
@@ -32,8 +32,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_key_binding,
        cmd_switch_client_exec
@@ -66,6 +66,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq)
        struct window           *w = NULL;
        struct window_pane      *wp = NULL;
        const char              *tflag;
+       struct key_binding_map_value *table;
+
 
        if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL)
                return (CMD_RETURN_ERROR);
@@ -77,6 +79,16 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq)
                        c->flags |= CLIENT_READONLY;
        }
 
+       if (args_has(args, 'T')) {
+               table = key_bindings_lookup_table(args_get(args, 'T'), 0);
+               if (!table) {
+                       cmdq_error(cmdq, "table %s doesn't exist", 
args_get(args, 'T'));
+                       return (CMD_RETURN_ERROR);
+               }
+               key_bindings_unreference_table(c->keytable);
+               c->keytable = table;
+       }
+
        tflag = args_get(args, 't');
        if (args_has(args, 'n')) {
                if ((s = session_next_session(c->session)) == NULL) {
diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c
index 1c4decb46b98..e54731f75183 100644
--- a/cmd-unbind-key.c
+++ b/cmd-unbind-key.c
@@ -31,8 +31,8 @@ enum cmd_retval        cmd_unbind_mode_key_table(struct cmd 
*, struct cmd_q *, int);
 
 const struct cmd_entry cmd_unbind_key_entry = {
        "unbind-key", "unbind",
-       "acnt:", 0, 1,
-       "[-acn] [-t mode-key-table] key",
+       "acnt:T:", 0, 1,
+       "[-acn] [-t mode-key-table] [-T key-table] key",
        0,
        NULL,
        cmd_unbind_key_exec
@@ -42,7 +42,6 @@ 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;
 
        if (!args_has(args, 'a')) {
@@ -67,16 +66,26 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
                return (cmd_unbind_mode_key_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')) {
+               key_bindings_remove(args_get(args, 'T'), key);
+               return (CMD_RETURN_NORMAL);
+       }
+
+       if (args_has(args, 'n')) {
+               key_bindings_remove("prefix", key);
+               return (CMD_RETURN_NORMAL);
+       }
+
+       key_bindings_remove("root", key);
        return (CMD_RETURN_NORMAL);
 }
 
diff --git a/format.c b/format.c
index 6f988b9ac2cc..8630aabed5d7 100644
--- a/format.c
+++ b/format.c
@@ -435,7 +435,8 @@ format_client(struct format_tree *ft, struct client *c)
        *strchr(tim, '\n') = '\0';
        format_add(ft, "client_activity_string", "%s", tim);
 
-       format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
+       format_add(ft, "client_prefix", strcmp(c->keytable->name, "root") ? 
"1": "0");
+       format_add(ft, "client_keytablename", "%s", c->keytable->name);
 
        if (c->tty.flags & TTY_UTF8)
                format_add(ft, "client_utf8", "%d", 1);
diff --git a/key-bindings.c b/key-bindings.c
index 58be0c6fe896..2940b2c15e8d 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -24,61 +24,146 @@
 
 #include "tmux.h"
 
+struct key_binding_map_entry *key_bindings_lookup_entry(const char *, int);
+
 RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+RB_GENERATE(key_binding_map, key_binding_map_entry, entry, 
key_binding_map_entry_cmp);
+
+struct key_binding_map key_binding_map;
 
-struct key_bindings    key_bindings;
+int
+key_binding_map_entry_cmp(struct key_binding_map_entry *e1, struct 
key_binding_map_entry *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_binding_map_entry *
+key_bindings_lookup_entry(const char *name, int create)
 {
-       struct key_binding      bd;
+       struct key_binding_map_entry    e;
+       struct key_binding_map_entry    *e_found;
 
-       bd.key = key;
-       return (RB_FIND(key_bindings, &key_bindings, &bd));
+       e.name = name;
+       e_found = RB_FIND(key_binding_map, &key_binding_map, &e);
+       if (e_found)
+               return e_found;
+
+       if (!create)
+               return NULL;
+
+       e_found = xmalloc(sizeof *e_found);
+       e_found->value = xmalloc(sizeof *(e_found->value));
+       e_found->name = e_found->value->name = xstrdup(name);
+       RB_INIT(&(e_found->value->key_bindings));
+       /* for key_binding_map */
+       e_found->value->references = 1;
+       RB_INSERT(key_binding_map, &key_binding_map, e_found);
+
+       return e_found;
+}
+
+struct key_binding_map_value *
+key_bindings_lookup_table(const char *name, int create)
+{
+       struct key_binding_map_entry    *e_found;
+
+       e_found = key_bindings_lookup_entry(name, create);
+       if (!e_found)
+               return NULL;
+
+       e_found->value->references++;
+       return e_found->value;
 }
 
 void
-key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
+key_bindings_unreference_table(struct key_binding_map_value *table)
 {
        struct key_binding      *bd;
 
-       key_bindings_remove(key);
+       if (--table->references == 0) {
+               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(table->name);
+               free(table);
+       }
+}
+
+void
+key_bindings_add(const char *name, int key, int can_repeat, struct cmd_list 
*cmdlist)
+{
+       struct key_binding_map_entry    *e;
+       struct key_binding               bd_search;
+       struct key_binding              *bd;
+
+       e = key_bindings_lookup_entry(name, 1);
+
+       bd_search.key = key;
+       bd = RB_FIND(key_bindings, &(e->value->key_bindings), &bd_search);
+       if (bd != NULL) {
+               RB_REMOVE(key_bindings, &(e->value->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, &(e->value->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_binding_map_entry    *e;
+       struct key_binding               bd_search;
+       struct key_binding              *bd;
 
-       if ((bd = key_bindings_lookup(key)) == NULL)
+       e = key_bindings_lookup_entry(name, 0);
+       if (!e)
                return;
-       RB_REMOVE(key_bindings, &key_bindings, bd);
+
+       bd_search.key = key;
+       bd = RB_FIND(key_bindings, &(e->value->key_bindings), &bd_search);
+       if (!bd)
+               return;
+
+       RB_REMOVE(key_bindings, &(e->value->key_bindings), bd);
        cmd_list_free(bd->cmdlist);
        free(bd);
+
+       if (RB_EMPTY(&(e->value->key_bindings))) {
+               RB_REMOVE(key_binding_map, &key_binding_map, e);
+               /* This represents key_binding_map's reference */
+               key_bindings_unreference_table(e->value);
+               free(e);
+       }
+}
+
+void
+key_bindings_remove_table(const char *name)
+{
+       struct key_binding_map_entry    *e;
+
+       e = key_bindings_lookup_entry(name, 0);
+       if (!e)
+               return;
+
+       RB_REMOVE(key_binding_map, &key_binding_map, e);
+       /* This represents key_binding_map's reference */
+       key_bindings_unreference_table(e->value);
+       free(e);
 }
 
 void
@@ -167,7 +252,7 @@ key_bindings_init(void)
        struct cmd      *cmd;
        struct cmd_list *cmdlist;
 
-       RB_INIT(&key_bindings);
+       RB_INIT(&key_binding_map);
 
        for (i = 0; i < nitems(table); i++) {
                cmdlist = xcalloc(1, sizeof *cmdlist);
@@ -183,7 +268,7 @@ key_bindings_init(void)
                TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry);
 
                key_bindings_add(
-                   table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist);
+                   "prefix", table[i].key, table[i].can_repeat, cmdlist);
        }
 }
 
diff --git a/server-client.c b/server-client.c
index e225de309b5f..f5fe5b015e4a 100644
--- a/server-client.c
+++ b/server-client.c
@@ -98,6 +98,7 @@ server_client_create(int fd)
        c->tty.mouse.flags = 0;
 
        c->flags |= CLIENT_FOCUSED;
+       c->keytable = key_bindings_lookup_table("root", 1);
 
        evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
 
@@ -170,6 +171,8 @@ server_client_lost(struct client *c)
 
        evtimer_del(&c->repeat_timer);
 
+       key_bindings_unreference_table(c->keytable);
+
        if (event_initialized(&c->identify_timer))
                evtimer_del(&c->identify_timer);
 
@@ -360,12 +363,14 @@ server_client_assume_paste(struct session *s)
 void
 server_client_handle_key(struct client *c, int key)
 {
-       struct session          *s;
-       struct window           *w;
-       struct window_pane      *wp;
-       struct timeval           tv;
-       struct key_binding      *bd;
-       int                      xtimeout, isprefix, ispaste;
+       struct session                  *s;
+       struct window                   *w;
+       struct window_pane              *wp;
+       struct timeval                   tv;
+       struct key_binding_map_value    *table;
+       struct key_binding               bd_search;
+       struct key_binding              *bd;
+       int                              xtimeout, isprefix, ispaste;
 
        /* Check the client is good to accept input. */
        if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
@@ -427,64 +432,82 @@ server_client_handle_key(struct client *c, int key)
 
        /* Treat prefix as a regular key when pasting is detected. */
        ispaste = server_client_assume_paste(s);
-       if (ispaste)
-               isprefix = 0;
+       if (ispaste) {
+               if (!(c->flags & CLIENT_READONLY))
+                       window_pane_key(wp, s, key);
+               return;
+       }
+
+       /* Try to see if we hit a key binding. */
+       for (;;) {
+               bd_search.key = key;
+               if ((bd = RB_FIND(key_bindings, &(c->keytable->key_bindings), 
&bd_search)) != NULL) {
+                       if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
+                               /* We don't honor repeating into a non-repeat 
binding, fall back to root and try again */
+                               server_keytable_client(c, "root");
+                               c->flags &= ~CLIENT_REPEAT;
+                               server_status_client(c);
+                               continue;
+                       }
+
+                       /* Hold a reference to this table to make sure the key 
binding doesn't disappear */
+                       table = c->keytable;
+                       table->references++;
+
+                       xtimeout = options_get_number(&s->options, 
"repeat-time");
+                       if (xtimeout != 0 && bd->can_repeat) {
+                               /* Now repeating in same keytable */
+                               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 {
+                               /* "Stop" (or don't start) repeating */
+                               c->flags &= ~CLIENT_REPEAT;
+                               server_keytable_client(c, "root");
+                       }
 
-       /* No previous prefix key. */
-       if (!(c->flags & CLIENT_PREFIX)) {
-               if (isprefix) {
-                       c->flags |= CLIENT_PREFIX;
                        server_status_client(c);
+                       key_bindings_dispatch(bd, c);
+
+                       key_bindings_unreference_table(table);
                        return;
                }
 
-               /* 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) {
+                       /* We missed, but we were in repeat, fall back to root 
and try again */
+                       server_keytable_client(c, "root");
                        c->flags &= ~CLIENT_REPEAT;
-                       if (isprefix)
-                               c->flags |= CLIENT_PREFIX;
-                       else if (!(c->flags & CLIENT_READONLY))
-                               window_pane_key(wp, s, key);
+                       server_status_client(c);
+                       continue;
                }
-               return;
+
+               /* Actual miss */
+               break;
        }
 
-       /* 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, s, key);
+       /* A miss in a non-root keytable fails out to root */
+       if (strcmp(c->keytable->name, "root")) {
+               server_keytable_client(c, "root");
+               server_status_client(c);
                return;
        }
 
-       /* 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);
+       /* A prefix miss in root switched to prefix */
+       if (isprefix) {
+               /* Prefix key switches to prefix table */
+               server_keytable_client(c, "prefix");
+               server_status_client(c);
+               return;
        }
 
-       /* Dispatch the command. */
-       key_bindings_dispatch(bd, c);
+       /* Anything else in root is straight through */
+       if (!(c->flags & CLIENT_READONLY)) {
+               window_pane_key(wp, s, key);
+       }
 }
 
 /* Client functions that need to happen every loop. */
@@ -700,9 +723,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_status_client(c);
-               c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
+               server_keytable_client(c, "root");
+               c->flags &= ~CLIENT_REPEAT;
+               server_status_client(c);
        }
 }
 
diff --git a/server-fn.c b/server-fn.c
index e0859f707034..6f3491693d76 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -101,6 +101,13 @@ server_status_client(struct client *c)
 }
 
 void
+server_keytable_client(struct client *c, const char *name)
+{
+       key_bindings_unreference_table(c->keytable);
+       c->keytable = key_bindings_lookup_table(name, 1);
+}
+
+void
 server_redraw_session(struct session *s)
 {
        struct client   *c;
diff --git a/tmux.1 b/tmux.1
index 7fd26cd7a199..e19904a2c8cc 100644
--- a/tmux.1
+++ b/tmux.1
@@ -808,6 +808,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
@@ -825,6 +826,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 interpretted 
from
+.Ar key-table .
 .El
 .Sh WINDOWS AND PANES
 A
@@ -1843,6 +1847,7 @@ Commands related to key bindings are as follows:
 .It Xo Ic bind-key
 .Op Fl cnr
 .Op Fl t Ar mode-key-table
+.Op Fl T Ar key-table
 .Ar key Ar command Op Ar arguments
 .Xc
 .D1 (alias: Ic bind )
@@ -1878,6 +1883,21 @@ or for normal mode without.
 To view the default bindings and possible commands, see the
 .Ic list-keys
 command.
+.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 Ic list-keys Op Fl t Ar key-table
 .D1 (alias: Ic lsk )
 List all key bindings.
@@ -1930,6 +1950,7 @@ 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-key-table
+.Op Fl T Ar key-table
 .Ar key
 .Xc
 .D1 (alias: Ic unbind )
@@ -1955,6 +1976,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
diff --git a/tmux.h b/tmux.h
index f9d6087ab714..cd15c5e0f4e1 100644
--- a/tmux.h
+++ b/tmux.h
@@ -164,10 +164,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. */
@@ -1298,7 +1297,7 @@ struct client {
        struct screen    status;
 
 #define CLIENT_TERMINAL 0x1
-#define CLIENT_PREFIX 0x2
+/* replaced by keytablename: #define CLIENT_PREFIX 0x2 */
 #define CLIENT_EXIT 0x4
 #define CLIENT_REDRAW 0x8
 #define CLIENT_STATUS 0x10
@@ -1317,6 +1316,7 @@ struct client {
 #define CLIENT_256COLOURS 0x20000
 #define CLIENT_IDENTIFIED 0x40000
        int              flags;
+       struct key_binding_map_value *keytable;
 
        struct event     identify_timer;
 
@@ -1444,6 +1444,20 @@ struct key_binding {
        RB_ENTRY(key_binding) entry;
 };
 RB_HEAD(key_bindings, key_binding);
+struct key_binding_map_value {
+       /* we keep another reference here so we can clean up just from the 
value */
+       char *name;
+       struct key_bindings key_bindings;
+
+       u_int references;
+};
+struct key_binding_map_entry {
+       const char *name;
+       struct key_binding_map_value *value;
+
+       RB_ENTRY(key_binding_map_entry) entry;
+};
+RB_HEAD(key_binding_map, key_binding_map_entry);
 
 /*
  * Option table entries. The option table is the user-visible part of the
@@ -1865,12 +1879,16 @@ int     cmd_string_parse(const char *, struct cmd_list 
**, const char *,
 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_binding_map, key_binding_map_entry, entry, 
key_binding_map_entry_cmp);
+extern struct key_binding_map key_binding_map;
+int     key_binding_map_entry_cmp(struct key_binding_map_entry *, struct 
key_binding_map_entry *);
+int     key_bindings_cmp(struct key_binding *, struct key_binding *);
+struct          key_binding_map_value *key_bindings_lookup_table(const char *, 
int);
+void    key_bindings_unreference_table(struct key_binding_map_value *);
+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 *);
 
@@ -1906,6 +1924,7 @@ void       server_write_session(struct session *, enum 
msgtype, const void *,
             size_t);
 void    server_redraw_client(struct client *);
 void    server_status_client(struct client *);
+void    server_keytable_client(struct client *, const char *);
 void    server_redraw_session(struct session *);
 void    server_redraw_session_group(struct session *);
 void    server_status_session(struct session *);
-- 
1.9.1


------------------------------------------------------------------------------
"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
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users

Reply via email to