Sorry if this isn't formatted as expected, I'm not at all familiar with
the operation of git's e-mail tools.

Keith

---
 Makefile.am        |   1 +
 cmd-bind-key.c     |  52 ++++++++++++++++---------
 cmd-list-keys.c    |  80 +++++++++++++++++++-------------------
 cmd-set-keytable.c |  44 +++++++++++++++++++++
 cmd-unbind-key.c   |  48 +++++++++++++++++------
 cmd.c              |   1 +
 format.c           |   3 +-
 key-bindings.c     |  95 ++++++++++++++++++++++++++++-----------------
 server-client.c    | 110 +++++++++++++++++++++++++++++++----------------------
 server.c           |   1 -
 tmux.1             |  46 ++++++++++++++++++++--
 tmux.h             |  26 +++++++++----
 12 files changed, 341 insertions(+), 166 deletions(-)
 create mode 100644 cmd-set-keytable.c

diff --git a/Makefile.am b/Makefile.am
index 5502de86d276..eb9fc9ed60bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,6 +123,7 @@ dist_tmux_SOURCES = \
        cmd-send-keys.c \
        cmd-set-buffer.c \
        cmd-set-environment.c \
+       cmd-set-keytable.c \
        cmd-set-option.c \
        cmd-show-environment.c \
        cmd-show-messages.c \
diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index 4ff3ac8431bc..e472a5a067dc 100644
--- a/cmd-bind-key.c
+++ b/cmd-bind-key.c
@@ -29,12 +29,14 @@
 
 enum cmd_retval         cmd_bind_key_exec(struct cmd *, struct cmd_q *);
 
-enum cmd_retval         cmd_bind_key_table(struct cmd *, struct cmd_q *, int);
+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 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,11 +46,9 @@ 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')) {
+       if (args_has(args, 't') || args_has(args, 'T')) {
                if (args->argc != 2 && args->argc != 3) {
                        cmdq_error(cmdq, "not enough arguments");
                        return (CMD_RETURN_ERROR);
@@ -67,24 +67,19 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
        }
 
        if (args_has(args, 't'))
-               return (cmd_bind_key_table(self, cmdq, key));
+               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
-cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
+cmd_bind_mode_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
 {
        struct args                     *args = self->args;
        const char                      *tablename;
@@ -131,3 +126,22 @@ cmd_bind_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..20fc386286e9 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->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->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-set-keytable.c b/cmd-set-keytable.c
new file mode 100644
index 000000000000..b009233101b4
--- /dev/null
+++ b/cmd-set-keytable.c
@@ -0,0 +1,44 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <n...@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+void            cmd_set_keytable_key_binding(struct cmd *, int);
+enum cmd_retval         cmd_set_keytable_exec(struct cmd *, struct cmd_q *);
+
+const struct cmd_entry cmd_set_keytable_entry = {
+       "set-keytable", "setkt",
+       "", 1, 1,
+       "table",
+       0,
+       NULL,
+       cmd_set_keytable_exec
+};
+
+enum cmd_retval
+cmd_set_keytable_exec(struct cmd *self, struct cmd_q *cmdq)
+{
+       free(cmdq->client->keytablename);
+       cmdq->client->keytablename = xstrdup(self->args->argv[0]);
+       return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c
index cf6ad506195f..cbc4743f3265 100644
--- a/cmd-unbind-key.c
+++ b/cmd-unbind-key.c
@@ -27,12 +27,13 @@
  */
 
 enum cmd_retval         cmd_unbind_key_exec(struct cmd *, struct cmd_q *);
-enum cmd_retval         cmd_unbind_key_table(struct cmd *, struct cmd_q *, 
int);
+enum cmd_retval         cmd_unbind_mode_key_table(struct cmd *, struct cmd_q 
*, int);
+void            cmd_unbind_key_wipe(const char *);
 
 const struct cmd_entry cmd_unbind_key_entry = {
        "unbind-key", "unbind",
-       "acnt:", 0, 1,
-       "[-acn] [-t key-table] key",
+       "acnt:T:", 0, 1,
+       "[-acn] [-t mode-key-table] [-T key-table] key",
        0,
        NULL,
        cmd_unbind_key_exec
@@ -42,7 +43,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')) {
@@ -64,24 +64,34 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
        }
 
        if (args_has(args, 't'))
-               return (cmd_unbind_key_table(self, cmdq, key));
+               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')) {
+                       cmd_unbind_key_wipe(args_get(args, 't'));
+                       return (CMD_RETURN_NORMAL);
                }
+               cmd_unbind_key_wipe("ROOT");
+               cmd_unbind_key_wipe("PREFIX");
+               return (CMD_RETURN_NORMAL);
+       }
+
+       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);
        }
 
-       if (!args_has(args, 'n'))
-               key |= KEYC_PREFIX;
-       key_bindings_remove(key);
+       key_bindings_remove("ROOT", key);
        return (CMD_RETURN_NORMAL);
 }
 
 enum cmd_retval
-cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
+cmd_unbind_mode_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
 {
        struct args                     *args = self->args;
        const char                      *tablename;
@@ -111,3 +121,17 @@ cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, 
int key)
        }
        return (CMD_RETURN_NORMAL);
 }
+
+void
+cmd_unbind_key_wipe(const char *tablename) {
+       struct key_bindings     *key_bindings;
+       struct key_binding      *bd;
+
+       while (1) {
+               key_bindings = key_bindings_lookup_table(tablename, 0);
+               if (key_bindings == NULL)
+                       return;
+               bd = RB_ROOT(key_bindings);
+               key_bindings_remove(tablename, bd->key);
+       }
+}
diff --git a/cmd.c b/cmd.c
index f2d88c050637..0c11a84766f7 100644
--- a/cmd.c
+++ b/cmd.c
@@ -95,6 +95,7 @@ const struct cmd_entry *cmd_table[] = {
        &cmd_server_info_entry,
        &cmd_set_buffer_entry,
        &cmd_set_environment_entry,
+       &cmd_set_keytable_entry,
        &cmd_set_option_entry,
        &cmd_set_window_option_entry,
        &cmd_show_buffer_entry,
diff --git a/format.c b/format.c
index 6f988b9ac2cc..4d3fc057f246 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->keytablename, "ROOT") ? "1": 
"0");
+       format_add(ft, "client_keytablename", "%s", c->keytablename);
 
        if (c->tty.flags & TTY_UTF8)
                format_add(ft, "client_utf8", "%d", 1);
diff --git a/key-bindings.c b/key-bindings.c
index f725508bce62..e5da8c97cd1d 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -25,72 +25,97 @@
 #include "tmux.h"
 
 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_bindings    key_bindings;
-struct key_bindings    dead_key_bindings;
+struct key_binding_map key_binding_map;
+
+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_bindings *
+key_bindings_lookup_table(const char *name, int create)
+{
+       struct key_binding_map_entry    e;
+       struct key_binding_map_entry    *e_found;
+
+       e.name = name;
+       e_found = RB_FIND(key_binding_map, &key_binding_map, &e);
+       if (e_found)
+               return &(e_found->key_bindings);
+
+       if (!create)
+               return NULL;
+
+       e_found = xmalloc(sizeof *e_found);
+       e_found->name = xstrdup(name);
+       RB_INIT(&(e_found->key_bindings));
+       RB_INSERT(key_binding_map, &key_binding_map, e_found);
+
+       return &(e_found->key_bindings);
 }
 
 struct key_binding *
-key_bindings_lookup(int key)
+key_bindings_lookup(const char *name, int key)
 {
+       struct key_bindings     *key_bindings;
        struct key_binding      bd;
 
+       key_bindings = key_bindings_lookup_table(name, 0);
+       if (!key_bindings)
+               return NULL;
+
        bd.key = key;
-       return (RB_FIND(key_bindings, &key_bindings, &bd));
+       return (RB_FIND(key_bindings, key_bindings, &bd));
 }
 
 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_bindings     *key_bindings;
        struct key_binding      *bd;
 
-       key_bindings_remove(key);
+       key_bindings_remove(name, key);
+
+       key_bindings = key_bindings_lookup_table(name, 1);
 
        bd = xmalloc(sizeof *bd);
        bd->key = key;
-       RB_INSERT(key_bindings, &key_bindings, bd);
+       RB_INSERT(key_bindings, 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_map_entry    e;
+       struct key_bindings     *key_bindings;
        struct key_binding      *bd;
 
-       if ((bd = key_bindings_lookup(key)) == NULL)
+       if ((bd = key_bindings_lookup(name, key)) == NULL)
                return;
-       RB_REMOVE(key_bindings, &key_bindings, bd);
-       RB_INSERT(key_bindings, &dead_key_bindings, bd);
-}
 
-void
-key_bindings_clean(void)
-{
-       struct key_binding      *bd;
+       key_bindings = key_bindings_lookup_table(name, 0);
+       if (!key_bindings)
+               return;
+
+       RB_REMOVE(key_bindings, key_bindings, bd);
+       cmd_list_free(bd->cmdlist);
+       free(bd);
 
-       while (!RB_EMPTY(&dead_key_bindings)) {
-               bd = RB_ROOT(&dead_key_bindings);
-               RB_REMOVE(key_bindings, &dead_key_bindings, bd);
-               cmd_list_free(bd->cmdlist);
-               free(bd);
+       if (RB_EMPTY(key_bindings)) {
+               e.name = name;
+               RB_REMOVE(key_binding_map, &key_binding_map, &e);
        }
 }
 
@@ -180,7 +205,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);
@@ -196,7 +221,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..c853712d3f10 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->keytablename = xstrdup("ROOT");
 
        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);
 
+       free(c->keytablename);
+
        if (event_initialized(&c->identify_timer))
                evtimer_del(&c->identify_timer);
 
@@ -427,64 +430,78 @@ 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;
+       }
 
-       /* No previous prefix key. */
-       if (!(c->flags & CLIENT_PREFIX)) {
-               if (isprefix) {
-                       c->flags |= CLIENT_PREFIX;
+       /* Try to see if we hit a key binding. */
+       while (1) {
+               if ((bd = key_bindings_lookup(c->keytablename, key)) != 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 */
+                               free(c->keytablename);
+                               c->keytablename = xstrdup("ROOT");
+                               c->flags &= ~CLIENT_REPEAT;
+                               server_status_client(c);
+                               continue;
+                       }
+                       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;
+                               free(c->keytablename);
+                               c->keytablename = xstrdup("ROOT");
+                       }
                        server_status_client(c);
+                       key_bindings_dispatch(bd, c);
                        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 */
+                       free(c->keytablename);
+                       c->keytablename = xstrdup("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->keytablename, "ROOT")) {
+               free(c->keytablename);
+               c->keytablename = xstrdup("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 */
+               free(c->keytablename);
+               c->keytablename = xstrdup("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 +717,10 @@ 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);
+               free(c->keytablename);
+               c->keytablename = xstrdup("ROOT");
+               c->flags &= ~CLIENT_REPEAT;
+               server_status_client(c);
        }
 }
 
diff --git a/server.c b/server.c
index d3ac0f8b0cdd..5ae44df23eff 100644
--- a/server.c
+++ b/server.c
@@ -209,7 +209,6 @@ server_loop(void)
                server_window_loop();
                server_client_loop();
 
-               key_bindings_clean();
                server_clean_dead();
        }
 }
diff --git a/tmux.1 b/tmux.1
index c05eacfdbc53..3a89e26c20d3 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1842,7 +1842,8 @@ Commands related to key bindings are as follows:
 .Bl -tag -width Ds
 .It Xo Ic bind-key
 .Op Fl cnr
-.Op Fl t Ar key-table
+.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 )
@@ -1871,13 +1872,28 @@ If
 is present,
 .Ar key
 is bound in
-.Ar key-table :
+.Ar mode-key-table :
 the binding for command mode with
 .Fl c
 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 the
+.Ic set-keytable
+command.
 .It Ic list-keys Op Fl t Ar key-table
 .D1 (alias: Ic lsk )
 List all key bindings.
@@ -1927,9 +1943,15 @@ flag causes the terminal state to be reset.
 Send the prefix key, or with
 .Fl 2
 the secondary prefix key, to a window as if it was pressed.
+.It Xo Ic set-keytable
+.Ar key-table
+.Xc
+Set the client's key table.  The next key from the client will be interpretted 
from
+.Ar key-table .
 .It Xo Ic unbind-key
 .Op Fl acn
-.Op Fl t Ar key-table
+.Op Fl t Ar mode-key-table
+.Op Fl T Ar key-table
 .Ar key
 .Xc
 .D1 (alias: Ic unbind )
@@ -1951,10 +1973,26 @@ If
 is present,
 .Ar key
 in
-.Ar key-table
+.Ar mode-key-table
 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 the
+.Ic set-keytable
+command.
 .El
 .Sh OPTIONS
 The appearance and behaviour of
diff --git a/tmux.h b/tmux.h
index fde94afc47b3..55463fcacce0 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;
+       char             *keytablename;
 
        struct event     identify_timer;
 
@@ -1444,6 +1444,13 @@ struct key_binding {
        RB_ENTRY(key_binding) entry;
 };
 RB_HEAD(key_bindings, key_binding);
+struct key_binding_map_entry {
+       const char *name;
+       struct key_bindings key_bindings;
+
+       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
@@ -1818,6 +1825,7 @@ extern const struct cmd_entry cmd_send_prefix_entry;
 extern const struct cmd_entry cmd_server_info_entry;
 extern const struct cmd_entry cmd_set_buffer_entry;
 extern const struct cmd_entry cmd_set_environment_entry;
+extern const struct cmd_entry cmd_set_keytable_entry;
 extern const struct cmd_entry cmd_set_option_entry;
 extern const struct cmd_entry cmd_set_window_option_entry;
 extern const struct cmd_entry cmd_show_buffer_entry;
@@ -1865,13 +1873,15 @@ 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;
+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 *);
 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);
-void    key_bindings_clean(void);
+RB_PROTOTYPE(key_binding_map, key_binding_map_entry, entry, 
key_binding_map_entry_cmp);
+struct key_bindings *key_bindings_lookup_table(const char *, int);
+struct key_binding *key_bindings_lookup(const char *, int);
+void    key_bindings_add(const char *, int, int, struct cmd_list *);
+void    key_bindings_remove(const char *, int);
 void    key_bindings_init(void);
 void    key_bindings_dispatch(struct key_binding *, struct client *);
 
-- 
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