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