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
> [email protected]
> 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
[email protected]
https://lists.sourceforge.net/lists/listinfo/tmux-users