Keith Amling
2014-05-12 23:18:16 UTC
Sorry if this isn't formatted as expected, I'm not at all familiar with
the operation of git's e-mail tools.
Keith
---
Makefile.am | 1 +
cmd-bind-key.c | 52 ++++++++++++++++---------
cmd-list-keys.c | 80 +++++++++++++++++++-------------------
cmd-set-keytable.c | 44 +++++++++++++++++++++
cmd-unbind-key.c | 48 +++++++++++++++++------
cmd.c | 1 +
format.c | 3 +-
key-bindings.c | 95 ++++++++++++++++++++++++++++-----------------
server-client.c | 110 +++++++++++++++++++++++++++++++----------------------
server.c | 1 -
tmux.1 | 46 ++++++++++++++++++++--
tmux.h | 26 +++++++++----
12 files changed, 341 insertions(+), 166 deletions(-)
create mode 100644 cmd-set-keytable.c
diff --git a/Makefile.am b/Makefile.am
index 5502de86d276..eb9fc9ed60bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,6 +123,7 @@ dist_tmux_SOURCES = \
cmd-send-keys.c \
cmd-set-buffer.c \
cmd-set-environment.c \
+ cmd-set-keytable.c \
cmd-set-option.c \
cmd-show-environment.c \
cmd-show-messages.c \
diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index 4ff3ac8431bc..e472a5a067dc 100644
--- a/cmd-bind-key.c
+++ b/cmd-bind-key.c
@@ -29,12 +29,14 @@
enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *);
-enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int);
+enum cmd_retval cmd_bind_mode_key_table(struct cmd *, struct cmd_q *, int);
+
+enum cmd_retval cmd_bind_key_table(struct cmd *, const char *, struct cmd_q *, int);
const struct cmd_entry cmd_bind_key_entry = {
"bind-key", "bind",
- "cnrt:", 1, -1,
- "[-cnr] [-t key-table] key command [arguments]",
+ "cnrt:T:", 1, -1,
+ "[-cnr] [-t mode-key-table] [-T key-table] key command [arguments]",
0,
NULL,
cmd_bind_key_exec
@@ -44,11 +46,9 @@ enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
- char *cause;
- struct cmd_list *cmdlist;
int key;
- if (args_has(args, 't')) {
+ if (args_has(args, 't') || args_has(args, 'T')) {
if (args->argc != 2 && args->argc != 3) {
cmdq_error(cmdq, "not enough arguments");
return (CMD_RETURN_ERROR);
@@ -67,24 +67,19 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
}
if (args_has(args, 't'))
- return (cmd_bind_key_table(self, cmdq, key));
+ return (cmd_bind_mode_key_table(self, cmdq, key));
- cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
- &cause);
- if (cmdlist == NULL) {
- cmdq_error(cmdq, "%s", cause);
- free(cause);
- return (CMD_RETURN_ERROR);
- }
+ if (args_has(args, 'T'))
+ return (cmd_bind_key_table(self, args_get(args, 'T'), cmdq, key));
- if (!args_has(args, 'n'))
- key |= KEYC_PREFIX;
- key_bindings_add(key, args_has(args, 'r'), cmdlist);
- return (CMD_RETURN_NORMAL);
+ if (args_has(args, 'n'))
+ return (cmd_bind_key_table(self, "ROOT", cmdq, key));
+
+ return (cmd_bind_key_table(self, "PREFIX", cmdq, key));
}
enum cmd_retval
-cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
+cmd_bind_mode_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
{
struct args *args = self->args;
const char *tablename;
@@ -131,3 +126,22 @@ cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
mbind->arg = arg != NULL ? xstrdup(arg) : NULL;
return (CMD_RETURN_NORMAL);
}
+
+enum cmd_retval
+cmd_bind_key_table(struct cmd *self, const char *tablename, struct cmd_q *cmdq, int key)
+{
+ struct args *args = self->args;
+ char *cause;
+ struct cmd_list *cmdlist;
+
+ cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
+ &cause);
+ if (cmdlist == NULL) {
+ cmdq_error(cmdq, "%s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+
+ key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist);
+ return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 615c5ce1fe67..20fc386286e9 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -41,56 +41,56 @@ const struct cmd_entry cmd_list_keys_entry = {
enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
{
- struct args *args = self->args;
- struct key_binding *bd;
- const char *key;
- char tmp[BUFSIZ], flags[8];
- size_t used;
- int width, keywidth;
+ struct args *args = self->args;
+ struct key_binding_map_entry *e;
+ struct key_binding *bd;
+ const char *key;
+ char tmp[BUFSIZ];
+ size_t used;
+ int hasrepeat, maxtablewidth, tablewidth, maxkeywidth, keywidth;
if (args_has(args, 't'))
return (cmd_list_keys_table(self, cmdq));
- width = 0;
+ hasrepeat = 0;
+ maxtablewidth = 0;
+ maxkeywidth = 0;
- RB_FOREACH(bd, key_bindings, &key_bindings) {
- key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
- if (key == NULL)
- continue;
+ RB_FOREACH(e, key_binding_map, &key_binding_map) {
+ RB_FOREACH(bd, key_bindings, &(e->key_bindings)) {
+ key = key_string_lookup_key(bd->key);
+ if (key == NULL)
+ continue;
- keywidth = strlen(key);
- if (!(bd->key & KEYC_PREFIX)) {
if (bd->can_repeat)
- keywidth += 4;
- else
- keywidth += 3;
- } else if (bd->can_repeat)
- keywidth += 3;
- if (keywidth > width)
- width = keywidth;
- }
+ hasrepeat = 1;
- RB_FOREACH(bd, key_bindings, &key_bindings) {
- key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
- if (key == NULL)
- continue;
+ tablewidth = strlen(e->name);
+ if (tablewidth > maxtablewidth)
+ maxtablewidth = tablewidth;
- *flags = '\0';
- if (!(bd->key & KEYC_PREFIX)) {
- if (bd->can_repeat)
- xsnprintf(flags, sizeof flags, "-rn ");
- else
- xsnprintf(flags, sizeof flags, "-n ");
- } else if (bd->can_repeat)
- xsnprintf(flags, sizeof flags, "-r ");
-
- used = xsnprintf(tmp, sizeof tmp, "%s%*s ",
- flags, (int) (width - strlen(flags)), key);
- if (used >= sizeof tmp)
- continue;
+ keywidth = strlen(key);
+ if (keywidth > maxkeywidth)
+ maxkeywidth = keywidth;
+ }
+ }
- cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
- cmdq_print(cmdq, "bind-key %s", tmp);
+ RB_FOREACH(e, key_binding_map, &key_binding_map) {
+ RB_FOREACH(bd, key_bindings, &(e->key_bindings)) {
+ key = key_string_lookup_key(bd->key);
+ if (key == NULL)
+ continue;
+
+ used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ",
+ (hasrepeat ? (bd->can_repeat ? "-r " : " ") : ""),
+ (int) maxtablewidth, e->name,
+ (int) maxkeywidth, key);
+ if (used >= sizeof tmp)
+ continue;
+
+ cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
+ cmdq_print(cmdq, "bind-key %s", tmp);
+ }
}
return (CMD_RETURN_NORMAL);
diff --git a/cmd-set-keytable.c b/cmd-set-keytable.c
new file mode 100644
index 000000000000..b009233101b4
--- /dev/null
+++ b/cmd-set-keytable.c
@@ -0,0 +1,44 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <***@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+void cmd_set_keytable_key_binding(struct cmd *, int);
+enum cmd_retval cmd_set_keytable_exec(struct cmd *, struct cmd_q *);
+
+const struct cmd_entry cmd_set_keytable_entry = {
+ "set-keytable", "setkt",
+ "", 1, 1,
+ "table",
+ 0,
+ NULL,
+ cmd_set_keytable_exec
+};
+
+enum cmd_retval
+cmd_set_keytable_exec(struct cmd *self, struct cmd_q *cmdq)
+{
+ free(cmdq->client->keytablename);
+ cmdq->client->keytablename = xstrdup(self->args->argv[0]);
+ return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c
index cf6ad506195f..cbc4743f3265 100644
--- a/cmd-unbind-key.c
+++ b/cmd-unbind-key.c
@@ -27,12 +27,13 @@
*/
enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *);
-enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int);
+enum cmd_retval cmd_unbind_mode_key_table(struct cmd *, struct cmd_q *, int);
+void cmd_unbind_key_wipe(const char *);
const struct cmd_entry cmd_unbind_key_entry = {
"unbind-key", "unbind",
- "acnt:", 0, 1,
- "[-acn] [-t key-table] key",
+ "acnt:T:", 0, 1,
+ "[-acn] [-t mode-key-table] [-T key-table] key",
0,
NULL,
cmd_unbind_key_exec
@@ -42,7 +43,6 @@ enum cmd_retval
cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
- struct key_binding *bd;
int key;
if (!args_has(args, 'a')) {
@@ -64,24 +64,34 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
}
if (args_has(args, 't'))
- return (cmd_unbind_key_table(self, cmdq, key));
+ return (cmd_unbind_mode_key_table(self, cmdq, key));
if (key == KEYC_NONE) {
- while (!RB_EMPTY(&key_bindings)) {
- bd = RB_ROOT(&key_bindings);
- key_bindings_remove(bd->key);
+ if (args_has(args, 'T')) {
+ cmd_unbind_key_wipe(args_get(args, 't'));
+ return (CMD_RETURN_NORMAL);
}
+ cmd_unbind_key_wipe("ROOT");
+ cmd_unbind_key_wipe("PREFIX");
+ return (CMD_RETURN_NORMAL);
+ }
+
+ if (args_has(args, 'T')) {
+ key_bindings_remove(args_get(args, 'T'), key);
+ return (CMD_RETURN_NORMAL);
+ }
+
+ if (args_has(args, 'n')) {
+ key_bindings_remove("PREFIX", key);
return (CMD_RETURN_NORMAL);
}
- if (!args_has(args, 'n'))
- key |= KEYC_PREFIX;
- key_bindings_remove(key);
+ key_bindings_remove("ROOT", key);
return (CMD_RETURN_NORMAL);
}
enum cmd_retval
-cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
+cmd_unbind_mode_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
{
struct args *args = self->args;
const char *tablename;
@@ -111,3 +121,17 @@ cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
}
return (CMD_RETURN_NORMAL);
}
+
+void
+cmd_unbind_key_wipe(const char *tablename) {
+ struct key_bindings *key_bindings;
+ struct key_binding *bd;
+
+ while (1) {
+ key_bindings = key_bindings_lookup_table(tablename, 0);
+ if (key_bindings == NULL)
+ return;
+ bd = RB_ROOT(key_bindings);
+ key_bindings_remove(tablename, bd->key);
+ }
+}
diff --git a/cmd.c b/cmd.c
index f2d88c050637..0c11a84766f7 100644
--- a/cmd.c
+++ b/cmd.c
@@ -95,6 +95,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_server_info_entry,
&cmd_set_buffer_entry,
&cmd_set_environment_entry,
+ &cmd_set_keytable_entry,
&cmd_set_option_entry,
&cmd_set_window_option_entry,
&cmd_show_buffer_entry,
diff --git a/format.c b/format.c
index 6f988b9ac2cc..4d3fc057f246 100644
--- a/format.c
+++ b/format.c
@@ -435,7 +435,8 @@ format_client(struct format_tree *ft, struct client *c)
*strchr(tim, '\n') = '\0';
format_add(ft, "client_activity_string", "%s", tim);
- format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
+ format_add(ft, "client_prefix", strcmp(c->keytablename, "ROOT") ? "1": "0");
+ format_add(ft, "client_keytablename", "%s", c->keytablename);
if (c->tty.flags & TTY_UTF8)
format_add(ft, "client_utf8", "%d", 1);
diff --git a/key-bindings.c b/key-bindings.c
index f725508bce62..e5da8c97cd1d 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -25,72 +25,97 @@
#include "tmux.h"
RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+RB_GENERATE(key_binding_map, key_binding_map_entry, entry, key_binding_map_entry_cmp);
-struct key_bindings key_bindings;
-struct key_bindings dead_key_bindings;
+struct key_binding_map key_binding_map;
+
+int
+key_binding_map_entry_cmp(struct key_binding_map_entry *e1, struct key_binding_map_entry *e2)
+{
+ return strcmp(e1->name, e2->name);
+}
int
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
{
- int key1, key2;
-
- key1 = bd1->key & ~KEYC_PREFIX;
- key2 = bd2->key & ~KEYC_PREFIX;
- if (key1 != key2)
- return (key1 - key2);
-
- if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
- return (-1);
- if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
- return (1);
- return (0);
+ return (bd1->key - bd2->key);
+}
+
+struct key_bindings *
+key_bindings_lookup_table(const char *name, int create)
+{
+ struct key_binding_map_entry e;
+ struct key_binding_map_entry *e_found;
+
+ e.name = name;
+ e_found = RB_FIND(key_binding_map, &key_binding_map, &e);
+ if (e_found)
+ return &(e_found->key_bindings);
+
+ if (!create)
+ return NULL;
+
+ e_found = xmalloc(sizeof *e_found);
+ e_found->name = xstrdup(name);
+ RB_INIT(&(e_found->key_bindings));
+ RB_INSERT(key_binding_map, &key_binding_map, e_found);
+
+ return &(e_found->key_bindings);
}
struct key_binding *
-key_bindings_lookup(int key)
+key_bindings_lookup(const char *name, int key)
{
+ struct key_bindings *key_bindings;
struct key_binding bd;
+ key_bindings = key_bindings_lookup_table(name, 0);
+ if (!key_bindings)
+ return NULL;
+
bd.key = key;
- return (RB_FIND(key_bindings, &key_bindings, &bd));
+ return (RB_FIND(key_bindings, key_bindings, &bd));
}
void
-key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
+key_bindings_add(const char *name, int key, int can_repeat, struct cmd_list *cmdlist)
{
+ struct key_bindings *key_bindings;
struct key_binding *bd;
- key_bindings_remove(key);
+ key_bindings_remove(name, key);
+
+ key_bindings = key_bindings_lookup_table(name, 1);
bd = xmalloc(sizeof *bd);
bd->key = key;
- RB_INSERT(key_bindings, &key_bindings, bd);
+ RB_INSERT(key_bindings, key_bindings, bd);
bd->can_repeat = can_repeat;
bd->cmdlist = cmdlist;
}
void
-key_bindings_remove(int key)
+key_bindings_remove(const char *name, int key)
{
+ struct key_binding_map_entry e;
+ struct key_bindings *key_bindings;
struct key_binding *bd;
- if ((bd = key_bindings_lookup(key)) == NULL)
+ if ((bd = key_bindings_lookup(name, key)) == NULL)
return;
- RB_REMOVE(key_bindings, &key_bindings, bd);
- RB_INSERT(key_bindings, &dead_key_bindings, bd);
-}
-void
-key_bindings_clean(void)
-{
- struct key_binding *bd;
+ key_bindings = key_bindings_lookup_table(name, 0);
+ if (!key_bindings)
+ return;
+
+ RB_REMOVE(key_bindings, key_bindings, bd);
+ cmd_list_free(bd->cmdlist);
+ free(bd);
- while (!RB_EMPTY(&dead_key_bindings)) {
- bd = RB_ROOT(&dead_key_bindings);
- RB_REMOVE(key_bindings, &dead_key_bindings, bd);
- cmd_list_free(bd->cmdlist);
- free(bd);
+ if (RB_EMPTY(key_bindings)) {
+ e.name = name;
+ RB_REMOVE(key_binding_map, &key_binding_map, &e);
}
}
@@ -180,7 +205,7 @@ key_bindings_init(void)
struct cmd *cmd;
struct cmd_list *cmdlist;
- RB_INIT(&key_bindings);
+ RB_INIT(&key_binding_map);
for (i = 0; i < nitems(table); i++) {
cmdlist = xcalloc(1, sizeof *cmdlist);
@@ -196,7 +221,7 @@ key_bindings_init(void)
TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry);
key_bindings_add(
- table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist);
+ "PREFIX", table[i].key, table[i].can_repeat, cmdlist);
}
}
diff --git a/server-client.c b/server-client.c
index e225de309b5f..c853712d3f10 100644
--- a/server-client.c
+++ b/server-client.c
@@ -98,6 +98,7 @@ server_client_create(int fd)
c->tty.mouse.flags = 0;
c->flags |= CLIENT_FOCUSED;
+ c->keytablename = xstrdup("ROOT");
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
@@ -170,6 +171,8 @@ server_client_lost(struct client *c)
evtimer_del(&c->repeat_timer);
+ free(c->keytablename);
+
if (event_initialized(&c->identify_timer))
evtimer_del(&c->identify_timer);
@@ -427,64 +430,78 @@ server_client_handle_key(struct client *c, int key)
/* Treat prefix as a regular key when pasting is detected. */
ispaste = server_client_assume_paste(s);
- if (ispaste)
- isprefix = 0;
+ if (ispaste) {
+ if (!(c->flags & CLIENT_READONLY))
+ window_pane_key(wp, s, key);
+ return;
+ }
- /* No previous prefix key. */
- if (!(c->flags & CLIENT_PREFIX)) {
- if (isprefix) {
- c->flags |= CLIENT_PREFIX;
+ /* Try to see if we hit a key binding. */
+ while (1) {
+ if ((bd = key_bindings_lookup(c->keytablename, key)) != NULL) {
+ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
+ /* We don't honor repeating into a non-repeat binding, fall back to ROOT and try again */
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ c->flags &= ~CLIENT_REPEAT;
+ server_status_client(c);
+ continue;
+ }
+ xtimeout = options_get_number(&s->options, "repeat-time");
+ if (xtimeout != 0 && bd->can_repeat) {
+ /* Now repeating in same keytable */
+ c->flags |= CLIENT_REPEAT;
+
+ tv.tv_sec = xtimeout / 1000;
+ tv.tv_usec = (xtimeout % 1000) * 1000L;
+ evtimer_del(&c->repeat_timer);
+ evtimer_add(&c->repeat_timer, &tv);
+ }
+ else {
+ /* "Stop" (or don't start) repeating */
+ c->flags &= ~CLIENT_REPEAT;
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ }
server_status_client(c);
+ key_bindings_dispatch(bd, c);
return;
}
- /* Try as a non-prefix key binding. */
- if (ispaste || (bd = key_bindings_lookup(key)) == NULL) {
- if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
- } else
- key_bindings_dispatch(bd, c);
- return;
- }
-
- /* Prefix key already pressed. Reset prefix and lookup key. */
- c->flags &= ~CLIENT_PREFIX;
- server_status_client(c);
- if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
- /* If repeating, treat this as a key, else ignore. */
if (c->flags & CLIENT_REPEAT) {
+ /* We missed, but we were in repeat, fall back to ROOT and try again */
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
+ server_status_client(c);
+ continue;
}
- return;
+
+ /* Actual miss */
+ break;
}
- /* If already repeating, but this key can't repeat, skip it. */
- if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
- c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
+ /* A miss in a non-ROOT keytable fails out to ROOT */
+ if (strcmp(c->keytablename, "ROOT")) {
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ server_status_client(c);
return;
}
- /* If this key can repeat, reset the repeat flags and timer. */
- xtimeout = options_get_number(&s->options, "repeat-time");
- if (xtimeout != 0 && bd->can_repeat) {
- c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
-
- tv.tv_sec = xtimeout / 1000;
- tv.tv_usec = (xtimeout % 1000) * 1000L;
- evtimer_del(&c->repeat_timer);
- evtimer_add(&c->repeat_timer, &tv);
+ /* A prefix miss in ROOT switched to PREFIX */
+ if (isprefix) {
+ /* Prefix key switches to PREFIX table */
+ free(c->keytablename);
+ c->keytablename = xstrdup("PREFIX");
+ server_status_client(c);
+ return;
}
- /* Dispatch the command. */
- key_bindings_dispatch(bd, c);
+ /* Anything else in ROOT is straight through */
+ if (!(c->flags & CLIENT_READONLY)) {
+ window_pane_key(wp, s, key);
+ }
}
/* Client functions that need to happen every loop. */
@@ -700,9 +717,10 @@ server_client_repeat_timer(unused int fd, unused short events, void *data)
struct client *c = data;
if (c->flags & CLIENT_REPEAT) {
- if (c->flags & CLIENT_PREFIX)
- server_status_client(c);
- c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ c->flags &= ~CLIENT_REPEAT;
+ server_status_client(c);
}
}
diff --git a/server.c b/server.c
index d3ac0f8b0cdd..5ae44df23eff 100644
--- a/server.c
+++ b/server.c
@@ -209,7 +209,6 @@ server_loop(void)
server_window_loop();
server_client_loop();
- key_bindings_clean();
server_clean_dead();
}
}
diff --git a/tmux.1 b/tmux.1
index c05eacfdbc53..3a89e26c20d3 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1842,7 +1842,8 @@ Commands related to key bindings are as follows:
.Bl -tag -width Ds
.It Xo Ic bind-key
.Op Fl cnr
-.Op Fl t Ar key-table
+.Op Fl t Ar mode-key-table
+.Op Fl T Ar key-table
.Ar key Ar command Op Ar arguments
.Xc
.D1 (alias: Ic bind )
@@ -1871,13 +1872,28 @@ If
is present,
.Ar key
is bound in
-.Ar key-table :
+.Ar mode-key-table :
the binding for command mode with
.Fl c
or for normal mode without.
To view the default bindings and possible commands, see the
.Ic list-keys
command.
+.Pp
+If
+.Fl T
+is present,
+.Ar key
+is bound in
+.Ar key-table :
+.Em PREFIX
+corresponds to the default,
+.Em ROOT
+corresponds to
+.Fl n ,
+and custom values may be used with the
+.Ic set-keytable
+command.
.It Ic list-keys Op Fl t Ar key-table
.D1 (alias: Ic lsk )
List all key bindings.
@@ -1927,9 +1943,15 @@ flag causes the terminal state to be reset.
Send the prefix key, or with
.Fl 2
the secondary prefix key, to a window as if it was pressed.
+.It Xo Ic set-keytable
+.Ar key-table
+.Xc
+Set the client's key table. The next key from the client will be interpretted from
+.Ar key-table .
.It Xo Ic unbind-key
.Op Fl acn
-.Op Fl t Ar key-table
+.Op Fl t Ar mode-key-table
+.Op Fl T Ar key-table
.Ar key
.Xc
.D1 (alias: Ic unbind )
@@ -1951,10 +1973,26 @@ If
is present,
.Ar key
in
-.Ar key-table
+.Ar mode-key-table
is unbound: the binding for command mode with
.Fl c
or for normal mode without.
+.Pp
+If
+.Fl T
+is present,
+.Ar key
+in
+.Ar key-table
+is unbound:
+.Em PREFIX
+corresponds to the default,
+.Em ROOT
+corresponds to
+.Fl n ,
+and custom values may be used with the
+.Ic set-keytable
+command.
.El
.Sh OPTIONS
The appearance and behaviour of
diff --git a/tmux.h b/tmux.h
index fde94afc47b3..55463fcacce0 100644
--- a/tmux.h
+++ b/tmux.h
@@ -164,10 +164,9 @@ extern char **environ;
#define KEYC_ESCAPE 0x2000
#define KEYC_CTRL 0x4000
#define KEYC_SHIFT 0x8000
-#define KEYC_PREFIX 0x10000
/* Mask to obtain key w/o modifiers. */
-#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX)
+#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT)
#define KEYC_MASK_KEY (~KEYC_MASK_MOD)
/* Other key codes. */
@@ -1298,7 +1297,7 @@ struct client {
struct screen status;
#define CLIENT_TERMINAL 0x1
-#define CLIENT_PREFIX 0x2
+/* replaced by keytablename: #define CLIENT_PREFIX 0x2 */
#define CLIENT_EXIT 0x4
#define CLIENT_REDRAW 0x8
#define CLIENT_STATUS 0x10
@@ -1317,6 +1316,7 @@ struct client {
#define CLIENT_256COLOURS 0x20000
#define CLIENT_IDENTIFIED 0x40000
int flags;
+ char *keytablename;
struct event identify_timer;
@@ -1444,6 +1444,13 @@ struct key_binding {
RB_ENTRY(key_binding) entry;
};
RB_HEAD(key_bindings, key_binding);
+struct key_binding_map_entry {
+ const char *name;
+ struct key_bindings key_bindings;
+
+ RB_ENTRY(key_binding_map_entry) entry;
+};
+RB_HEAD(key_binding_map, key_binding_map_entry);
/*
* Option table entries. The option table is the user-visible part of the
@@ -1818,6 +1825,7 @@ extern const struct cmd_entry cmd_send_prefix_entry;
extern const struct cmd_entry cmd_server_info_entry;
extern const struct cmd_entry cmd_set_buffer_entry;
extern const struct cmd_entry cmd_set_environment_entry;
+extern const struct cmd_entry cmd_set_keytable_entry;
extern const struct cmd_entry cmd_set_option_entry;
extern const struct cmd_entry cmd_set_window_option_entry;
extern const struct cmd_entry cmd_show_buffer_entry;
@@ -1865,13 +1873,15 @@ int cmd_string_parse(const char *, struct cmd_list **, const char *,
int client_main(int, char **, int);
/* key-bindings.c */
-extern struct key_bindings key_bindings;
+extern struct key_binding_map key_binding_map;
+int key_binding_map_entry_cmp(struct key_binding_map_entry *, struct key_binding_map_entry *);
int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp);
-struct key_binding *key_bindings_lookup(int);
-void key_bindings_add(int, int, struct cmd_list *);
-void key_bindings_remove(int);
-void key_bindings_clean(void);
+RB_PROTOTYPE(key_binding_map, key_binding_map_entry, entry, key_binding_map_entry_cmp);
+struct key_bindings *key_bindings_lookup_table(const char *, int);
+struct key_binding *key_bindings_lookup(const char *, int);
+void key_bindings_add(const char *, int, int, struct cmd_list *);
+void key_bindings_remove(const char *, int);
void key_bindings_init(void);
void key_bindings_dispatch(struct key_binding *, struct client *);
the operation of git's e-mail tools.
Keith
---
Makefile.am | 1 +
cmd-bind-key.c | 52 ++++++++++++++++---------
cmd-list-keys.c | 80 +++++++++++++++++++-------------------
cmd-set-keytable.c | 44 +++++++++++++++++++++
cmd-unbind-key.c | 48 +++++++++++++++++------
cmd.c | 1 +
format.c | 3 +-
key-bindings.c | 95 ++++++++++++++++++++++++++++-----------------
server-client.c | 110 +++++++++++++++++++++++++++++++----------------------
server.c | 1 -
tmux.1 | 46 ++++++++++++++++++++--
tmux.h | 26 +++++++++----
12 files changed, 341 insertions(+), 166 deletions(-)
create mode 100644 cmd-set-keytable.c
diff --git a/Makefile.am b/Makefile.am
index 5502de86d276..eb9fc9ed60bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,6 +123,7 @@ dist_tmux_SOURCES = \
cmd-send-keys.c \
cmd-set-buffer.c \
cmd-set-environment.c \
+ cmd-set-keytable.c \
cmd-set-option.c \
cmd-show-environment.c \
cmd-show-messages.c \
diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index 4ff3ac8431bc..e472a5a067dc 100644
--- a/cmd-bind-key.c
+++ b/cmd-bind-key.c
@@ -29,12 +29,14 @@
enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *);
-enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int);
+enum cmd_retval cmd_bind_mode_key_table(struct cmd *, struct cmd_q *, int);
+
+enum cmd_retval cmd_bind_key_table(struct cmd *, const char *, struct cmd_q *, int);
const struct cmd_entry cmd_bind_key_entry = {
"bind-key", "bind",
- "cnrt:", 1, -1,
- "[-cnr] [-t key-table] key command [arguments]",
+ "cnrt:T:", 1, -1,
+ "[-cnr] [-t mode-key-table] [-T key-table] key command [arguments]",
0,
NULL,
cmd_bind_key_exec
@@ -44,11 +46,9 @@ enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
- char *cause;
- struct cmd_list *cmdlist;
int key;
- if (args_has(args, 't')) {
+ if (args_has(args, 't') || args_has(args, 'T')) {
if (args->argc != 2 && args->argc != 3) {
cmdq_error(cmdq, "not enough arguments");
return (CMD_RETURN_ERROR);
@@ -67,24 +67,19 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
}
if (args_has(args, 't'))
- return (cmd_bind_key_table(self, cmdq, key));
+ return (cmd_bind_mode_key_table(self, cmdq, key));
- cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
- &cause);
- if (cmdlist == NULL) {
- cmdq_error(cmdq, "%s", cause);
- free(cause);
- return (CMD_RETURN_ERROR);
- }
+ if (args_has(args, 'T'))
+ return (cmd_bind_key_table(self, args_get(args, 'T'), cmdq, key));
- if (!args_has(args, 'n'))
- key |= KEYC_PREFIX;
- key_bindings_add(key, args_has(args, 'r'), cmdlist);
- return (CMD_RETURN_NORMAL);
+ if (args_has(args, 'n'))
+ return (cmd_bind_key_table(self, "ROOT", cmdq, key));
+
+ return (cmd_bind_key_table(self, "PREFIX", cmdq, key));
}
enum cmd_retval
-cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
+cmd_bind_mode_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
{
struct args *args = self->args;
const char *tablename;
@@ -131,3 +126,22 @@ cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
mbind->arg = arg != NULL ? xstrdup(arg) : NULL;
return (CMD_RETURN_NORMAL);
}
+
+enum cmd_retval
+cmd_bind_key_table(struct cmd *self, const char *tablename, struct cmd_q *cmdq, int key)
+{
+ struct args *args = self->args;
+ char *cause;
+ struct cmd_list *cmdlist;
+
+ cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
+ &cause);
+ if (cmdlist == NULL) {
+ cmdq_error(cmdq, "%s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+
+ key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist);
+ return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 615c5ce1fe67..20fc386286e9 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -41,56 +41,56 @@ const struct cmd_entry cmd_list_keys_entry = {
enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
{
- struct args *args = self->args;
- struct key_binding *bd;
- const char *key;
- char tmp[BUFSIZ], flags[8];
- size_t used;
- int width, keywidth;
+ struct args *args = self->args;
+ struct key_binding_map_entry *e;
+ struct key_binding *bd;
+ const char *key;
+ char tmp[BUFSIZ];
+ size_t used;
+ int hasrepeat, maxtablewidth, tablewidth, maxkeywidth, keywidth;
if (args_has(args, 't'))
return (cmd_list_keys_table(self, cmdq));
- width = 0;
+ hasrepeat = 0;
+ maxtablewidth = 0;
+ maxkeywidth = 0;
- RB_FOREACH(bd, key_bindings, &key_bindings) {
- key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
- if (key == NULL)
- continue;
+ RB_FOREACH(e, key_binding_map, &key_binding_map) {
+ RB_FOREACH(bd, key_bindings, &(e->key_bindings)) {
+ key = key_string_lookup_key(bd->key);
+ if (key == NULL)
+ continue;
- keywidth = strlen(key);
- if (!(bd->key & KEYC_PREFIX)) {
if (bd->can_repeat)
- keywidth += 4;
- else
- keywidth += 3;
- } else if (bd->can_repeat)
- keywidth += 3;
- if (keywidth > width)
- width = keywidth;
- }
+ hasrepeat = 1;
- RB_FOREACH(bd, key_bindings, &key_bindings) {
- key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
- if (key == NULL)
- continue;
+ tablewidth = strlen(e->name);
+ if (tablewidth > maxtablewidth)
+ maxtablewidth = tablewidth;
- *flags = '\0';
- if (!(bd->key & KEYC_PREFIX)) {
- if (bd->can_repeat)
- xsnprintf(flags, sizeof flags, "-rn ");
- else
- xsnprintf(flags, sizeof flags, "-n ");
- } else if (bd->can_repeat)
- xsnprintf(flags, sizeof flags, "-r ");
-
- used = xsnprintf(tmp, sizeof tmp, "%s%*s ",
- flags, (int) (width - strlen(flags)), key);
- if (used >= sizeof tmp)
- continue;
+ keywidth = strlen(key);
+ if (keywidth > maxkeywidth)
+ maxkeywidth = keywidth;
+ }
+ }
- cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
- cmdq_print(cmdq, "bind-key %s", tmp);
+ RB_FOREACH(e, key_binding_map, &key_binding_map) {
+ RB_FOREACH(bd, key_bindings, &(e->key_bindings)) {
+ key = key_string_lookup_key(bd->key);
+ if (key == NULL)
+ continue;
+
+ used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ",
+ (hasrepeat ? (bd->can_repeat ? "-r " : " ") : ""),
+ (int) maxtablewidth, e->name,
+ (int) maxkeywidth, key);
+ if (used >= sizeof tmp)
+ continue;
+
+ cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
+ cmdq_print(cmdq, "bind-key %s", tmp);
+ }
}
return (CMD_RETURN_NORMAL);
diff --git a/cmd-set-keytable.c b/cmd-set-keytable.c
new file mode 100644
index 000000000000..b009233101b4
--- /dev/null
+++ b/cmd-set-keytable.c
@@ -0,0 +1,44 @@
+/* $Id$ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <***@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+void cmd_set_keytable_key_binding(struct cmd *, int);
+enum cmd_retval cmd_set_keytable_exec(struct cmd *, struct cmd_q *);
+
+const struct cmd_entry cmd_set_keytable_entry = {
+ "set-keytable", "setkt",
+ "", 1, 1,
+ "table",
+ 0,
+ NULL,
+ cmd_set_keytable_exec
+};
+
+enum cmd_retval
+cmd_set_keytable_exec(struct cmd *self, struct cmd_q *cmdq)
+{
+ free(cmdq->client->keytablename);
+ cmdq->client->keytablename = xstrdup(self->args->argv[0]);
+ return (CMD_RETURN_NORMAL);
+}
diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c
index cf6ad506195f..cbc4743f3265 100644
--- a/cmd-unbind-key.c
+++ b/cmd-unbind-key.c
@@ -27,12 +27,13 @@
*/
enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *);
-enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int);
+enum cmd_retval cmd_unbind_mode_key_table(struct cmd *, struct cmd_q *, int);
+void cmd_unbind_key_wipe(const char *);
const struct cmd_entry cmd_unbind_key_entry = {
"unbind-key", "unbind",
- "acnt:", 0, 1,
- "[-acn] [-t key-table] key",
+ "acnt:T:", 0, 1,
+ "[-acn] [-t mode-key-table] [-T key-table] key",
0,
NULL,
cmd_unbind_key_exec
@@ -42,7 +43,6 @@ enum cmd_retval
cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
- struct key_binding *bd;
int key;
if (!args_has(args, 'a')) {
@@ -64,24 +64,34 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
}
if (args_has(args, 't'))
- return (cmd_unbind_key_table(self, cmdq, key));
+ return (cmd_unbind_mode_key_table(self, cmdq, key));
if (key == KEYC_NONE) {
- while (!RB_EMPTY(&key_bindings)) {
- bd = RB_ROOT(&key_bindings);
- key_bindings_remove(bd->key);
+ if (args_has(args, 'T')) {
+ cmd_unbind_key_wipe(args_get(args, 't'));
+ return (CMD_RETURN_NORMAL);
}
+ cmd_unbind_key_wipe("ROOT");
+ cmd_unbind_key_wipe("PREFIX");
+ return (CMD_RETURN_NORMAL);
+ }
+
+ if (args_has(args, 'T')) {
+ key_bindings_remove(args_get(args, 'T'), key);
+ return (CMD_RETURN_NORMAL);
+ }
+
+ if (args_has(args, 'n')) {
+ key_bindings_remove("PREFIX", key);
return (CMD_RETURN_NORMAL);
}
- if (!args_has(args, 'n'))
- key |= KEYC_PREFIX;
- key_bindings_remove(key);
+ key_bindings_remove("ROOT", key);
return (CMD_RETURN_NORMAL);
}
enum cmd_retval
-cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
+cmd_unbind_mode_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
{
struct args *args = self->args;
const char *tablename;
@@ -111,3 +121,17 @@ cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key)
}
return (CMD_RETURN_NORMAL);
}
+
+void
+cmd_unbind_key_wipe(const char *tablename) {
+ struct key_bindings *key_bindings;
+ struct key_binding *bd;
+
+ while (1) {
+ key_bindings = key_bindings_lookup_table(tablename, 0);
+ if (key_bindings == NULL)
+ return;
+ bd = RB_ROOT(key_bindings);
+ key_bindings_remove(tablename, bd->key);
+ }
+}
diff --git a/cmd.c b/cmd.c
index f2d88c050637..0c11a84766f7 100644
--- a/cmd.c
+++ b/cmd.c
@@ -95,6 +95,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_server_info_entry,
&cmd_set_buffer_entry,
&cmd_set_environment_entry,
+ &cmd_set_keytable_entry,
&cmd_set_option_entry,
&cmd_set_window_option_entry,
&cmd_show_buffer_entry,
diff --git a/format.c b/format.c
index 6f988b9ac2cc..4d3fc057f246 100644
--- a/format.c
+++ b/format.c
@@ -435,7 +435,8 @@ format_client(struct format_tree *ft, struct client *c)
*strchr(tim, '\n') = '\0';
format_add(ft, "client_activity_string", "%s", tim);
- format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
+ format_add(ft, "client_prefix", strcmp(c->keytablename, "ROOT") ? "1": "0");
+ format_add(ft, "client_keytablename", "%s", c->keytablename);
if (c->tty.flags & TTY_UTF8)
format_add(ft, "client_utf8", "%d", 1);
diff --git a/key-bindings.c b/key-bindings.c
index f725508bce62..e5da8c97cd1d 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -25,72 +25,97 @@
#include "tmux.h"
RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+RB_GENERATE(key_binding_map, key_binding_map_entry, entry, key_binding_map_entry_cmp);
-struct key_bindings key_bindings;
-struct key_bindings dead_key_bindings;
+struct key_binding_map key_binding_map;
+
+int
+key_binding_map_entry_cmp(struct key_binding_map_entry *e1, struct key_binding_map_entry *e2)
+{
+ return strcmp(e1->name, e2->name);
+}
int
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
{
- int key1, key2;
-
- key1 = bd1->key & ~KEYC_PREFIX;
- key2 = bd2->key & ~KEYC_PREFIX;
- if (key1 != key2)
- return (key1 - key2);
-
- if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
- return (-1);
- if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
- return (1);
- return (0);
+ return (bd1->key - bd2->key);
+}
+
+struct key_bindings *
+key_bindings_lookup_table(const char *name, int create)
+{
+ struct key_binding_map_entry e;
+ struct key_binding_map_entry *e_found;
+
+ e.name = name;
+ e_found = RB_FIND(key_binding_map, &key_binding_map, &e);
+ if (e_found)
+ return &(e_found->key_bindings);
+
+ if (!create)
+ return NULL;
+
+ e_found = xmalloc(sizeof *e_found);
+ e_found->name = xstrdup(name);
+ RB_INIT(&(e_found->key_bindings));
+ RB_INSERT(key_binding_map, &key_binding_map, e_found);
+
+ return &(e_found->key_bindings);
}
struct key_binding *
-key_bindings_lookup(int key)
+key_bindings_lookup(const char *name, int key)
{
+ struct key_bindings *key_bindings;
struct key_binding bd;
+ key_bindings = key_bindings_lookup_table(name, 0);
+ if (!key_bindings)
+ return NULL;
+
bd.key = key;
- return (RB_FIND(key_bindings, &key_bindings, &bd));
+ return (RB_FIND(key_bindings, key_bindings, &bd));
}
void
-key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
+key_bindings_add(const char *name, int key, int can_repeat, struct cmd_list *cmdlist)
{
+ struct key_bindings *key_bindings;
struct key_binding *bd;
- key_bindings_remove(key);
+ key_bindings_remove(name, key);
+
+ key_bindings = key_bindings_lookup_table(name, 1);
bd = xmalloc(sizeof *bd);
bd->key = key;
- RB_INSERT(key_bindings, &key_bindings, bd);
+ RB_INSERT(key_bindings, key_bindings, bd);
bd->can_repeat = can_repeat;
bd->cmdlist = cmdlist;
}
void
-key_bindings_remove(int key)
+key_bindings_remove(const char *name, int key)
{
+ struct key_binding_map_entry e;
+ struct key_bindings *key_bindings;
struct key_binding *bd;
- if ((bd = key_bindings_lookup(key)) == NULL)
+ if ((bd = key_bindings_lookup(name, key)) == NULL)
return;
- RB_REMOVE(key_bindings, &key_bindings, bd);
- RB_INSERT(key_bindings, &dead_key_bindings, bd);
-}
-void
-key_bindings_clean(void)
-{
- struct key_binding *bd;
+ key_bindings = key_bindings_lookup_table(name, 0);
+ if (!key_bindings)
+ return;
+
+ RB_REMOVE(key_bindings, key_bindings, bd);
+ cmd_list_free(bd->cmdlist);
+ free(bd);
- while (!RB_EMPTY(&dead_key_bindings)) {
- bd = RB_ROOT(&dead_key_bindings);
- RB_REMOVE(key_bindings, &dead_key_bindings, bd);
- cmd_list_free(bd->cmdlist);
- free(bd);
+ if (RB_EMPTY(key_bindings)) {
+ e.name = name;
+ RB_REMOVE(key_binding_map, &key_binding_map, &e);
}
}
@@ -180,7 +205,7 @@ key_bindings_init(void)
struct cmd *cmd;
struct cmd_list *cmdlist;
- RB_INIT(&key_bindings);
+ RB_INIT(&key_binding_map);
for (i = 0; i < nitems(table); i++) {
cmdlist = xcalloc(1, sizeof *cmdlist);
@@ -196,7 +221,7 @@ key_bindings_init(void)
TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry);
key_bindings_add(
- table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist);
+ "PREFIX", table[i].key, table[i].can_repeat, cmdlist);
}
}
diff --git a/server-client.c b/server-client.c
index e225de309b5f..c853712d3f10 100644
--- a/server-client.c
+++ b/server-client.c
@@ -98,6 +98,7 @@ server_client_create(int fd)
c->tty.mouse.flags = 0;
c->flags |= CLIENT_FOCUSED;
+ c->keytablename = xstrdup("ROOT");
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
@@ -170,6 +171,8 @@ server_client_lost(struct client *c)
evtimer_del(&c->repeat_timer);
+ free(c->keytablename);
+
if (event_initialized(&c->identify_timer))
evtimer_del(&c->identify_timer);
@@ -427,64 +430,78 @@ server_client_handle_key(struct client *c, int key)
/* Treat prefix as a regular key when pasting is detected. */
ispaste = server_client_assume_paste(s);
- if (ispaste)
- isprefix = 0;
+ if (ispaste) {
+ if (!(c->flags & CLIENT_READONLY))
+ window_pane_key(wp, s, key);
+ return;
+ }
- /* No previous prefix key. */
- if (!(c->flags & CLIENT_PREFIX)) {
- if (isprefix) {
- c->flags |= CLIENT_PREFIX;
+ /* Try to see if we hit a key binding. */
+ while (1) {
+ if ((bd = key_bindings_lookup(c->keytablename, key)) != NULL) {
+ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
+ /* We don't honor repeating into a non-repeat binding, fall back to ROOT and try again */
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ c->flags &= ~CLIENT_REPEAT;
+ server_status_client(c);
+ continue;
+ }
+ xtimeout = options_get_number(&s->options, "repeat-time");
+ if (xtimeout != 0 && bd->can_repeat) {
+ /* Now repeating in same keytable */
+ c->flags |= CLIENT_REPEAT;
+
+ tv.tv_sec = xtimeout / 1000;
+ tv.tv_usec = (xtimeout % 1000) * 1000L;
+ evtimer_del(&c->repeat_timer);
+ evtimer_add(&c->repeat_timer, &tv);
+ }
+ else {
+ /* "Stop" (or don't start) repeating */
+ c->flags &= ~CLIENT_REPEAT;
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ }
server_status_client(c);
+ key_bindings_dispatch(bd, c);
return;
}
- /* Try as a non-prefix key binding. */
- if (ispaste || (bd = key_bindings_lookup(key)) == NULL) {
- if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
- } else
- key_bindings_dispatch(bd, c);
- return;
- }
-
- /* Prefix key already pressed. Reset prefix and lookup key. */
- c->flags &= ~CLIENT_PREFIX;
- server_status_client(c);
- if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
- /* If repeating, treat this as a key, else ignore. */
if (c->flags & CLIENT_REPEAT) {
+ /* We missed, but we were in repeat, fall back to ROOT and try again */
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
+ server_status_client(c);
+ continue;
}
- return;
+
+ /* Actual miss */
+ break;
}
- /* If already repeating, but this key can't repeat, skip it. */
- if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
- c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
+ /* A miss in a non-ROOT keytable fails out to ROOT */
+ if (strcmp(c->keytablename, "ROOT")) {
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ server_status_client(c);
return;
}
- /* If this key can repeat, reset the repeat flags and timer. */
- xtimeout = options_get_number(&s->options, "repeat-time");
- if (xtimeout != 0 && bd->can_repeat) {
- c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
-
- tv.tv_sec = xtimeout / 1000;
- tv.tv_usec = (xtimeout % 1000) * 1000L;
- evtimer_del(&c->repeat_timer);
- evtimer_add(&c->repeat_timer, &tv);
+ /* A prefix miss in ROOT switched to PREFIX */
+ if (isprefix) {
+ /* Prefix key switches to PREFIX table */
+ free(c->keytablename);
+ c->keytablename = xstrdup("PREFIX");
+ server_status_client(c);
+ return;
}
- /* Dispatch the command. */
- key_bindings_dispatch(bd, c);
+ /* Anything else in ROOT is straight through */
+ if (!(c->flags & CLIENT_READONLY)) {
+ window_pane_key(wp, s, key);
+ }
}
/* Client functions that need to happen every loop. */
@@ -700,9 +717,10 @@ server_client_repeat_timer(unused int fd, unused short events, void *data)
struct client *c = data;
if (c->flags & CLIENT_REPEAT) {
- if (c->flags & CLIENT_PREFIX)
- server_status_client(c);
- c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
+ free(c->keytablename);
+ c->keytablename = xstrdup("ROOT");
+ c->flags &= ~CLIENT_REPEAT;
+ server_status_client(c);
}
}
diff --git a/server.c b/server.c
index d3ac0f8b0cdd..5ae44df23eff 100644
--- a/server.c
+++ b/server.c
@@ -209,7 +209,6 @@ server_loop(void)
server_window_loop();
server_client_loop();
- key_bindings_clean();
server_clean_dead();
}
}
diff --git a/tmux.1 b/tmux.1
index c05eacfdbc53..3a89e26c20d3 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1842,7 +1842,8 @@ Commands related to key bindings are as follows:
.Bl -tag -width Ds
.It Xo Ic bind-key
.Op Fl cnr
-.Op Fl t Ar key-table
+.Op Fl t Ar mode-key-table
+.Op Fl T Ar key-table
.Ar key Ar command Op Ar arguments
.Xc
.D1 (alias: Ic bind )
@@ -1871,13 +1872,28 @@ If
is present,
.Ar key
is bound in
-.Ar key-table :
+.Ar mode-key-table :
the binding for command mode with
.Fl c
or for normal mode without.
To view the default bindings and possible commands, see the
.Ic list-keys
command.
+.Pp
+If
+.Fl T
+is present,
+.Ar key
+is bound in
+.Ar key-table :
+.Em PREFIX
+corresponds to the default,
+.Em ROOT
+corresponds to
+.Fl n ,
+and custom values may be used with the
+.Ic set-keytable
+command.
.It Ic list-keys Op Fl t Ar key-table
.D1 (alias: Ic lsk )
List all key bindings.
@@ -1927,9 +1943,15 @@ flag causes the terminal state to be reset.
Send the prefix key, or with
.Fl 2
the secondary prefix key, to a window as if it was pressed.
+.It Xo Ic set-keytable
+.Ar key-table
+.Xc
+Set the client's key table. The next key from the client will be interpretted from
+.Ar key-table .
.It Xo Ic unbind-key
.Op Fl acn
-.Op Fl t Ar key-table
+.Op Fl t Ar mode-key-table
+.Op Fl T Ar key-table
.Ar key
.Xc
.D1 (alias: Ic unbind )
@@ -1951,10 +1973,26 @@ If
is present,
.Ar key
in
-.Ar key-table
+.Ar mode-key-table
is unbound: the binding for command mode with
.Fl c
or for normal mode without.
+.Pp
+If
+.Fl T
+is present,
+.Ar key
+in
+.Ar key-table
+is unbound:
+.Em PREFIX
+corresponds to the default,
+.Em ROOT
+corresponds to
+.Fl n ,
+and custom values may be used with the
+.Ic set-keytable
+command.
.El
.Sh OPTIONS
The appearance and behaviour of
diff --git a/tmux.h b/tmux.h
index fde94afc47b3..55463fcacce0 100644
--- a/tmux.h
+++ b/tmux.h
@@ -164,10 +164,9 @@ extern char **environ;
#define KEYC_ESCAPE 0x2000
#define KEYC_CTRL 0x4000
#define KEYC_SHIFT 0x8000
-#define KEYC_PREFIX 0x10000
/* Mask to obtain key w/o modifiers. */
-#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX)
+#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT)
#define KEYC_MASK_KEY (~KEYC_MASK_MOD)
/* Other key codes. */
@@ -1298,7 +1297,7 @@ struct client {
struct screen status;
#define CLIENT_TERMINAL 0x1
-#define CLIENT_PREFIX 0x2
+/* replaced by keytablename: #define CLIENT_PREFIX 0x2 */
#define CLIENT_EXIT 0x4
#define CLIENT_REDRAW 0x8
#define CLIENT_STATUS 0x10
@@ -1317,6 +1316,7 @@ struct client {
#define CLIENT_256COLOURS 0x20000
#define CLIENT_IDENTIFIED 0x40000
int flags;
+ char *keytablename;
struct event identify_timer;
@@ -1444,6 +1444,13 @@ struct key_binding {
RB_ENTRY(key_binding) entry;
};
RB_HEAD(key_bindings, key_binding);
+struct key_binding_map_entry {
+ const char *name;
+ struct key_bindings key_bindings;
+
+ RB_ENTRY(key_binding_map_entry) entry;
+};
+RB_HEAD(key_binding_map, key_binding_map_entry);
/*
* Option table entries. The option table is the user-visible part of the
@@ -1818,6 +1825,7 @@ extern const struct cmd_entry cmd_send_prefix_entry;
extern const struct cmd_entry cmd_server_info_entry;
extern const struct cmd_entry cmd_set_buffer_entry;
extern const struct cmd_entry cmd_set_environment_entry;
+extern const struct cmd_entry cmd_set_keytable_entry;
extern const struct cmd_entry cmd_set_option_entry;
extern const struct cmd_entry cmd_set_window_option_entry;
extern const struct cmd_entry cmd_show_buffer_entry;
@@ -1865,13 +1873,15 @@ int cmd_string_parse(const char *, struct cmd_list **, const char *,
int client_main(int, char **, int);
/* key-bindings.c */
-extern struct key_bindings key_bindings;
+extern struct key_binding_map key_binding_map;
+int key_binding_map_entry_cmp(struct key_binding_map_entry *, struct key_binding_map_entry *);
int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp);
-struct key_binding *key_bindings_lookup(int);
-void key_bindings_add(int, int, struct cmd_list *);
-void key_bindings_remove(int);
-void key_bindings_clean(void);
+RB_PROTOTYPE(key_binding_map, key_binding_map_entry, entry, key_binding_map_entry_cmp);
+struct key_bindings *key_bindings_lookup_table(const char *, int);
+struct key_binding *key_bindings_lookup(const char *, int);
+void key_bindings_add(const char *, int, int, struct cmd_list *);
+void key_bindings_remove(const char *, int);
void key_bindings_init(void);
void key_bindings_dispatch(struct key_binding *, struct client *);
--
1.9.1
1.9.1