Hi

I don't have a lot of time right now, but here are some comments:

- What's the point of having key_binding_map_value and
  key_binding_map_entry? Why not just one key_table struct?
  key_bindings_remove can remove it from the tree and unref it, the
  unref func can free it when it hits 0.

- Also if we just have key_table then key_binding_map could be renamed
  to key_tables which is shorter.

- In unbind-key, looks like the -n and no flag table names are the wrong
  way round?

- In bind-key, I see no need for a helper function - just do something like:

        if (args_has(args, 'T'))
                table = args_get(args, 'T');
        else if (args_has(args, 'n'))
                table = "root";
        else
                table = "prefix";

        cmdlist = ....

        key_bindings_add(table, key, args_has(args, 'r'), cmdlist);
        return (CMD_RETURN_NORMAL);




On Tue, May 13, 2014 at 05:01:17PM -0700, Keith Amling wrote:
> 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

------------------------------------------------------------------------------
"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