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 | 137 +++++++++++++++++++++++++++++++++++++++++----------- server-client.c | 127 ++++++++++++++++++++++++++++-------------------- server-fn.c | 7 +++ tmux.1 | 37 ++++++++++++++ tmux.h | 35 +++++++++++--- 10 files changed, 359 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..7a39a3c27ef6 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..e4ab8d5ff2e7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -25,60 +25,143 @@ #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_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_binding * -key_bindings_lookup(int key) +struct key_binding_map_value * +key_bindings_lookup_table(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) { + e_found->value->references++; + return e_found->value; + } + + 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)); + /* one for key_binding_map and one for caller */ + e_found->value->references = 2; + RB_INSERT(key_binding_map, &key_binding_map, e_found); + + 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); + } +} + +void +key_bindings_add(const char *name, int key, int can_repeat, struct cmd_list *cmdlist) +{ + struct key_binding_map_value *table; + struct key_binding bd_search; + struct key_binding *bd; + + table = key_bindings_lookup_table(name, 1); + + bd_search.key = key; + bd = RB_FIND(key_bindings, &(table->key_bindings), &bd_search); + if (bd != NULL) { + RB_REMOVE(key_bindings, &(table->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, &(table->key_bindings), bd); bd->can_repeat = can_repeat; bd->cmdlist = cmdlist; + + key_bindings_unreference_table(table); } 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_map_value *table; + struct key_binding bd_search; + struct key_binding *bd; + + table = key_bindings_lookup_table(name, 0); + if (!table) + return; - if ((bd = key_bindings_lookup(key)) == NULL) + bd_search.key = key; + bd = RB_FIND(key_bindings, &(table->key_bindings), &bd_search); + if (!bd) { + key_bindings_unreference_table(table); return; - RB_REMOVE(key_bindings, &key_bindings, bd); + } + + RB_REMOVE(key_bindings, &(table->key_bindings), bd); cmd_list_free(bd->cmdlist); free(bd); + + if (RB_EMPTY(&(table->key_bindings))) { + e.name = name; + RB_REMOVE(key_binding_map, &key_binding_map, &e); + /* This represents key_binding_map's reference */ + key_bindings_unreference_table(table); + } + + /* and this represents the reference we own from key_bindings_lookup_table */ + key_bindings_unreference_table(table); +} + +void +key_bindings_remove_table(const char *name) +{ + struct key_binding_map_entry e; + struct key_binding_map_value *table; + + table = key_bindings_lookup_table(name, 0); + if (!table) + return; + + e.name = name; + RB_REMOVE(key_binding_map, &key_binding_map, &e); + /* This represents key_binding_map's reference */ + key_bindings_unreference_table(table); + + /* and this represents the reference we own from key_bindings_lookup_table */ + key_bindings_unreference_table(table); } void @@ -167,7 +250,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 +266,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