Discussion:
[PATCH] Allow custom key tables for e.g. multiple keystroke bindings.
Keith Amling
2014-05-12 23:18:16 UTC
Permalink
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 *);
--
1.9.1
Nicholas Marriott
2014-05-13 01:04:42 UTC
Permalink
Thanks - I think this is the right idea, but not quite there.

There should be no need for a prefix key anymore. Instead, the default
root table should have a binding for C-b to change the key table.

That way the prefix table is not special, the only special table is the
root table.

(Although we'll need some compatibility goo to keep the prefix option
and send-prefix working.)

Also some specific comments on the diff:

- We should make set-keytable an argument to bind-key or (maybe better)
switch-client rather than a new command.

- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).

- I don't like the table names in uppercase, "root" and "prefix" would
be better.

- I think it is safe to remove dead_key_bindings now but please do it as
a separate diff. Likewise I would pull the mode-key renaming bits out
separately.

- You will save me a little time if you use "for (;;)" instead of "while
(1)".
Post by Keith Amling
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$ */
+
+/*
+ *
+ * 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
.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
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
+.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
+.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
------------------------------------------------------------------------------
"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
https://lists.sourceforge.net/lists/listinfo/tmux-users
Nicholas Marriott
2014-05-13 07:27:10 UTC
Permalink
Post by Nicholas Marriott
There should be no need for a prefix key anymore. Instead, the default
root table should have a binding for C-b to change the key table.
That way the prefix table is not special, the only special table is the
root table.
(Although we'll need some compatibility goo to keep the prefix option
and send-prefix working.)
I had considered that but turned it down to the [in my opinion]
excessive amount of goo that would be required to deal with it. In such
a world there isn't a prefix key, there aren't two prefix keys (there's
apparently already a "prefix2"!), there's just whatever the heck you
decided to bind or unbind. For what it's worth I think such a world is
strictly superior architecturally but I just don't see it net worth it
with the terrible backwards compatibility mess included.
This is by far to me the most unclear concern in terms of what to do
about it. I just don't see a way to translate an arbitrary collection
of key bindings in to a legacy "prefix[2]" option view. Are we to
special case a read of the "prefix" option to return the first root key
binding for "set-keytable prefix" it can find (and "prefix2" to return
the 2nd)? Presumably setting "prefix" will add the binding and delete
the old value's binding (as long as "prefix2" isn't set to the same)?
I'm just not seeing it.
I don't think it'd be that much compatibility code.

We could just maintain a flag for the "option-set" prefix in the root
table, and that can be used for prefix/send-prefix. The root table can
never be deleted so that isn't an issue.

prefix2 can go away once there is an alternative.

Still, if you want to make the other changes first I will look again and
we can decide on this later.
Post by Nicholas Marriott
- We should make set-keytable an argument to bind-key or (maybe better)
switch-client rather than a new command.
I'm not sure I personally agree. This seems like heading in the
direction of git's "one command does fifteen barely-related things".
That said it is your project and I'm happy to hack it that way if that's
your final call.
Yes, we have too many commands and options already. I'd make it
switch-client -T.
Post by Nicholas Marriott
- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).
I agree xstrdup/free all over the place is a mess but keeping a pointer
to an actual table seems like an accident waiting to happen in terms of
safety.
You could say this about any pointer.
Additionally it provides weirdness if e.g. a user sets a
keytable, then unbinds the last key in the keytable and rebinds it
(causing the client to point to a stale empty keytable).
I don't see that this will be able to happen - when the user unbinds the
last key the table needs to be destroyed, at that point the client
should be fixed up (or the table destroy deferred until the client is
finished with it).
Maybe move
tablename getting/setting into functions to abstract it away? Then we
can even do something fancy like have canonical non-managed copies of
the "ROOT" and "PREFIX" strings.
Yes if you want to try this first instead I can take a look.
Post by Nicholas Marriott
- I don't like the table names in uppercase, "root" and "prefix" would
be better.
As you wish. While you're at it if you want them named something
different entirely that would be reasonable, I pretty much just picked
the two strings off the cuff.
I like the names, but I think lowercase is better.
Post by Nicholas Marriott
- I think it is safe to remove dead_key_bindings now but please do it as
a separate diff. Likewise I would pull the mode-key renaming bits out
separately.
Oh, yeah. I had originally made the first a separate change on my local
branch but then just smooshed it all to send without thinking about it.
I'm also curious what the history on that is, it wasn't clear to me why
that was there in the first place.
Before cmdlists were reference counted, things like "bind x bind x lsk"
would cause a use-after-free.
Post by Nicholas Marriott
- You will save me a little time if you use "for (;;)" instead of "while
(1)".
Uh, as you wish although I'm not sure I understand the subtext. Just
for my curiosity is it just that you prefer "for (;;)"?
Yes, if you leave it as "while (1)" I'll have to go and change it before
I apply the diff.
Keith
Post by Nicholas Marriott
Date: Tue, 13 May 2014 02:04:42 +0100
Subject: Re: [PATCH] Allow custom key tables for e.g. multiple keystroke
bindings.
Thanks - I think this is the right idea, but not quite there.
There should be no need for a prefix key anymore. Instead, the default
root table should have a binding for C-b to change the key table.
That way the prefix table is not special, the only special table is the
root table.
(Although we'll need some compatibility goo to keep the prefix option
and send-prefix working.)
- We should make set-keytable an argument to bind-key or (maybe better)
switch-client rather than a new command.
- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).
- I don't like the table names in uppercase, "root" and "prefix" would
be better.
- I think it is safe to remove dead_key_bindings now but please do it as
a separate diff. Likewise I would pull the mode-key renaming bits out
separately.
- You will save me a little time if you use "for (;;)" instead of "while
(1)".
Post by Keith Amling
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$ */
+
+/*
+ *
+ * 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
.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
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
+.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
+.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
------------------------------------------------------------------------------
"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."
https://urldefense.proofpoint.com/v1/url?u=http://p.sf.net/sfu/SauceLabs&k=fDZpZZQMmYwf27OU23GmAQ%3D%3D%0A&r=BTdtcPIZXD8V7r6BhVE1Cy1S1ITG2lF8LZPYHbBpv%2B0%3D%0A&m=bi1uWxNPLiV%2BM52G7zv4qsFMczpysaL4AG0%2B1C7o1%2BA%3D%0A&s=c4a65022a4d6b8349d946735d01b625ad7de4b11b6efb75ba34521c9be5196a7
_______________________________________________
tmux-users mailing list
https://urldefense.proofpoint.com/v1/url?u=https://lists.sourceforge.net/lists/listinfo/tmux-users&k=fDZpZZQMmYwf27OU23GmAQ%3D%3D%0A&r=BTdtcPIZXD8V7r6BhVE1Cy1S1ITG2lF8LZPYHbBpv%2B0%3D%0A&m=bi1uWxNPLiV%2BM52G7zv4qsFMczpysaL4AG0%2B1C7o1%2BA%3D%0A&s=27481cdf90a90d18d51c90ade176c49c5f38f3831394fe992fd0a0ad99b81223
Nicholas Marriott
2014-05-13 22:28:05 UTC
Permalink
Hi
The various things I didn't quote are all straight-forward and will be
my pleasure to fix.
Post by Nicholas Marriott
I don't think it'd be that much compatibility code.
We could just maintain a flag for the "option-set" prefix in the root
table, and that can be used for prefix/send-prefix. The root table can
never be deleted so that isn't an issue.
prefix2 can go away once there is an alternative.
Still, if you want to make the other changes first I will look again and
we can decide on this later.
Sorry, I'm obviously falling behind here; I just don't follow what
you're proposing with enough clarity to implement it. Are you
suggesting another KEYC_* flag (or two?) for "this is the binding set by
`set-option prefix[2]`"? Are you proposing an extra field (or two?) in
key_bindings to hold the prefix in the root table? I don't mean to be a
drag and I'd rather do what I can to minimize your work explaining your
vision to chumps and/or having to clean up our crappy patches but I'm
just not following this one.
I was suggesting an extra flag in the struct key_binding to mark the
current "option-set" prefix in the root table. Basically when you change
the option, it'll find the one with the flag and move it.

It's just an idea - thinking again it may be much the same just to leave
prefix as you have it, or to do something similar.

So long as I can add new prefixes by binding keys in the root table to
"switchc -Tprefix", it may be much the same to check the prefix option
too (so it is an implicit, invisible binding to "switchc -Tprefix").

In any case leave it as it is for the next diff and I'll look at it
again then.
I'll clean the patch up for the other stuff first anyway.
Post by Nicholas Marriott
Post by Nicholas Marriott
- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).
I agree xstrdup/free all over the place is a mess but keeping a pointer
to an actual table seems like an accident waiting to happen in terms of
safety.
You could say this about any pointer.
Additionally it provides weirdness if e.g. a user sets a
keytable, then unbinds the last key in the keytable and rebinds it
(causing the client to point to a stale empty keytable).
I don't see that this will be able to happen - when the user unbinds the
last key the table needs to be destroyed, at that point the client
should be fixed up (or the table destroy deferred until the client is
finished with it).
Well, I agree it needs to be destroyed eventually but the issue is that
if a user thinks of the client as being set to [read from] a keytable
with a name then they will likely think binding a key into a keytable of
that name will be the same table even if the bind happens after the set.
This will still work if the key table is a pointer, both the bind and
the set will use the same key table.

Or are you thinking of creating the table after running the set command?
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
My opinion about safety is completely countered by ref counting (it was
the final unbind's deletion having to know about and clean up
outstanding references from clients that was worrying me) and the
weirdness with stale keytables of duplicate names isn't that big of a
deal for the sanity we're gonna save in code.
They don't need to know about the specific references from clients, they
can check every client in the clients list. All clients will be in the
list and nobody will have more than at most a few hundred so walking
them all would be fine.

Or reference counting the tables would work too if you prefer.
Keith
Nicholas Marriott
2014-05-13 22:53:02 UTC
Permalink
Post by Nicholas Marriott
Well, I agree it needs to be destroyed eventually but the issue is that
if a user thinks of the client as being set to [read from] a keytable
with a name then they will likely think binding a key into a keytable of
that name will be the same table even if the bind happens after the set.
This will still work if the key table is a pointer, both the bind and
the set will use the same key table.
Or are you thinking of creating the table after running the set command?
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
I guess I don't super care what happens since I won't personally be
writing any switch-client -T into an empty table but I had assumed we
would allow switching to a new [and ephemeral] table since otherwise
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.

I can't think of a case where I would want it to automatically create a
table, except to confuse people who make a typo in the table name. Do
you have a use in mind?
In many ways it is a different view of things. I was thinking of key
bindings as now indexed by the table and the key rather than just the
key whereas the direction this patch is heading is that the table itself
as a map is an exposed part of the model rather than an implementation
detail.
Yes I think it should be exposed, I think "key tables containing key
bindings" is easier to understand than thinking of it as a sort of tag
on the key, and it has the same effect really.
That said, reiterate your commitment once more to failing switch-client
-T into an empty table and I'm happy to do it.
Post by Nicholas Marriott
My opinion about safety is completely countered by ref counting (it was
the final unbind's deletion having to know about and clean up
outstanding references from clients that was worrying me) and the
weirdness with stale keytables of duplicate names isn't that big of a
deal for the sanity we're gonna save in code.
They don't need to know about the specific references from clients, they
can check every client in the clients list. All clients will be in the
list and nobody will have more than at most a few hundred so walking
them all would be fine.
Or reference counting the tables would work too if you prefer.
Reference counting it is for sure. Performance wasn't really my
concern, the spooky ownership-at-a-distance was. It just seems fragile
for clients to hold a reference they don't really own and have the
actual owner (key-bindings.c) have to be aware of these extra
references. With reference counting it's entirely moot though.
Sure, reference counting the tables is fine.

Thanks
Keith Amling
2014-05-13 23:22:09 UTC
Permalink
Post by Nicholas Marriott
Post by Nicholas Marriott
Or are you thinking of creating the table after running the set command?
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
I guess I don't super care what happens since I won't personally be
writing any switch-client -T into an empty table but I had assumed we
would allow switching to a new [and ephemeral] table since otherwise
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.
I can't think of a case where I would want it to automatically create a
table, except to confuse people who make a typo in the table name. Do
you have a use in mind?
Not specifically, I just prefer to see tools degrade what might be
described as "uniformly" where possible. Nonuniform behaviour makes
tools dangerous for automated use because when writing a script you're
unlikely to think of things like "what if this table is empty" or "what
if this command barfs on no args when really no args means I just didn't
need it to do anything".

I'll switch it and the next set of patches will be up shortly.

Keith
Ross Hadden
2015-04-13 15:21:52 UTC
Permalink
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
Or are you thinking of creating the table after running the set command?
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
I guess I don't super care what happens since I won't personally be
writing any switch-client -T into an empty table but I had assumed we
would allow switching to a new [and ephemeral] table since otherwise
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.
I can't think of a case where I would want it to automatically create a
table, except to confuse people who make a typo in the table name. Do
you have a use in mind?
Not specifically, I just prefer to see tools degrade what might be
described as "uniformly" where possible. Nonuniform behaviour makes
tools dangerous for automated use because when writing a script you're
unlikely to think of things like "what if this table is empty" or "what
if this command barfs on no args when really no args means I just didn't
need it to do anything".
I'll switch it and the next set of patches will be up shortly.
Keith
--------------------------------------------------------------------------
----
Post by Keith Amling
"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
Has work on this continued? I made an issue about it
(https://sourceforge.net/p/tmux/tickets/172/) in an effort to bring
attention to this patch, but Thomas said it's pretty much not going anywhere
unless the original patch author continues or makes an effort to get it
merged himself.

I would love to see this landed in tmux! My current workaround can be seen
at https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
and https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
cords.sh.
Nicholas Marriott
2015-04-13 21:30:43 UTC
Permalink
Here's the latest diff.

It actually seems to work fine, although I haven't done a lot of testing.

IIRC I wasn't wild about the cmd-list-keys.c and cmd-bind-key.c changes;
certainly lsk -T should error on an unknown table, same as bind -T. I
think the manpage bits could do with some improvement too.


Index: cmd-bind-key.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-bind-key.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-bind-key.c
--- cmd-bind-key.c 10 Apr 2015 16:00:08 -0000 1.20
+++ cmd-bind-key.c 13 Apr 2015 21:29:21 -0000
@@ -33,8 +33,8 @@ enum cmd_retval cmd_bind_key_mode_table

const struct cmd_entry cmd_bind_key_entry = {
"bind-key", "bind",
- "cnrt:", 1, -1,
- "[-cnr] [-t mode-table] key command [arguments]",
+ "cnrt:T:", 1, -1,
+ "[-cnr] [-t mode-table] [-T key-table] key command [arguments]",
0,
cmd_bind_key_exec
};
@@ -46,6 +46,7 @@ cmd_bind_key_exec(struct cmd *self, stru
char *cause;
struct cmd_list *cmdlist;
int key;
+ const char *tablename;

if (args_has(args, 't')) {
if (args->argc != 2 && args->argc != 3) {
@@ -68,6 +69,13 @@ cmd_bind_key_exec(struct cmd *self, stru
if (args_has(args, 't'))
return (cmd_bind_key_mode_table(self, cmdq, key));

+ if (args_has(args, 'T'))
+ tablename = args_get(args, 'T');
+ else if (args_has(args, 'n'))
+ tablename = "root";
+ else
+ tablename = "prefix";
+
cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
&cause);
if (cmdlist == NULL) {
@@ -76,9 +84,7 @@ cmd_bind_key_exec(struct cmd *self, stru
return (CMD_RETURN_ERROR);
}

- if (!args_has(args, 'n'))
- key |= KEYC_PREFIX;
- key_bindings_add(key, args_has(args, 'r'), cmdlist);
+ key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist);
return (CMD_RETURN_NORMAL);
}

Index: cmd-list-keys.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-list-keys.c,v
retrieving revision 1.25
diff -u -p -r1.25 cmd-list-keys.c
--- cmd-list-keys.c 20 Oct 2014 23:27:14 -0000 1.25
+++ cmd-list-keys.c 13 Apr 2015 21:29:21 -0000
@@ -33,8 +33,8 @@ enum cmd_retval cmd_list_keys_commands(

const struct cmd_entry cmd_list_keys_entry = {
"list-keys", "lsk",
- "t:", 0, 0,
- "[-t key-table]",
+ "t:T:", 0, 0,
+ "[-t mode-table] [-T key-table]",
0,
cmd_list_keys_exec
};
@@ -51,58 +51,65 @@ enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
+ struct key_table *table;
struct key_binding *bd;
- const char *key;
- char tmp[BUFSIZ], flags[8];
+ const char *key, *tablename, *r;
+ char tmp[BUFSIZ];
size_t used;
- int width, keywidth;
+ int repeat, width, tablewidth, keywidth;

if (self->entry == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, cmdq));

if (args_has(args, 't'))
return (cmd_list_keys_table(self, cmdq));
+ tablename = args_get(args, 'T');

- width = 0;
-
- RB_FOREACH(bd, key_bindings, &key_bindings) {
- key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
- if (key == NULL)
+ repeat = 0;
+ tablewidth = keywidth = 0;
+ RB_FOREACH(table, key_tables, &key_tables) {
+ if (tablename != NULL && strcmp(table->name, tablename) != 0)
continue;
+ RB_FOREACH(bd, key_bindings, &(table->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;
+ repeat = 1;
+
+ width = strlen(table->name);
+ if (width > tablewidth)
+ tablewidth =width;
+ width = strlen(key);
+ if (width > keywidth)
+ keywidth = width;
+ }
}

- RB_FOREACH(bd, key_bindings, &key_bindings) {
- key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
- if (key == NULL)
+ RB_FOREACH(table, key_tables, &key_tables) {
+ if (tablename != NULL && strcmp(table->name, tablename) != 0)
continue;
-
- *flags = '\0';
- if (!(bd->key & KEYC_PREFIX)) {
- if (bd->can_repeat)
- xsnprintf(flags, sizeof flags, "-rn ");
+ RB_FOREACH(bd, key_bindings, &(table->key_bindings)) {
+ key = key_string_lookup_key(bd->key);
+ if (key == NULL)
+ continue;
+
+ if (!repeat)
+ r = "";
+ else if (bd->can_repeat)
+ r = "-r ";
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;
+ r = " ";
+ used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r,
+ (int)tablewidth, table->name, (int)keywidth, key);
+ if (used < sizeof tmp) {
+ cmd_list_print(bd->cmdlist, tmp + used,
+ (sizeof tmp) - used);
+ }

- cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
- cmdq_print(cmdq, "bind-key %s", tmp);
+ cmdq_print(cmdq, "bind-key %s", tmp);
+ }
}

return (CMD_RETURN_NORMAL);
Index: cmd-switch-client.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-switch-client.c,v
retrieving revision 1.22
diff -u -p -r1.22 cmd-switch-client.c
--- cmd-switch-client.c 20 Oct 2014 22:29:25 -0000 1.22
+++ cmd-switch-client.c 13 Apr 2015 21:29:21 -0000
@@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec(

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_exec
};
@@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self,
struct winlink *wl = NULL;
struct window *w = NULL;
struct window_pane *wp = NULL;
- const char *tflag;
+ const char *tflag, *tablename;
+ struct key_table *table;

if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
@@ -56,6 +57,18 @@ cmd_switch_client_exec(struct cmd *self,
c->flags &= ~CLIENT_READONLY;
else
c->flags |= CLIENT_READONLY;
+ }
+
+ tablename = args_get(args, 'T');
+ if (tablename != NULL) {
+ table = key_bindings_get_table(tablename, 0);
+ if (table == NULL) {
+ cmdq_error(cmdq, "table %s doesn't exist", tablename);
+ return (CMD_RETURN_ERROR);
+ }
+ table->references++;
+ key_bindings_unref_table(c->keytable);
+ c->keytable = table;
}

tflag = args_get(args, 't');
Index: cmd-unbind-key.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-unbind-key.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-unbind-key.c
--- cmd-unbind-key.c 20 Oct 2014 22:29:25 -0000 1.20
+++ cmd-unbind-key.c 13 Apr 2015 21:29:21 -0000
@@ -31,8 +31,8 @@ enum cmd_retval cmd_unbind_key_mode_tab

const struct cmd_entry cmd_unbind_key_entry = {
"unbind-key", "unbind",
- "acnt:", 0, 1,
- "[-acn] [-t mode-table] key",
+ "acnt:T:", 0, 1,
+ "[-acn] [-t mode-table] [-T key-table] key",
0,
cmd_unbind_key_exec
};
@@ -40,9 +40,9 @@ const struct cmd_entry cmd_unbind_key_en
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;
+ struct args *args = self->args;
+ int key;
+ const char *tablename;

if (!args_has(args, 'a')) {
if (args->argc != 1) {
@@ -66,16 +66,23 @@ cmd_unbind_key_exec(struct cmd *self, st
return (cmd_unbind_key_mode_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'))
+ tablename = args_get(args, 'T');
+ else if (args_has(args, 'n'))
+ tablename = "root";
+ else
+ tablename = "prefix";
+
+ key_bindings_remove(tablename, key);
return (CMD_RETURN_NORMAL);
}

Index: format.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/format.c,v
retrieving revision 1.60
diff -u -p -r1.60 format.c
--- format.c 31 Mar 2015 17:58:36 -0000 1.60
+++ format.c 13 Apr 2015 21:29:21 -0000
@@ -545,7 +545,11 @@ format_defaults_client(struct format_tre
format_add(ft, "client_activity", "%lld", (long long) t);
format_add(ft, "client_activity_string", "%s", format_time_string(t));

- format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
+ if (strcmp(c->keytable->name, "root") == 0)
+ format_add(ft, "client_prefix", "%d", 0);
+ else
+ format_add(ft, "client_prefix", "%d", 1);
+ format_add(ft, "client_key_table", "%s", c->keytable->name);

if (c->tty.flags & TTY_UTF8)
format_add(ft, "client_utf8", "%d", 1);
Index: key-bindings.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/key-bindings.c,v
retrieving revision 1.43
diff -u -p -r1.43 key-bindings.c
--- key-bindings.c 22 Oct 2014 23:18:53 -0000 1.43
+++ key-bindings.c 13 Apr 2015 21:29:21 -0000
@@ -25,60 +25,121 @@
#include "tmux.h"

RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+RB_GENERATE(key_tables, key_table, entry, key_table_cmp);
+struct key_tables key_tables = RB_INITIALIZER(&key_tables);

-struct key_bindings key_bindings;
+int
+key_table_cmp(struct key_table *e1, struct key_table *e2)
+{
+ return (strcmp(e1->name, e2->name));
+}

int
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
{
- int key1, key2;
+ return (bd1->key - bd2->key);
+}
+
+struct key_table *
+key_bindings_get_table(const char *name, int create)
+{
+ struct key_table table_search, *table;
+
+ table_search.name = name;
+ table = RB_FIND(key_tables, &key_tables, &table_search);
+ if (table != NULL || !create)
+ return (table);

- key1 = bd1->key & ~KEYC_PREFIX;
- key2 = bd2->key & ~KEYC_PREFIX;
- if (key1 != key2)
- return (key1 - key2);
+ table = xmalloc(sizeof *table);
+ table->name = xstrdup(name);
+ RB_INIT(&table->key_bindings);

- if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
- return (-1);
- if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
- return (1);
- return (0);
+ table->references = 1; /* one reference in key_tables */
+ RB_INSERT(key_tables, &key_tables, table);
+
+ return (table);
}

-struct key_binding *
-key_bindings_lookup(int key)
+void
+key_bindings_unref_table(struct key_table *table)
{
- struct key_binding bd;
+ struct key_binding *bd;

- bd.key = key;
- return (RB_FIND(key_bindings, &key_bindings, &bd));
+ if (--table->references != 0)
+ return;
+
+ 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((void *)table->name);
+ free(table);
}

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_binding *bd;
+ struct key_table *table;
+ struct key_binding bd_search, *bd;

- key_bindings_remove(key);
+ table = key_bindings_get_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;
}

void
-key_bindings_remove(int key)
+key_bindings_remove(const char *name, int key)
{
- struct key_binding *bd;
+ struct key_table *table;
+ struct key_binding bd_search, *bd;
+
+ table = key_bindings_get_table(name, 0);
+ if (table == NULL)
+ return;

- if ((bd = key_bindings_lookup(key)) == NULL)
+ bd_search.key = key;
+ bd = RB_FIND(key_bindings, &table->key_bindings, &bd_search);
+ if (bd == NULL)
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)) {
+ RB_REMOVE(key_tables, &key_tables, table);
+ key_bindings_unref_table(table);
+ }
+}
+
+void
+key_bindings_remove_table(const char *name)
+{
+ struct key_table *table;
+
+ table = key_bindings_get_table(name, 0);
+ if (table == NULL)
+ return;
+
+ RB_REMOVE(key_tables, &key_tables, table);
+ key_bindings_unref_table(table);
}

void
@@ -164,8 +225,6 @@ key_bindings_init(void)
char* cause;
int error;
struct cmd_q *cmdq;
-
- RB_INIT(&key_bindings);

cmdq = cmdq_new(NULL);
for (i = 0; i < nitems(defaults); i++) {
Index: server-client.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/server-client.c,v
retrieving revision 1.129
diff -u -p -r1.129 server-client.c
--- server-client.c 31 Mar 2015 17:45:10 -0000 1.129
+++ server-client.c 13 Apr 2015 21:29:21 -0000
@@ -100,6 +100,9 @@ server_client_create(int fd)

c->flags |= CLIENT_FOCUSED;

+ c->keytable = key_bindings_get_table("root", 1);
+ c->keytable->references++;
+
evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);

for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
@@ -171,6 +174,8 @@ server_client_lost(struct client *c)

evtimer_del(&c->repeat_timer);

+ key_bindings_unref_table(c->keytable);
+
if (event_initialized(&c->identify_timer))
evtimer_del(&c->identify_timer);

@@ -362,33 +367,28 @@ server_client_assume_paste(struct sessio
void
server_client_handle_key(struct client *c, int key)
{
- struct session *s;
+ struct session *s = c->session;
struct window *w;
struct window_pane *wp;
struct timeval tv;
- struct key_binding *bd;
- int xtimeout, isprefix, ispaste;
+ struct key_table *table = c->keytable;
+ struct key_binding bd_search, *bd;
+ int xtimeout;

/* Check the client is good to accept input. */
- if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
- return;
-
- if (c->session == NULL)
+ if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
return;
- s = c->session;
+ w = c->session->curw->window;
+ wp = w->active;

/* Update the activity timer. */
if (gettimeofday(&c->activity_time, NULL) != 0)
fatal("gettimeofday failed");
-
memcpy(&s->last_activity_time, &s->activity_time,
sizeof s->last_activity_time);
memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);

- w = c->session->curw->window;
- wp = w->active;
-
- /* Special case: number keys jump to pane in identify mode. */
+ /* Number keys jump to pane in identify mode. */
if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
if (c->flags & CLIENT_READONLY)
return;
@@ -419,74 +419,89 @@ server_client_handle_key(struct client *
return;
}

- /* Is this a prefix key? */
- if (key == options_get_number(&s->options, "prefix"))
- isprefix = 1;
- else if (key == options_get_number(&s->options, "prefix2"))
- isprefix = 1;
- else
- isprefix = 0;
+ /* Treat everything as a regular key when pasting is detected. */
+ if (server_client_assume_paste(s)) {
+ if (!(c->flags & CLIENT_READONLY))
+ window_pane_key(wp, s, key);
+ return;
+ }

- /* Treat prefix as a regular key when pasting is detected. */
- ispaste = server_client_assume_paste(s);
- if (ispaste)
- isprefix = 0;
-
- /* No previous prefix key. */
- if (!(c->flags & CLIENT_PREFIX)) {
- if (isprefix) {
- c->flags |= CLIENT_PREFIX;
+retry:
+ /* Try to see if there is a key binding in the current table. */
+ bd_search.key = key;
+ bd = RB_FIND(key_bindings, &table->key_bindings, &bd_search);
+ if (bd != NULL) {
+ /*
+ * Key was matched in this table. If currently repeating but
+ * a non-repeating binding was found, stop repeating and try
+ * again in the root table.
+ */
+ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) {
+ server_set_key_table(c, "root");
+ c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
- return;
+ goto retry;
}

- /* 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) {
+ /*
+ * Take a reference to this table to make sure the key binding
+ * doesn't disappear.
+ */
+ table->references++;
+
+ /*
+ * If this is a repeating key, start the timer. Otherwise reset
+ * the client back to the root table.
+ */
+ xtimeout = options_get_number(&s->options, "repeat-time");
+ if (xtimeout != 0 && bd->can_repeat) {
+ 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 {
c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
+ server_set_key_table(c, "root");
}
+ server_status_client(c);
+
+ /* Dispatch the key binding. */
+ key_bindings_dispatch(bd, c);
+ key_bindings_unref_table(table);
+
return;
}

- /* If already repeating, but this key can't repeat, skip it. */
- if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
+ /*
+ * No match in this table. If repeating, switch the client back to the
+ * root table and try again.
+ */
+ if (c->flags & CLIENT_REPEAT) {
+ server_set_key_table(c, "root");
c->flags &= ~CLIENT_REPEAT;
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else if (!(c->flags & CLIENT_READONLY))
- window_pane_key(wp, s, key);
- return;
+ server_status_client(c);
+ goto retry;
}

- /* 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);
+ /* If no match and we're not in the root table, that's it. */
+ if (strcmp(c->keytable->name, "root") != 0) {
+ server_set_key_table(c, "root");
+ server_status_client(c);
+ return;
}

- /* Dispatch the command. */
- key_bindings_dispatch(bd, c);
+ /*
+ * No match, but in the root table. Prefix switches to the prefix table
+ * and everything else is passed through.
+ */
+ if (key == options_get_number(&s->options, "prefix") ||
+ key == options_get_number(&s->options, "prefix2")) {
+ server_set_key_table(c, "prefix");
+ server_status_client(c);
+ } else if (!(c->flags & CLIENT_READONLY))
+ window_pane_key(wp, s, key);
}

/* Client functions that need to happen every loop. */
@@ -692,9 +707,9 @@ server_client_repeat_timer(unused int fd
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_set_key_table(c, "root");
+ c->flags &= ~CLIENT_REPEAT;
+ server_status_client(c);
}
}

Index: server-fn.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/server-fn.c,v
retrieving revision 1.79
diff -u -p -r1.79 server-fn.c
--- server-fn.c 31 Mar 2015 17:45:10 -0000 1.79
+++ server-fn.c 13 Apr 2015 21:29:22 -0000
@@ -101,6 +101,14 @@ server_status_client(struct client *c)
}

void
+server_set_key_table(struct client *c, const char *name)
+{
+ key_bindings_unref_table(c->keytable);
+ c->keytable = key_bindings_get_table(name, 1);
+ c->keytable->references++;
+}
+
+void
server_redraw_session(struct session *s)
{
struct client *c;
Index: tmux.1
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.1,v
retrieving revision 1.417
diff -u -p -r1.417 tmux.1
--- tmux.1 10 Apr 2015 16:00:08 -0000 1.417
+++ tmux.1 13 Apr 2015 21:29:23 -0000
@@ -838,6 +838,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
@@ -855,6 +856,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 interpreted from
+.Ar key-table .
.El
.Sh WINDOWS AND PANES
A
@@ -1905,6 +1909,7 @@ Commands related to key bindings are as
.It Xo Ic bind-key
.Op Fl cnr
.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
.Ar key Ar command Op Ar arguments
.Xc
.D1 (alias: Ic bind )
@@ -1940,18 +1945,41 @@ or for normal mode without.
To view the default bindings and possible commands, see the
.Ic list-keys
command.
-.It Ic list-keys Op Fl t Ar key-table
+.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 Xo Ic list-keys
+.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
+.Xc
.D1 (alias: Ic lsk )
List all key bindings.
Without
.Fl t
-the primary key bindings - those executed when preceded by the prefix key -
-are printed.
+or
+.Fl T
+all key tables are printed.
+With
+.Fl T
+only
+.Ar key-table .
.Pp
With
.Fl t ,
the key bindings in
-.Ar key-table
+.Ar mode-table
are listed; this may be one of:
.Em vi-edit ,
.Em emacs-edit ,
@@ -1992,6 +2020,7 @@ the secondary prefix key, to a window as
.It Xo Ic unbind-key
.Op Fl acn
.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
.Ar key
.Xc
.D1 (alias: Ic unbind )
@@ -2017,6 +2046,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
Index: tmux.h
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.h,v
retrieving revision 1.490
diff -u -p -r1.490 tmux.h
--- tmux.h 6 Feb 2015 17:21:08 -0000 1.490
+++ tmux.h 13 Apr 2015 21:29:23 -0000
@@ -89,10 +89,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. */
@@ -1287,7 +1286,7 @@ struct client {
struct screen status;

#define CLIENT_TERMINAL 0x1
-#define CLIENT_PREFIX 0x2
+/* 0x2 unused */
#define CLIENT_EXIT 0x4
#define CLIENT_REDRAW 0x8
#define CLIENT_STATUS 0x10
@@ -1306,6 +1305,7 @@ struct client {
#define CLIENT_256COLOURS 0x20000
#define CLIENT_IDENTIFIED 0x40000
int flags;
+ struct key_table *keytable;

struct event identify_timer;

@@ -1423,15 +1423,24 @@ struct cmd_entry {
enum cmd_retval (*exec)(struct cmd *, struct cmd_q *);
};

-/* Key binding. */
+/* Key binding and key table. */
struct key_binding {
- int key;
- struct cmd_list *cmdlist;
- int can_repeat;
+ int key;
+ struct cmd_list *cmdlist;
+ int can_repeat;

- RB_ENTRY(key_binding) entry;
+ RB_ENTRY(key_binding) entry;
};
RB_HEAD(key_bindings, key_binding);
+struct key_table {
+ const char *name;
+ struct key_bindings key_bindings;
+
+ u_int references;
+
+ RB_ENTRY(key_table) entry;
+};
+RB_HEAD(key_tables, key_table);

/*
* Option table entries. The option table is the user-visible part of the
@@ -1848,12 +1857,16 @@ void cmd_wait_for_flush(void);
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_tables, key_table, entry, key_table_cmp);
+extern struct key_tables key_tables;
+int key_table_cmp(struct key_table *, struct key_table *);
+int key_bindings_cmp(struct key_binding *, struct key_binding *);
+struct key_table *key_bindings_get_table(const char *, int);
+void key_bindings_unref_table(struct key_table *);
+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 *);

@@ -1889,6 +1902,7 @@ void server_write_session(struct sessio
size_t);
void server_redraw_client(struct client *);
void server_status_client(struct client *);
+void server_set_key_table(struct client *, const char *);
void server_redraw_session(struct session *);
void server_redraw_session_group(struct session *);
void server_status_session(struct session *);
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
Or are you thinking of creating the table after running the set
command?
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
I guess I don't super care what happens since I won't personally be
writing any switch-client -T into an empty table but I had assumed we
would allow switching to a new [and ephemeral] table since otherwise
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.
I can't think of a case where I would want it to automatically create a
table, except to confuse people who make a typo in the table name. Do
you have a use in mind?
Not specifically, I just prefer to see tools degrade what might be
described as "uniformly" where possible. Nonuniform behaviour makes
tools dangerous for automated use because when writing a script you're
unlikely to think of things like "what if this table is empty" or "what
if this command barfs on no args when really no args means I just didn't
need it to do anything".
I'll switch it and the next set of patches will be up shortly.
Keith
--------------------------------------------------------------------------
----
Post by Keith Amling
"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
Post by Keith Amling
Simple to use. Nothing to install. Get started now for free."
http://p.sf.net/sfu/SauceLabs
Has work on this continued? I made an issue about it
(https://sourceforge.net/p/tmux/tickets/172/) in an effort to bring
attention to this patch, but Thomas said it's pretty much not going anywhere
unless the original patch author continues or makes an effort to get it
merged himself.
I would love to see this landed in tmux! My current workaround can be seen
at https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
and https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
cords.sh.
------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
tmux-users mailing list
https://lists.sourceforge.net/lists/listinfo/tmux-users
Nicholas Marriott
2015-04-19 21:45:38 UTC
Permalink
We don't need a merge request, I'll commit it when I'm happy with it. Thanks
Would you mind making a merge request for it?** I could, but it wouldn't
really be right to make a merge request with someone else's hard work!**
Your name should be on this.
Thanks,
~Ross
On Mon, Apr 13, 2015 at 5:30 PM Nicholas Marriott
Here's the latest diff.
It actually seems to work fine, although I haven't done a lot of testing.
IIRC I wasn't wild about the cmd-list-keys.c and cmd-bind-key.c changes;
certainly lsk -T should error on an unknown table, same as bind -T. I
think the manpage bits could do with some improvement too.
Index: cmd-bind-key.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-bind-key.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-bind-key.c
--- cmd-bind-key.c** ** ** 10 Apr 2015 16:00:08 -0000** ** ** 1.20
+++ cmd-bind-key.c** ** ** 13 Apr 2015 21:29:21 -0000
@@ -33,8 +33,8 @@ enum cmd_retval** ** ** ** cmd_bind_key_mode_table
**const struct cmd_entry cmd_bind_key_entry = {
** ** ** ** "bind-key", "bind",
-** ** ** **"cnrt:", 1, -1,
-** ** ** **"[-cnr] [-t mode-table] key command [arguments]",
+** ** ** **"cnrt:T:", 1, -1,
+** ** ** **"[-cnr] [-t mode-table] [-T key-table] key command
[arguments]",
** ** ** ** 0,
** ** ** ** cmd_bind_key_exec
**};
@@ -46,6 +46,7 @@ cmd_bind_key_exec(struct cmd *self, stru
** ** ** ** char** ** ** ** ** ** *cause;
** ** ** ** struct cmd_list *cmdlist;
** ** ** ** int** ** ** ** ** ** ** key;
+** ** ** **const char** ** ** *tablename;
** ** ** ** if (args_has(args, 't')) {
** ** ** ** ** ** ** ** if (args->argc != 2 && args->argc != 3) {
@@ -68,6 +69,13 @@ cmd_bind_key_exec(struct cmd *self, stru
** ** ** ** if (args_has(args, 't'))
** ** ** ** ** ** ** ** return (cmd_bind_key_mode_table(self, cmdq,
key));
+** ** ** **if (args_has(args, 'T'))
+** ** ** ** ** ** ** **tablename = args_get(args, 'T');
+** ** ** **else if (args_has(args, 'n'))
+** ** ** ** ** ** ** **tablename = "root";
+** ** ** **else
+** ** ** ** ** ** ** **tablename = "prefix";
+
** ** ** ** cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1,
NULL, 0,
** ** ** ** ** ** &cause);
** ** ** ** if (cmdlist == NULL) {
@@ -76,9 +84,7 @@ cmd_bind_key_exec(struct cmd *self, stru
** ** ** ** ** ** ** ** return (CMD_RETURN_ERROR);
** ** ** ** }
-** ** ** **if (!args_has(args, 'n'))
-** ** ** ** ** **key |= KEYC_PREFIX;
-** ** ** **key_bindings_add(key, args_has(args, 'r'), cmdlist);
+** ** ** **key_bindings_add(tablename, key, args_has(args, 'r'),
cmdlist);
** ** ** ** return (CMD_RETURN_NORMAL);
**}
Index: cmd-list-keys.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-list-keys.c,v
retrieving revision 1.25
diff -u -p -r1.25 cmd-list-keys.c
--- cmd-list-keys.c** ** **20 Oct 2014 23:27:14 -0000** ** ** 1.25
+++ cmd-list-keys.c** ** **13 Apr 2015 21:29:21 -0000
@@ -33,8 +33,8 @@ enum cmd_retval** ** ** ** cmd_list_keys_commands(
**const struct cmd_entry cmd_list_keys_entry = {
** ** ** ** "list-keys", "lsk",
-** ** ** **"t:", 0, 0,
-** ** ** **"[-t key-table]",
+** ** ** **"t:T:", 0, 0,
+** ** ** **"[-t mode-table] [-T key-table]",
** ** ** ** 0,
** ** ** ** cmd_list_keys_exec
**};
@@ -51,58 +51,65 @@ enum cmd_retval
**cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
**{
** ** ** ** struct args** ** ** ** ** ** ***args = self->args;
+** ** ** **struct key_table** ** ** ** *table;
** ** ** ** struct key_binding** ** ** *bd;
-** ** ** **const char** ** ** ** ** ** ** *key;
-** ** ** **char** ** ** ** ** ** ** ** ** ** **tmp[BUFSIZ], flags[8];
+** ** ** **const char** ** ** ** ** ** ** *key, *tablename, *r;
+** ** ** **char** ** ** ** ** ** ** ** ** ** **tmp[BUFSIZ];
** ** ** ** size_t** ** ** ** ** ** ** ** ** **used;
-** ** ** **int** ** ** ** ** ** ** ** ** ** ** width, keywidth;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** repeat, width,
tablewidth, keywidth;
** ** ** ** if (self->entry == &cmd_list_commands_entry)
** ** ** ** ** ** ** ** return (cmd_list_keys_commands(self, cmdq));
** ** ** ** if (args_has(args, 't'))
** ** ** ** ** ** ** ** return (cmd_list_keys_table(self, cmdq));
+** ** ** **tablename = args_get(args, 'T');
-** ** ** **width = 0;
-
-** ** ** **RB_FOREACH(bd, key_bindings, &key_bindings) {
-** ** ** ** ** ** ** **key = key_string_lookup_key(bd->key &
~KEYC_PREFIX);
-** ** ** ** ** ** ** **if (key == NULL)
+** ** ** **repeat = 0;
+** ** ** **tablewidth = keywidth = 0;
+** ** ** **RB_FOREACH(table, key_tables, &key_tables) {
+** ** ** ** ** ** ** **if (tablename != NULL && strcmp(table->name,
tablename) != 0)
** ** ** ** ** ** ** ** ** ** ** ** continue;
+** ** ** ** ** ** ** **RB_FOREACH(bd, key_bindings,
&(table->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;
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **repeat = 1;
+
+** ** ** ** ** ** ** ** ** ** ** **width = strlen(table->name);
+** ** ** ** ** ** ** ** ** ** ** **if (width > tablewidth)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **tablewidth =width;
+** ** ** ** ** ** ** ** ** ** ** **width = strlen(key);
+** ** ** ** ** ** ** ** ** ** ** **if (width > keywidth)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **keywidth = width;
+** ** ** ** ** ** ** **}
** ** ** ** }
-** ** ** **RB_FOREACH(bd, key_bindings, &key_bindings) {
-** ** ** ** ** ** ** **key = key_string_lookup_key(bd->key &
~KEYC_PREFIX);
-** ** ** ** ** ** ** **if (key == NULL)
+** ** ** **RB_FOREACH(table, key_tables, &key_tables) {
+** ** ** ** ** ** ** **if (tablename != NULL && strcmp(table->name,
tablename) != 0)
** ** ** ** ** ** ** ** ** ** ** ** continue;
-
-** ** ** ** ** ** ** ***flags = '\0';
-** ** ** ** ** ** ** **if (!(bd->key & KEYC_PREFIX)) {
-** ** ** ** ** ** ** ** ** ** ** **if (bd->can_repeat)
-** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **xsnprintf(flags, sizeof
flags, "-rn ");
+** ** ** ** ** ** ** **RB_FOREACH(bd, key_bindings,
&(table->key_bindings)) {
+** ** ** ** ** ** ** ** ** ** ** **key =
key_string_lookup_key(bd->key);
+** ** ** ** ** ** ** ** ** ** ** **if (key == NULL)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **continue;
+
+** ** ** ** ** ** ** ** ** ** ** **if (!repeat)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **r = "";
+** ** ** ** ** ** ** ** ** ** ** **else if (bd->can_repeat)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **r = "-r ";
** ** ** ** ** ** ** ** ** ** ** ** 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;
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **r = "** **";
+** ** ** ** ** ** ** ** ** ** ** **used = xsnprintf(tmp, sizeof tmp,
"%s-T %-*s %-*s ", r,
+** ** ** ** ** ** ** ** ** ** ** ** ** **(int)tablewidth, table->name,
(int)keywidth, key);
+** ** ** ** ** ** ** ** ** ** ** **if (used < sizeof tmp) {
+** ** ** ** ** ** ** ** ** ** ** ** ** ** **
**cmd_list_print(bd->cmdlist, tmp + used,
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **(sizeof tmp) -
used);
+** ** ** ** ** ** ** ** ** ** ** **}
-** ** ** ** ** ** ** **cmd_list_print(bd->cmdlist, tmp + used, (sizeof
tmp) - used);
-** ** ** ** ** ** ** **cmdq_print(cmdq, "bind-key %s", tmp);
+** ** ** ** ** ** ** ** ** ** ** **cmdq_print(cmdq, "bind-key %s",
tmp);
+** ** ** ** ** ** ** **}
** ** ** ** }
** ** ** ** return (CMD_RETURN_NORMAL);
Index: cmd-switch-client.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-switch-client.c,v
retrieving revision 1.22
diff -u -p -r1.22 cmd-switch-client.c
--- cmd-switch-client.c 20 Oct 2014 22:29:25 -0000** ** ** 1.22
+++ cmd-switch-client.c 13 Apr 2015 21:29:21 -0000
@@ -31,8 +31,8 @@ enum cmd_retval** ** ** ** cmd_switch_client_exec(
**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_exec
**};
@@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self,
** ** ** ** struct winlink** ** ** ** ** *wl = NULL;
** ** ** ** struct window** ** ** ** ** ***w = NULL;
** ** ** ** struct window_pane** ** ** *wp = NULL;
-** ** ** **const char** ** ** ** ** ** ** *tflag;
+** ** ** **const char** ** ** ** ** ** ** *tflag, *tablename;
+** ** ** **struct key_table** ** ** ** *table;
** ** ** ** if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) ==
NULL)
** ** ** ** ** ** ** ** return (CMD_RETURN_ERROR);
@@ -56,6 +57,18 @@ cmd_switch_client_exec(struct cmd *self,
** ** ** ** ** ** ** ** ** ** ** ** c->flags &= ~CLIENT_READONLY;
** ** ** ** ** ** ** ** else
** ** ** ** ** ** ** ** ** ** ** ** c->flags |= CLIENT_READONLY;
+** ** ** **}
+
+** ** ** **tablename = args_get(args, 'T');
+** ** ** **if (tablename != NULL) {
+** ** ** ** ** ** ** **table = key_bindings_get_table(tablename, 0);
+** ** ** ** ** ** ** **if (table == NULL) {
+** ** ** ** ** ** ** ** ** ** ** **cmdq_error(cmdq, "table %s doesn't
exist", tablename);
+** ** ** ** ** ** ** ** ** ** ** **return (CMD_RETURN_ERROR);
+** ** ** ** ** ** ** **}
+** ** ** ** ** ** ** **table->references++;
+** ** ** ** ** ** ** **key_bindings_unref_table(c->keytable);
+** ** ** ** ** ** ** **c->keytable = table;
** ** ** ** }
** ** ** ** tflag = args_get(args, 't');
Index: cmd-unbind-key.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-unbind-key.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-unbind-key.c
--- cmd-unbind-key.c** ** 20 Oct 2014 22:29:25 -0000** ** ** 1.20
+++ cmd-unbind-key.c** ** 13 Apr 2015 21:29:21 -0000
@@ -31,8 +31,8 @@ enum cmd_retval** ** ** ** cmd_unbind_key_mode_tab
**const struct cmd_entry cmd_unbind_key_entry = {
** ** ** ** "unbind-key", "unbind",
-** ** ** **"acnt:", 0, 1,
-** ** ** **"[-acn] [-t mode-table] key",
+** ** ** **"acnt:T:", 0, 1,
+** ** ** **"[-acn] [-t mode-table] [-T key-table] key",
** ** ** ** 0,
** ** ** ** cmd_unbind_key_exec
**};
@@ -40,9 +40,9 @@ const struct cmd_entry cmd_unbind_key_en
**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;
+** ** ** **struct args** ** ***args = self->args;
+** ** ** **int** ** ** ** ** ** ** key;
+** ** ** **const char** ** ** *tablename;
** ** ** ** if (!args_has(args, 'a')) {
** ** ** ** ** ** ** ** if (args->argc != 1) {
@@ -66,16 +66,23 @@ cmd_unbind_key_exec(struct cmd *self, st
** ** ** ** ** ** ** ** return (cmd_unbind_key_mode_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'))
+** ** ** ** ** ** ** **tablename = args_get(args, 'T');
+** ** ** **else if (args_has(args, 'n'))
+** ** ** ** ** ** ** **tablename = "root";
+** ** ** **else
+** ** ** ** ** ** ** **tablename = "prefix";
+
+** ** ** **key_bindings_remove(tablename, key);
** ** ** ** return (CMD_RETURN_NORMAL);
**}
Index: format.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/format.c,v
retrieving revision 1.60
diff -u -p -r1.60 format.c
--- format.c** ** 31 Mar 2015 17:58:36 -0000** ** ** 1.60
+++ format.c** ** 13 Apr 2015 21:29:21 -0000
@@ -545,7 +545,11 @@ format_defaults_client(struct format_tre
** ** ** ** format_add(ft, "client_activity", "%lld", (long long) t);
** ** ** ** format_add(ft, "client_activity_string", "%s",
format_time_string(t));
-** ** ** **format_add(ft, "client_prefix", "%d", !!(c->flags &
CLIENT_PREFIX));
+** ** ** **if (strcmp(c->keytable->name, "root") == 0)
+** ** ** ** ** ** ** **format_add(ft, "client_prefix", "%d", 0);
+** ** ** **else
+** ** ** ** ** ** ** **format_add(ft, "client_prefix", "%d", 1);
+** ** ** **format_add(ft, "client_key_table", "%s", c->keytable->name);
** ** ** ** if (c->tty.flags & TTY_UTF8)
** ** ** ** ** ** ** ** format_add(ft, "client_utf8", "%d", 1);
Index: key-bindings.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/key-bindings.c,v
retrieving revision 1.43
diff -u -p -r1.43 key-bindings.c
--- key-bindings.c** ** ** 22 Oct 2014 23:18:53 -0000** ** ** 1.43
+++ key-bindings.c** ** ** 13 Apr 2015 21:29:21 -0000
@@ -25,60 +25,121 @@
**#include "tmux.h"
**RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+RB_GENERATE(key_tables, key_table, entry, key_table_cmp);
+struct key_tables key_tables = RB_INITIALIZER(&key_tables);
-struct key_bindings** ** key_bindings;
+int
+key_table_cmp(struct key_table *e1, struct key_table *e2)
+{
+** ** ** **return (strcmp(e1->name, e2->name));
+}
**int
**key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
**{
-** ** ** **int** ** **key1, key2;
+** ** ** **return (bd1->key - bd2->key);
+}
+
+struct key_table *
+key_bindings_get_table(const char *name, int create)
+{
+** ** ** **struct key_table** ** ** ** table_search, *table;
+
+** ** ** **[2]table_search.name = name;
+** ** ** **table = RB_FIND(key_tables, &key_tables, &table_search);
+** ** ** **if (table != NULL || !create)
+** ** ** ** ** ** ** **return (table);
-** ** ** **key1 = bd1->key & ~KEYC_PREFIX;
-** ** ** **key2 = bd2->key & ~KEYC_PREFIX;
-** ** ** **if (key1 != key2)
-** ** ** ** ** ** ** **return (key1 - key2);
+** ** ** **table = xmalloc(sizeof *table);
+** ** ** **table->name = xstrdup(name);
+** ** ** **RB_INIT(&table->key_bindings);
-** ** ** **if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
-** ** ** ** ** ** ** **return (-1);
-** ** ** **if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
-** ** ** ** ** ** ** **return (1);
-** ** ** **return (0);
+** ** ** **table->references = 1; /* one reference in key_tables */
+** ** ** **RB_INSERT(key_tables, &key_tables, table);
+
+** ** ** **return (table);
**}
-struct key_binding *
-key_bindings_lookup(int key)
+void
+key_bindings_unref_table(struct key_table *table)
**{
-** ** ** **struct key_binding** ** ** bd;
+** ** ** **struct key_binding** ** ** *bd;
-** ** ** **bd.key = key;
-** ** ** **return (RB_FIND(key_bindings, &key_bindings, &bd));
+** ** ** **if (--table->references != 0)
+** ** ** ** ** ** ** **return;
+
+** ** ** **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((void *)table->name);
+** ** ** **free(table);
**}
**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_binding** ** ** *bd;
+** ** ** **struct key_table** ** ** ** *table;
+** ** ** **struct key_binding** ** ** **bd_search, *bd;
-** ** ** **key_bindings_remove(key);
+** ** ** **table = key_bindings_get_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;
**}
**void
-key_bindings_remove(int key)
+key_bindings_remove(const char *name, int key)
**{
-** ** ** **struct key_binding** ** ** *bd;
+** ** ** **struct key_table** ** ** ** *table;
+** ** ** **struct key_binding** ** ** **bd_search, *bd;
+
+** ** ** **table = key_bindings_get_table(name, 0);
+** ** ** **if (table == NULL)
+** ** ** ** ** ** ** **return;
-** ** ** **if ((bd = key_bindings_lookup(key)) == NULL)
+** ** ** **bd_search.key = key;
+** ** ** **bd = RB_FIND(key_bindings, &table->key_bindings,
&bd_search);
+** ** ** **if (bd == NULL)
** ** ** ** ** ** ** ** 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)) {
+** ** ** ** ** ** ** **RB_REMOVE(key_tables, &key_tables, table);
+** ** ** ** ** ** ** **key_bindings_unref_table(table);
+** ** ** **}
+}
+
+void
+key_bindings_remove_table(const char *name)
+{
+** ** ** **struct key_table** ** ** ** *table;
+
+** ** ** **table = key_bindings_get_table(name, 0);
+** ** ** **if (table == NULL)
+** ** ** ** ** ** ** **return;
+
+** ** ** **RB_REMOVE(key_tables, &key_tables, table);
+** ** ** **key_bindings_unref_table(table);
**}
**void
@@ -164,8 +225,6 @@ key_bindings_init(void)
** ** ** ** char*** ** ** ** ** ** cause;
** ** ** ** int** ** ** ** ** ** ** error;
** ** ** ** struct cmd_q** ** *cmdq;
-
-** ** ** **RB_INIT(&key_bindings);
** ** ** ** cmdq = cmdq_new(NULL);
** ** ** ** for (i = 0; i < nitems(defaults); i++) {
Index: server-client.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/server-client.c,v
retrieving revision 1.129
diff -u -p -r1.129 server-client.c
--- server-client.c** ** **31 Mar 2015 17:45:10 -0000** ** ** 1.129
+++ server-client.c** ** **13 Apr 2015 21:29:21 -0000
@@ -100,6 +100,9 @@ server_client_create(int fd)
** ** ** ** c->flags |= CLIENT_FOCUSED;
+** ** ** **c->keytable = key_bindings_get_table("root", 1);
+** ** ** **c->keytable->references++;
+
** ** ** ** evtimer_set(&c->repeat_timer, server_client_repeat_timer,
c);
** ** ** ** for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
@@ -171,6 +174,8 @@ server_client_lost(struct client *c)
** ** ** ** evtimer_del(&c->repeat_timer);
+** ** ** **key_bindings_unref_table(c->keytable);
+
** ** ** ** if (event_initialized(&c->identify_timer))
** ** ** ** ** ** ** ** evtimer_del(&c->identify_timer);
@@ -362,33 +367,28 @@ server_client_assume_paste(struct sessio
**void
**server_client_handle_key(struct client *c, int key)
**{
-** ** ** **struct session** ** ** ** ** *s;
+** ** ** **struct session** ** ** ** ** *s = c->session;
** ** ** ** struct window** ** ** ** ** ***w;
** ** ** ** struct window_pane** ** ** *wp;
** ** ** ** struct timeval** ** ** ** ** **tv;
-** ** ** **struct key_binding** ** ** *bd;
-** ** ** **int** ** ** ** ** ** ** ** ** ** ** xtimeout, isprefix,
ispaste;
+** ** ** **struct key_table** ** ** ** *table = c->keytable;
+** ** ** **struct key_binding** ** ** **bd_search, *bd;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** xtimeout;
** ** ** ** /* Check the client is good to accept input. */
-** ** ** **if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
-** ** ** ** ** ** ** **return;
-
-** ** ** **if (c->session == NULL)
+** ** ** **if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED))
!= 0)
** ** ** ** ** ** ** ** return;
-** ** ** **s = c->session;
+** ** ** **w = c->session->curw->window;
+** ** ** **wp = w->active;
** ** ** ** /* Update the activity timer. */
** ** ** ** if (gettimeofday(&c->activity_time, NULL) != 0)
** ** ** ** ** ** ** ** fatal("gettimeofday failed");
-
** ** ** ** memcpy(&s->last_activity_time, &s->activity_time,
** ** ** ** ** ** sizeof s->last_activity_time);
** ** ** ** memcpy(&s->activity_time, &c->activity_time, sizeof
s->activity_time);
-** ** ** **w = c->session->curw->window;
-** ** ** **wp = w->active;
-
-** ** ** **/* Special case: number keys jump to pane in identify mode.
*/
+** ** ** **/* Number keys jump to pane in identify mode. */
** ** ** ** if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9')
{
** ** ** ** ** ** ** ** if (c->flags & CLIENT_READONLY)
** ** ** ** ** ** ** ** ** ** ** ** return;
@@ -419,74 +419,89 @@ server_client_handle_key(struct client *
** ** ** ** ** ** ** ** return;
** ** ** ** }
-** ** ** **/* Is this a prefix key? */
-** ** ** **if (key == options_get_number(&s->options, "prefix"))
-** ** ** ** ** ** ** **isprefix = 1;
-** ** ** **else if (key == options_get_number(&s->options, "prefix2"))
-** ** ** ** ** ** ** **isprefix = 1;
-** ** ** **else
-** ** ** ** ** ** ** **isprefix = 0;
+** ** ** **/* Treat everything as a regular key when pasting is
detected. */
+** ** ** **if (server_client_assume_paste(s)) {
+** ** ** ** ** ** ** **if (!(c->flags & CLIENT_READONLY))
+** ** ** ** ** ** ** ** ** ** ** **window_pane_key(wp, s, key);
+** ** ** ** ** ** ** **return;
+** ** ** **}
-** ** ** **/* Treat prefix as a regular key when pasting is detected.
*/
-** ** ** **ispaste = server_client_assume_paste(s);
-** ** ** **if (ispaste)
-** ** ** ** ** ** ** **isprefix = 0;
-
-** ** ** **/* No previous prefix key. */
-** ** ** **if (!(c->flags & CLIENT_PREFIX)) {
-** ** ** ** ** ** ** **if (isprefix) {
-** ** ** ** ** ** ** ** ** ** ** **c->flags |= CLIENT_PREFIX;
+** ** ** **/* Try to see if there is a key binding in the current
table. */
+** ** ** **bd_search.key = key;
+** ** ** **bd = RB_FIND(key_bindings, &table->key_bindings,
&bd_search);
+** ** ** **if (bd != NULL) {
+** ** ** ** ** ** ** **/*
+** ** ** ** ** ** ** ** * Key was matched in this table. If currently
repeating but
+** ** ** ** ** ** ** ** * a non-repeating binding was found, stop
repeating and try
+** ** ** ** ** ** ** ** * again in the root table.
+** ** ** ** ** ** ** ** */
+** ** ** ** ** ** ** **if ((c->flags & CLIENT_REPEAT) &&
!bd->can_repeat) {
+** ** ** ** ** ** ** ** ** ** ** **server_set_key_table(c, "root");
+** ** ** ** ** ** ** ** ** ** ** **c->flags &= ~CLIENT_REPEAT;
** ** ** ** ** ** ** ** ** ** ** ** server_status_client(c);
-** ** ** ** ** ** ** ** ** ** ** **return;
+** ** ** ** ** ** ** ** ** ** ** **goto retry;
** ** ** ** ** ** ** ** }
-** ** ** ** ** ** ** **/* 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) {
+** ** ** ** ** ** ** **/*
+** ** ** ** ** ** ** ** * Take a reference to this table to make sure
the key binding
+** ** ** ** ** ** ** ** * doesn't disappear.
+** ** ** ** ** ** ** ** */
+** ** ** ** ** ** ** **table->references++;
+
+** ** ** ** ** ** ** **/*
+** ** ** ** ** ** ** ** * If this is a repeating key, start the timer.
Otherwise reset
+** ** ** ** ** ** ** ** * the client back to the root table.
+** ** ** ** ** ** ** ** */
+** ** ** ** ** ** ** **xtimeout = options_get_number(&s->options,
"repeat-time");
+** ** ** ** ** ** ** **if (xtimeout != 0 && bd->can_repeat) {
+** ** ** ** ** ** ** ** ** ** ** **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 {
** ** ** ** ** ** ** ** ** ** ** ** c->flags &= ~CLIENT_REPEAT;
-** ** ** ** ** ** ** ** ** ** ** **if (isprefix)
-** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **c->flags |=
CLIENT_PREFIX;
-** ** ** ** ** ** ** ** ** ** ** **else if (!(c->flags &
CLIENT_READONLY))
-** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **window_pane_key(wp, s,
key);
+** ** ** ** ** ** ** ** ** ** ** **server_set_key_table(c, "root");
** ** ** ** ** ** ** ** }
+** ** ** ** ** ** ** **server_status_client(c);
+
+** ** ** ** ** ** ** **/* Dispatch the key binding. */
+** ** ** ** ** ** ** **key_bindings_dispatch(bd, c);
+** ** ** ** ** ** ** **key_bindings_unref_table(table);
+
** ** ** ** ** ** ** ** return;
** ** ** ** }
-** ** ** **/* If already repeating, but this key can't repeat, skip it.
*/
-** ** ** **if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
+** ** ** **/*
+** ** ** ** * No match in this table. If repeating, switch the client
back to the
+** ** ** ** * root table and try again.
+** ** ** ** */
+** ** ** **if (c->flags & CLIENT_REPEAT) {
+** ** ** ** ** ** ** **server_set_key_table(c, "root");
** ** ** ** ** ** ** ** c->flags &= ~CLIENT_REPEAT;
-** ** ** ** ** ** ** **if (isprefix)
-** ** ** ** ** ** ** ** ** ** ** **c->flags |= CLIENT_PREFIX;
-** ** ** ** ** ** ** **else if (!(c->flags & CLIENT_READONLY))
-** ** ** ** ** ** ** ** ** ** ** **window_pane_key(wp, s, key);
-** ** ** ** ** ** ** **return;
+** ** ** ** ** ** ** **server_status_client(c);
+** ** ** ** ** ** ** **goto retry;
** ** ** ** }
-** ** ** **/* 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);
+** ** ** **/* If no match and we're not in the root table, that's it.
*/
+** ** ** **if (strcmp(c->keytable->name, "root") != 0) {
+** ** ** ** ** ** ** **server_set_key_table(c, "root");
+** ** ** ** ** ** ** **server_status_client(c);
+** ** ** ** ** ** ** **return;
** ** ** ** }
-** ** ** **/* Dispatch the command. */
-** ** ** **key_bindings_dispatch(bd, c);
+** ** ** **/*
+** ** ** ** * No match, but in the root table. Prefix switches to the
prefix table
+** ** ** ** * and everything else is passed through.
+** ** ** ** */
+** ** ** **if (key == options_get_number(&s->options, "prefix") ||
+** ** ** ** ** **key == options_get_number(&s->options, "prefix2")) {
+** ** ** ** ** ** ** **server_set_key_table(c, "prefix");
+** ** ** ** ** ** ** **server_status_client(c);
+** ** ** **} else if (!(c->flags & CLIENT_READONLY))
+** ** ** ** ** ** ** **window_pane_key(wp, s, key);
**}
**/* Client functions that need to happen every loop. */
@@ -692,9 +707,9 @@ server_client_repeat_timer(unused int fd
** ** ** ** 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_set_key_table(c, "root");
+** ** ** ** ** ** ** **c->flags &= ~CLIENT_REPEAT;
+** ** ** ** ** ** ** **server_status_client(c);
** ** ** ** }
**}
Index: server-fn.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/server-fn.c,v
retrieving revision 1.79
diff -u -p -r1.79 server-fn.c
--- server-fn.c 31 Mar 2015 17:45:10 -0000** ** ** 1.79
+++ server-fn.c 13 Apr 2015 21:29:22 -0000
@@ -101,6 +101,14 @@ server_status_client(struct client *c)
**}
**void
+server_set_key_table(struct client *c, const char *name)
+{
+** ** ** **key_bindings_unref_table(c->keytable);
+** ** ** **c->keytable = key_bindings_get_table(name, 1);
+** ** ** **c->keytable->references++;
+}
+
+void
**server_redraw_session(struct session *s)
**{
** ** ** ** struct client** ***c;
Index: tmux.1
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.1,v
retrieving revision 1.417
diff -u -p -r1.417 tmux.1
--- tmux.1** ** ** 10 Apr 2015 16:00:08 -0000** ** ** 1.417
+++ tmux.1** ** ** 13 Apr 2015 21:29:23 -0000
@@ -838,6 +838,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
@@ -855,6 +856,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
interpreted from
+.Ar key-table .
**.El
**.Sh WINDOWS AND PANES
**A
@@ -1905,6 +1909,7 @@ Commands related to key bindings are as
**.It Xo Ic bind-key
**.Op Fl cnr
**.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
**.Ar key Ar command Op Ar arguments
**.Xc
**.D1 (alias: Ic bind )
@@ -1940,18 +1945,41 @@ or for normal mode without.
**To view the default bindings and possible commands, see the
**.Ic list-keys
**command.
-.It Ic list-keys Op Fl t Ar key-table
+.Pp
+If
+.Fl T
+is present,
+.Ar key
+is bound in
+.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 Xo Ic list-keys
+.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
+.Xc
**.D1 (alias: Ic lsk )
**List all key bindings.
**Without
**.Fl t
-the primary key bindings - those executed when preceded by the prefix key -
-are printed.
+or
+.Fl T
+all key tables are printed.
+With
+.Fl T
+only
+.Ar key-table .
**.Pp
**With
**.Fl t ,
**the key bindings in
-.Ar key-table
+.Ar mode-table
**.Em vi-edit ,
**.Em emacs-edit ,
@@ -1992,6 +2020,7 @@ the secondary prefix key, to a window as
**.It Xo Ic unbind-key
**.Op Fl acn
**.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
**.Ar key
**.Xc
**.D1 (alias: Ic unbind )
@@ -2017,6 +2046,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
+.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
Index: tmux.h
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.h,v
retrieving revision 1.490
diff -u -p -r1.490 tmux.h
--- tmux.h** ** ** 6 Feb 2015 17:21:08 -0000** ** ** **1.490
+++ tmux.h** ** ** 13 Apr 2015 21:29:23 -0000
@@ -89,10 +89,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. */
@@ -1287,7 +1286,7 @@ struct client {
** ** ** ** struct screen** ** status;
**#define CLIENT_TERMINAL 0x1
-#define CLIENT_PREFIX 0x2
+/* 0x2 unused */
**#define CLIENT_EXIT 0x4
**#define CLIENT_REDRAW 0x8
**#define CLIENT_STATUS 0x10
@@ -1306,6 +1305,7 @@ struct client {
**#define CLIENT_256COLOURS 0x20000
**#define CLIENT_IDENTIFIED 0x40000
** ** ** ** int** ** ** ** ** ** ** flags;
+** ** ** **struct key_table *keytable;
** ** ** ** struct event** ** **identify_timer;
@@ -1423,15 +1423,24 @@ struct cmd_entry {
** ** ** ** enum cmd_retval** (*exec)(struct cmd *, struct cmd_q *);
**};
-/* Key binding. */
+/* Key binding and key table. */
**struct key_binding {
-** ** ** **int** ** ** ** ** ** ** key;
-** ** ** **struct cmd_list *cmdlist;
-** ** ** **int** ** ** ** ** ** ** can_repeat;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** key;
+** ** ** **struct cmd_list** ** ** ** ***cmdlist;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** can_repeat;
-** ** ** **RB_ENTRY(key_binding) entry;
+** ** ** **RB_ENTRY(key_binding)** ** entry;
**};
**RB_HEAD(key_bindings, key_binding);
+struct key_table {
+** ** ** **const char** ** ** ** ** ** ** ***name;
+** ** ** **struct key_bindings** ** ** key_bindings;
+
+** ** ** **u_int** ** ** ** ** ** ** ** ** ** references;
+
+** ** ** **RB_ENTRY(key_table)** ** ** entry;
+};
+RB_HEAD(key_tables, key_table);
**/*
** * Option table entries. The option table is the user-visible part of
the
@@ -1848,12 +1857,16 @@ void** ** cmd_wait_for_flush(void);
**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_tables, key_table, entry, key_table_cmp);
+extern struct key_tables key_tables;
+int** ** **key_table_cmp(struct key_table *, struct key_table *);
+int** ** **key_bindings_cmp(struct key_binding *, struct key_binding
*);
+struct** ** ** ** ** key_table *key_bindings_get_table(const char *,
int);
+void** ** key_bindings_unref_table(struct key_table *);
+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
*);
@@ -1889,6 +1902,7 @@ void** ** ** **server_write_session(struct sessio
** ** ** ** ** ** **size_t);
**void** ** server_redraw_client(struct client *);
**void** ** server_status_client(struct client *);
+void** ** server_set_key_table(struct client *, const char *);
**void** ** server_redraw_session(struct session *);
**void** ** server_redraw_session_group(struct session *);
**void** ** server_status_session(struct session *);
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
Or are you thinking of creating the table after running the
set
Post by Nicholas Marriott
command?
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
This shouldn't work - you shouldn't be able to set a client to
a
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
nonexistent table.
I guess I don't super care what happens since I won't personally
be
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
writing any switch-client -T into an empty table but I had
assumed we
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
would allow switching to a new [and ephemeral] table since
otherwise
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.
I can't think of a case where I would want it to automatically
create a
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
table, except to confuse people who make a typo in the table name.
Do
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
you have a use in mind?
Not specifically, I just prefer to see tools degrade what might be
described as "uniformly" where possible.** Nonuniform behaviour
makes
Post by Nicholas Marriott
Post by Keith Amling
tools dangerous for automated use because when writing a script
you're
Post by Nicholas Marriott
Post by Keith Amling
unlikely to think of things like "what if this table is empty" or
"what
Post by Nicholas Marriott
Post by Keith Amling
if this command barfs on no args when really no args means I just
didn't
Post by Nicholas Marriott
Post by Keith Amling
need it to do anything".
I'll switch it and the next set of patches will be up shortly.
Keith
--------------------------------------------------------------------------
Post by Nicholas Marriott
----
Post by Keith Amling
"Accelerate Dev Cycles with Automated Cross-Browser Testing - For
FREE
Post by Nicholas Marriott
Post by Keith Amling
Instantly run your Selenium tests across 300+ browser/OS combos.
Get unparalleled scalability from the best Selenium testing platform
available
Post by Keith Amling
Simple to use. Nothing to install. Get started now for free."
[4]http://p.sf.net/sfu/SauceLabs
Has work on this continued?** I made an issue about it
([5]https://sourceforge.net/p/tmux/tickets/172/) in an effort to bring
attention to this patch, but Thomas said it's pretty much not going
anywhere
Post by Nicholas Marriott
unless the original patch author continues or makes an effort to get
it
Post by Nicholas Marriott
merged himself.
I would love to see this landed in tmux!** My current workaround can
be seen
Post by Nicholas Marriott
at
[6]https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
Post by Nicholas Marriott
and
[7]https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
Post by Nicholas Marriott
cords.sh.
------------------------------------------------------------------------------
Post by Nicholas Marriott
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live
exercises
Post by Nicholas Marriott
[8]http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual-
event?utm_
Post by Nicholas Marriott
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
tmux-users mailing list
[10]https://lists.sourceforge.net/lists/listinfo/tmux-users
References
Visible links
2. http://table_search.name/
3. http://palantir.com/
4. http://p.sf.net/sfu/SauceLabs
5. https://sourceforge.net/p/tmux/tickets/172/
6. https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
7. https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
8. http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual-
10. https://lists.sourceforge.net/lists/listinfo/tmux-users
Nicholas Marriott
2015-04-19 21:48:51 UTC
Permalink
It needs a bit of love again after the mouse keys stuff that just went
in so it may not be for a while...
Post by Nicholas Marriott
We don't need a merge request, I'll commit it when I'm happy with it. Thanks
Would you mind making a merge request for it?** I could, but it wouldn't
really be right to make a merge request with someone else's hard work!**
Your name should be on this.
Thanks,
~Ross
On Mon, Apr 13, 2015 at 5:30 PM Nicholas Marriott
Here's the latest diff.
It actually seems to work fine, although I haven't done a lot of testing.
IIRC I wasn't wild about the cmd-list-keys.c and cmd-bind-key.c changes;
certainly lsk -T should error on an unknown table, same as bind -T. I
think the manpage bits could do with some improvement too.
Index: cmd-bind-key.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-bind-key.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-bind-key.c
--- cmd-bind-key.c** ** ** 10 Apr 2015 16:00:08 -0000** ** ** 1.20
+++ cmd-bind-key.c** ** ** 13 Apr 2015 21:29:21 -0000
@@ -33,8 +33,8 @@ enum cmd_retval** ** ** ** cmd_bind_key_mode_table
**const struct cmd_entry cmd_bind_key_entry = {
** ** ** ** "bind-key", "bind",
-** ** ** **"cnrt:", 1, -1,
-** ** ** **"[-cnr] [-t mode-table] key command [arguments]",
+** ** ** **"cnrt:T:", 1, -1,
+** ** ** **"[-cnr] [-t mode-table] [-T key-table] key command
[arguments]",
** ** ** ** 0,
** ** ** ** cmd_bind_key_exec
**};
@@ -46,6 +46,7 @@ cmd_bind_key_exec(struct cmd *self, stru
** ** ** ** char** ** ** ** ** ** *cause;
** ** ** ** struct cmd_list *cmdlist;
** ** ** ** int** ** ** ** ** ** ** key;
+** ** ** **const char** ** ** *tablename;
** ** ** ** if (args_has(args, 't')) {
** ** ** ** ** ** ** ** if (args->argc != 2 && args->argc != 3) {
@@ -68,6 +69,13 @@ cmd_bind_key_exec(struct cmd *self, stru
** ** ** ** if (args_has(args, 't'))
** ** ** ** ** ** ** ** return (cmd_bind_key_mode_table(self, cmdq,
key));
+** ** ** **if (args_has(args, 'T'))
+** ** ** ** ** ** ** **tablename = args_get(args, 'T');
+** ** ** **else if (args_has(args, 'n'))
+** ** ** ** ** ** ** **tablename = "root";
+** ** ** **else
+** ** ** ** ** ** ** **tablename = "prefix";
+
** ** ** ** cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1,
NULL, 0,
** ** ** ** ** ** &cause);
** ** ** ** if (cmdlist == NULL) {
@@ -76,9 +84,7 @@ cmd_bind_key_exec(struct cmd *self, stru
** ** ** ** ** ** ** ** return (CMD_RETURN_ERROR);
** ** ** ** }
-** ** ** **if (!args_has(args, 'n'))
-** ** ** ** ** **key |= KEYC_PREFIX;
-** ** ** **key_bindings_add(key, args_has(args, 'r'), cmdlist);
+** ** ** **key_bindings_add(tablename, key, args_has(args, 'r'),
cmdlist);
** ** ** ** return (CMD_RETURN_NORMAL);
**}
Index: cmd-list-keys.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-list-keys.c,v
retrieving revision 1.25
diff -u -p -r1.25 cmd-list-keys.c
--- cmd-list-keys.c** ** **20 Oct 2014 23:27:14 -0000** ** ** 1.25
+++ cmd-list-keys.c** ** **13 Apr 2015 21:29:21 -0000
@@ -33,8 +33,8 @@ enum cmd_retval** ** ** ** cmd_list_keys_commands(
**const struct cmd_entry cmd_list_keys_entry = {
** ** ** ** "list-keys", "lsk",
-** ** ** **"t:", 0, 0,
-** ** ** **"[-t key-table]",
+** ** ** **"t:T:", 0, 0,
+** ** ** **"[-t mode-table] [-T key-table]",
** ** ** ** 0,
** ** ** ** cmd_list_keys_exec
**};
@@ -51,58 +51,65 @@ enum cmd_retval
**cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
**{
** ** ** ** struct args** ** ** ** ** ** ***args = self->args;
+** ** ** **struct key_table** ** ** ** *table;
** ** ** ** struct key_binding** ** ** *bd;
-** ** ** **const char** ** ** ** ** ** ** *key;
-** ** ** **char** ** ** ** ** ** ** ** ** ** **tmp[BUFSIZ], flags[8];
+** ** ** **const char** ** ** ** ** ** ** *key, *tablename, *r;
+** ** ** **char** ** ** ** ** ** ** ** ** ** **tmp[BUFSIZ];
** ** ** ** size_t** ** ** ** ** ** ** ** ** **used;
-** ** ** **int** ** ** ** ** ** ** ** ** ** ** width, keywidth;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** repeat, width,
tablewidth, keywidth;
** ** ** ** if (self->entry == &cmd_list_commands_entry)
** ** ** ** ** ** ** ** return (cmd_list_keys_commands(self, cmdq));
** ** ** ** if (args_has(args, 't'))
** ** ** ** ** ** ** ** return (cmd_list_keys_table(self, cmdq));
+** ** ** **tablename = args_get(args, 'T');
-** ** ** **width = 0;
-
-** ** ** **RB_FOREACH(bd, key_bindings, &key_bindings) {
-** ** ** ** ** ** ** **key = key_string_lookup_key(bd->key &
~KEYC_PREFIX);
-** ** ** ** ** ** ** **if (key == NULL)
+** ** ** **repeat = 0;
+** ** ** **tablewidth = keywidth = 0;
+** ** ** **RB_FOREACH(table, key_tables, &key_tables) {
+** ** ** ** ** ** ** **if (tablename != NULL && strcmp(table->name,
tablename) != 0)
** ** ** ** ** ** ** ** ** ** ** ** continue;
+** ** ** ** ** ** ** **RB_FOREACH(bd, key_bindings,
&(table->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;
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **repeat = 1;
+
+** ** ** ** ** ** ** ** ** ** ** **width = strlen(table->name);
+** ** ** ** ** ** ** ** ** ** ** **if (width > tablewidth)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **tablewidth =width;
+** ** ** ** ** ** ** ** ** ** ** **width = strlen(key);
+** ** ** ** ** ** ** ** ** ** ** **if (width > keywidth)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **keywidth = width;
+** ** ** ** ** ** ** **}
** ** ** ** }
-** ** ** **RB_FOREACH(bd, key_bindings, &key_bindings) {
-** ** ** ** ** ** ** **key = key_string_lookup_key(bd->key &
~KEYC_PREFIX);
-** ** ** ** ** ** ** **if (key == NULL)
+** ** ** **RB_FOREACH(table, key_tables, &key_tables) {
+** ** ** ** ** ** ** **if (tablename != NULL && strcmp(table->name,
tablename) != 0)
** ** ** ** ** ** ** ** ** ** ** ** continue;
-
-** ** ** ** ** ** ** ***flags = '\0';
-** ** ** ** ** ** ** **if (!(bd->key & KEYC_PREFIX)) {
-** ** ** ** ** ** ** ** ** ** ** **if (bd->can_repeat)
-** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **xsnprintf(flags, sizeof
flags, "-rn ");
+** ** ** ** ** ** ** **RB_FOREACH(bd, key_bindings,
&(table->key_bindings)) {
+** ** ** ** ** ** ** ** ** ** ** **key =
key_string_lookup_key(bd->key);
+** ** ** ** ** ** ** ** ** ** ** **if (key == NULL)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **continue;
+
+** ** ** ** ** ** ** ** ** ** ** **if (!repeat)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **r = "";
+** ** ** ** ** ** ** ** ** ** ** **else if (bd->can_repeat)
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **r = "-r ";
** ** ** ** ** ** ** ** ** ** ** ** 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;
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **r = "** **";
+** ** ** ** ** ** ** ** ** ** ** **used = xsnprintf(tmp, sizeof tmp,
"%s-T %-*s %-*s ", r,
+** ** ** ** ** ** ** ** ** ** ** ** ** **(int)tablewidth, table->name,
(int)keywidth, key);
+** ** ** ** ** ** ** ** ** ** ** **if (used < sizeof tmp) {
+** ** ** ** ** ** ** ** ** ** ** ** ** ** **
**cmd_list_print(bd->cmdlist, tmp + used,
+** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **(sizeof tmp) -
used);
+** ** ** ** ** ** ** ** ** ** ** **}
-** ** ** ** ** ** ** **cmd_list_print(bd->cmdlist, tmp + used, (sizeof
tmp) - used);
-** ** ** ** ** ** ** **cmdq_print(cmdq, "bind-key %s", tmp);
+** ** ** ** ** ** ** ** ** ** ** **cmdq_print(cmdq, "bind-key %s",
tmp);
+** ** ** ** ** ** ** **}
** ** ** ** }
** ** ** ** return (CMD_RETURN_NORMAL);
Index: cmd-switch-client.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-switch-client.c,v
retrieving revision 1.22
diff -u -p -r1.22 cmd-switch-client.c
--- cmd-switch-client.c 20 Oct 2014 22:29:25 -0000** ** ** 1.22
+++ cmd-switch-client.c 13 Apr 2015 21:29:21 -0000
@@ -31,8 +31,8 @@ enum cmd_retval** ** ** ** cmd_switch_client_exec(
**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_exec
**};
@@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self,
** ** ** ** struct winlink** ** ** ** ** *wl = NULL;
** ** ** ** struct window** ** ** ** ** ***w = NULL;
** ** ** ** struct window_pane** ** ** *wp = NULL;
-** ** ** **const char** ** ** ** ** ** ** *tflag;
+** ** ** **const char** ** ** ** ** ** ** *tflag, *tablename;
+** ** ** **struct key_table** ** ** ** *table;
** ** ** ** if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) ==
NULL)
** ** ** ** ** ** ** ** return (CMD_RETURN_ERROR);
@@ -56,6 +57,18 @@ cmd_switch_client_exec(struct cmd *self,
** ** ** ** ** ** ** ** ** ** ** ** c->flags &= ~CLIENT_READONLY;
** ** ** ** ** ** ** ** else
** ** ** ** ** ** ** ** ** ** ** ** c->flags |= CLIENT_READONLY;
+** ** ** **}
+
+** ** ** **tablename = args_get(args, 'T');
+** ** ** **if (tablename != NULL) {
+** ** ** ** ** ** ** **table = key_bindings_get_table(tablename, 0);
+** ** ** ** ** ** ** **if (table == NULL) {
+** ** ** ** ** ** ** ** ** ** ** **cmdq_error(cmdq, "table %s doesn't
exist", tablename);
+** ** ** ** ** ** ** ** ** ** ** **return (CMD_RETURN_ERROR);
+** ** ** ** ** ** ** **}
+** ** ** ** ** ** ** **table->references++;
+** ** ** ** ** ** ** **key_bindings_unref_table(c->keytable);
+** ** ** ** ** ** ** **c->keytable = table;
** ** ** ** }
** ** ** ** tflag = args_get(args, 't');
Index: cmd-unbind-key.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-unbind-key.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-unbind-key.c
--- cmd-unbind-key.c** ** 20 Oct 2014 22:29:25 -0000** ** ** 1.20
+++ cmd-unbind-key.c** ** 13 Apr 2015 21:29:21 -0000
@@ -31,8 +31,8 @@ enum cmd_retval** ** ** ** cmd_unbind_key_mode_tab
**const struct cmd_entry cmd_unbind_key_entry = {
** ** ** ** "unbind-key", "unbind",
-** ** ** **"acnt:", 0, 1,
-** ** ** **"[-acn] [-t mode-table] key",
+** ** ** **"acnt:T:", 0, 1,
+** ** ** **"[-acn] [-t mode-table] [-T key-table] key",
** ** ** ** 0,
** ** ** ** cmd_unbind_key_exec
**};
@@ -40,9 +40,9 @@ const struct cmd_entry cmd_unbind_key_en
**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;
+** ** ** **struct args** ** ***args = self->args;
+** ** ** **int** ** ** ** ** ** ** key;
+** ** ** **const char** ** ** *tablename;
** ** ** ** if (!args_has(args, 'a')) {
** ** ** ** ** ** ** ** if (args->argc != 1) {
@@ -66,16 +66,23 @@ cmd_unbind_key_exec(struct cmd *self, st
** ** ** ** ** ** ** ** return (cmd_unbind_key_mode_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'))
+** ** ** ** ** ** ** **tablename = args_get(args, 'T');
+** ** ** **else if (args_has(args, 'n'))
+** ** ** ** ** ** ** **tablename = "root";
+** ** ** **else
+** ** ** ** ** ** ** **tablename = "prefix";
+
+** ** ** **key_bindings_remove(tablename, key);
** ** ** ** return (CMD_RETURN_NORMAL);
**}
Index: format.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/format.c,v
retrieving revision 1.60
diff -u -p -r1.60 format.c
--- format.c** ** 31 Mar 2015 17:58:36 -0000** ** ** 1.60
+++ format.c** ** 13 Apr 2015 21:29:21 -0000
@@ -545,7 +545,11 @@ format_defaults_client(struct format_tre
** ** ** ** format_add(ft, "client_activity", "%lld", (long long) t);
** ** ** ** format_add(ft, "client_activity_string", "%s",
format_time_string(t));
-** ** ** **format_add(ft, "client_prefix", "%d", !!(c->flags &
CLIENT_PREFIX));
+** ** ** **if (strcmp(c->keytable->name, "root") == 0)
+** ** ** ** ** ** ** **format_add(ft, "client_prefix", "%d", 0);
+** ** ** **else
+** ** ** ** ** ** ** **format_add(ft, "client_prefix", "%d", 1);
+** ** ** **format_add(ft, "client_key_table", "%s", c->keytable->name);
** ** ** ** if (c->tty.flags & TTY_UTF8)
** ** ** ** ** ** ** ** format_add(ft, "client_utf8", "%d", 1);
Index: key-bindings.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/key-bindings.c,v
retrieving revision 1.43
diff -u -p -r1.43 key-bindings.c
--- key-bindings.c** ** ** 22 Oct 2014 23:18:53 -0000** ** ** 1.43
+++ key-bindings.c** ** ** 13 Apr 2015 21:29:21 -0000
@@ -25,60 +25,121 @@
**#include "tmux.h"
**RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+RB_GENERATE(key_tables, key_table, entry, key_table_cmp);
+struct key_tables key_tables = RB_INITIALIZER(&key_tables);
-struct key_bindings** ** key_bindings;
+int
+key_table_cmp(struct key_table *e1, struct key_table *e2)
+{
+** ** ** **return (strcmp(e1->name, e2->name));
+}
**int
**key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
**{
-** ** ** **int** ** **key1, key2;
+** ** ** **return (bd1->key - bd2->key);
+}
+
+struct key_table *
+key_bindings_get_table(const char *name, int create)
+{
+** ** ** **struct key_table** ** ** ** table_search, *table;
+
+** ** ** **[2]table_search.name = name;
+** ** ** **table = RB_FIND(key_tables, &key_tables, &table_search);
+** ** ** **if (table != NULL || !create)
+** ** ** ** ** ** ** **return (table);
-** ** ** **key1 = bd1->key & ~KEYC_PREFIX;
-** ** ** **key2 = bd2->key & ~KEYC_PREFIX;
-** ** ** **if (key1 != key2)
-** ** ** ** ** ** ** **return (key1 - key2);
+** ** ** **table = xmalloc(sizeof *table);
+** ** ** **table->name = xstrdup(name);
+** ** ** **RB_INIT(&table->key_bindings);
-** ** ** **if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
-** ** ** ** ** ** ** **return (-1);
-** ** ** **if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
-** ** ** ** ** ** ** **return (1);
-** ** ** **return (0);
+** ** ** **table->references = 1; /* one reference in key_tables */
+** ** ** **RB_INSERT(key_tables, &key_tables, table);
+
+** ** ** **return (table);
**}
-struct key_binding *
-key_bindings_lookup(int key)
+void
+key_bindings_unref_table(struct key_table *table)
**{
-** ** ** **struct key_binding** ** ** bd;
+** ** ** **struct key_binding** ** ** *bd;
-** ** ** **bd.key = key;
-** ** ** **return (RB_FIND(key_bindings, &key_bindings, &bd));
+** ** ** **if (--table->references != 0)
+** ** ** ** ** ** ** **return;
+
+** ** ** **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((void *)table->name);
+** ** ** **free(table);
**}
**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_binding** ** ** *bd;
+** ** ** **struct key_table** ** ** ** *table;
+** ** ** **struct key_binding** ** ** **bd_search, *bd;
-** ** ** **key_bindings_remove(key);
+** ** ** **table = key_bindings_get_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;
**}
**void
-key_bindings_remove(int key)
+key_bindings_remove(const char *name, int key)
**{
-** ** ** **struct key_binding** ** ** *bd;
+** ** ** **struct key_table** ** ** ** *table;
+** ** ** **struct key_binding** ** ** **bd_search, *bd;
+
+** ** ** **table = key_bindings_get_table(name, 0);
+** ** ** **if (table == NULL)
+** ** ** ** ** ** ** **return;
-** ** ** **if ((bd = key_bindings_lookup(key)) == NULL)
+** ** ** **bd_search.key = key;
+** ** ** **bd = RB_FIND(key_bindings, &table->key_bindings,
&bd_search);
+** ** ** **if (bd == NULL)
** ** ** ** ** ** ** ** 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)) {
+** ** ** ** ** ** ** **RB_REMOVE(key_tables, &key_tables, table);
+** ** ** ** ** ** ** **key_bindings_unref_table(table);
+** ** ** **}
+}
+
+void
+key_bindings_remove_table(const char *name)
+{
+** ** ** **struct key_table** ** ** ** *table;
+
+** ** ** **table = key_bindings_get_table(name, 0);
+** ** ** **if (table == NULL)
+** ** ** ** ** ** ** **return;
+
+** ** ** **RB_REMOVE(key_tables, &key_tables, table);
+** ** ** **key_bindings_unref_table(table);
**}
**void
@@ -164,8 +225,6 @@ key_bindings_init(void)
** ** ** ** char*** ** ** ** ** ** cause;
** ** ** ** int** ** ** ** ** ** ** error;
** ** ** ** struct cmd_q** ** *cmdq;
-
-** ** ** **RB_INIT(&key_bindings);
** ** ** ** cmdq = cmdq_new(NULL);
** ** ** ** for (i = 0; i < nitems(defaults); i++) {
Index: server-client.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/server-client.c,v
retrieving revision 1.129
diff -u -p -r1.129 server-client.c
--- server-client.c** ** **31 Mar 2015 17:45:10 -0000** ** ** 1.129
+++ server-client.c** ** **13 Apr 2015 21:29:21 -0000
@@ -100,6 +100,9 @@ server_client_create(int fd)
** ** ** ** c->flags |= CLIENT_FOCUSED;
+** ** ** **c->keytable = key_bindings_get_table("root", 1);
+** ** ** **c->keytable->references++;
+
** ** ** ** evtimer_set(&c->repeat_timer, server_client_repeat_timer,
c);
** ** ** ** for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
@@ -171,6 +174,8 @@ server_client_lost(struct client *c)
** ** ** ** evtimer_del(&c->repeat_timer);
+** ** ** **key_bindings_unref_table(c->keytable);
+
** ** ** ** if (event_initialized(&c->identify_timer))
** ** ** ** ** ** ** ** evtimer_del(&c->identify_timer);
@@ -362,33 +367,28 @@ server_client_assume_paste(struct sessio
**void
**server_client_handle_key(struct client *c, int key)
**{
-** ** ** **struct session** ** ** ** ** *s;
+** ** ** **struct session** ** ** ** ** *s = c->session;
** ** ** ** struct window** ** ** ** ** ***w;
** ** ** ** struct window_pane** ** ** *wp;
** ** ** ** struct timeval** ** ** ** ** **tv;
-** ** ** **struct key_binding** ** ** *bd;
-** ** ** **int** ** ** ** ** ** ** ** ** ** ** xtimeout, isprefix,
ispaste;
+** ** ** **struct key_table** ** ** ** *table = c->keytable;
+** ** ** **struct key_binding** ** ** **bd_search, *bd;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** xtimeout;
** ** ** ** /* Check the client is good to accept input. */
-** ** ** **if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
-** ** ** ** ** ** ** **return;
-
-** ** ** **if (c->session == NULL)
+** ** ** **if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED))
!= 0)
** ** ** ** ** ** ** ** return;
-** ** ** **s = c->session;
+** ** ** **w = c->session->curw->window;
+** ** ** **wp = w->active;
** ** ** ** /* Update the activity timer. */
** ** ** ** if (gettimeofday(&c->activity_time, NULL) != 0)
** ** ** ** ** ** ** ** fatal("gettimeofday failed");
-
** ** ** ** memcpy(&s->last_activity_time, &s->activity_time,
** ** ** ** ** ** sizeof s->last_activity_time);
** ** ** ** memcpy(&s->activity_time, &c->activity_time, sizeof
s->activity_time);
-** ** ** **w = c->session->curw->window;
-** ** ** **wp = w->active;
-
-** ** ** **/* Special case: number keys jump to pane in identify mode.
*/
+** ** ** **/* Number keys jump to pane in identify mode. */
** ** ** ** if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9')
{
** ** ** ** ** ** ** ** if (c->flags & CLIENT_READONLY)
** ** ** ** ** ** ** ** ** ** ** ** return;
@@ -419,74 +419,89 @@ server_client_handle_key(struct client *
** ** ** ** ** ** ** ** return;
** ** ** ** }
-** ** ** **/* Is this a prefix key? */
-** ** ** **if (key == options_get_number(&s->options, "prefix"))
-** ** ** ** ** ** ** **isprefix = 1;
-** ** ** **else if (key == options_get_number(&s->options, "prefix2"))
-** ** ** ** ** ** ** **isprefix = 1;
-** ** ** **else
-** ** ** ** ** ** ** **isprefix = 0;
+** ** ** **/* Treat everything as a regular key when pasting is
detected. */
+** ** ** **if (server_client_assume_paste(s)) {
+** ** ** ** ** ** ** **if (!(c->flags & CLIENT_READONLY))
+** ** ** ** ** ** ** ** ** ** ** **window_pane_key(wp, s, key);
+** ** ** ** ** ** ** **return;
+** ** ** **}
-** ** ** **/* Treat prefix as a regular key when pasting is detected.
*/
-** ** ** **ispaste = server_client_assume_paste(s);
-** ** ** **if (ispaste)
-** ** ** ** ** ** ** **isprefix = 0;
-
-** ** ** **/* No previous prefix key. */
-** ** ** **if (!(c->flags & CLIENT_PREFIX)) {
-** ** ** ** ** ** ** **if (isprefix) {
-** ** ** ** ** ** ** ** ** ** ** **c->flags |= CLIENT_PREFIX;
+** ** ** **/* Try to see if there is a key binding in the current
table. */
+** ** ** **bd_search.key = key;
+** ** ** **bd = RB_FIND(key_bindings, &table->key_bindings,
&bd_search);
+** ** ** **if (bd != NULL) {
+** ** ** ** ** ** ** **/*
+** ** ** ** ** ** ** ** * Key was matched in this table. If currently
repeating but
+** ** ** ** ** ** ** ** * a non-repeating binding was found, stop
repeating and try
+** ** ** ** ** ** ** ** * again in the root table.
+** ** ** ** ** ** ** ** */
+** ** ** ** ** ** ** **if ((c->flags & CLIENT_REPEAT) &&
!bd->can_repeat) {
+** ** ** ** ** ** ** ** ** ** ** **server_set_key_table(c, "root");
+** ** ** ** ** ** ** ** ** ** ** **c->flags &= ~CLIENT_REPEAT;
** ** ** ** ** ** ** ** ** ** ** ** server_status_client(c);
-** ** ** ** ** ** ** ** ** ** ** **return;
+** ** ** ** ** ** ** ** ** ** ** **goto retry;
** ** ** ** ** ** ** ** }
-** ** ** ** ** ** ** **/* 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) {
+** ** ** ** ** ** ** **/*
+** ** ** ** ** ** ** ** * Take a reference to this table to make sure
the key binding
+** ** ** ** ** ** ** ** * doesn't disappear.
+** ** ** ** ** ** ** ** */
+** ** ** ** ** ** ** **table->references++;
+
+** ** ** ** ** ** ** **/*
+** ** ** ** ** ** ** ** * If this is a repeating key, start the timer.
Otherwise reset
+** ** ** ** ** ** ** ** * the client back to the root table.
+** ** ** ** ** ** ** ** */
+** ** ** ** ** ** ** **xtimeout = options_get_number(&s->options,
"repeat-time");
+** ** ** ** ** ** ** **if (xtimeout != 0 && bd->can_repeat) {
+** ** ** ** ** ** ** ** ** ** ** **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 {
** ** ** ** ** ** ** ** ** ** ** ** c->flags &= ~CLIENT_REPEAT;
-** ** ** ** ** ** ** ** ** ** ** **if (isprefix)
-** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **c->flags |=
CLIENT_PREFIX;
-** ** ** ** ** ** ** ** ** ** ** **else if (!(c->flags &
CLIENT_READONLY))
-** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **window_pane_key(wp, s,
key);
+** ** ** ** ** ** ** ** ** ** ** **server_set_key_table(c, "root");
** ** ** ** ** ** ** ** }
+** ** ** ** ** ** ** **server_status_client(c);
+
+** ** ** ** ** ** ** **/* Dispatch the key binding. */
+** ** ** ** ** ** ** **key_bindings_dispatch(bd, c);
+** ** ** ** ** ** ** **key_bindings_unref_table(table);
+
** ** ** ** ** ** ** ** return;
** ** ** ** }
-** ** ** **/* If already repeating, but this key can't repeat, skip it.
*/
-** ** ** **if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
+** ** ** **/*
+** ** ** ** * No match in this table. If repeating, switch the client
back to the
+** ** ** ** * root table and try again.
+** ** ** ** */
+** ** ** **if (c->flags & CLIENT_REPEAT) {
+** ** ** ** ** ** ** **server_set_key_table(c, "root");
** ** ** ** ** ** ** ** c->flags &= ~CLIENT_REPEAT;
-** ** ** ** ** ** ** **if (isprefix)
-** ** ** ** ** ** ** ** ** ** ** **c->flags |= CLIENT_PREFIX;
-** ** ** ** ** ** ** **else if (!(c->flags & CLIENT_READONLY))
-** ** ** ** ** ** ** ** ** ** ** **window_pane_key(wp, s, key);
-** ** ** ** ** ** ** **return;
+** ** ** ** ** ** ** **server_status_client(c);
+** ** ** ** ** ** ** **goto retry;
** ** ** ** }
-** ** ** **/* 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);
+** ** ** **/* If no match and we're not in the root table, that's it.
*/
+** ** ** **if (strcmp(c->keytable->name, "root") != 0) {
+** ** ** ** ** ** ** **server_set_key_table(c, "root");
+** ** ** ** ** ** ** **server_status_client(c);
+** ** ** ** ** ** ** **return;
** ** ** ** }
-** ** ** **/* Dispatch the command. */
-** ** ** **key_bindings_dispatch(bd, c);
+** ** ** **/*
+** ** ** ** * No match, but in the root table. Prefix switches to the
prefix table
+** ** ** ** * and everything else is passed through.
+** ** ** ** */
+** ** ** **if (key == options_get_number(&s->options, "prefix") ||
+** ** ** ** ** **key == options_get_number(&s->options, "prefix2")) {
+** ** ** ** ** ** ** **server_set_key_table(c, "prefix");
+** ** ** ** ** ** ** **server_status_client(c);
+** ** ** **} else if (!(c->flags & CLIENT_READONLY))
+** ** ** ** ** ** ** **window_pane_key(wp, s, key);
**}
**/* Client functions that need to happen every loop. */
@@ -692,9 +707,9 @@ server_client_repeat_timer(unused int fd
** ** ** ** 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_set_key_table(c, "root");
+** ** ** ** ** ** ** **c->flags &= ~CLIENT_REPEAT;
+** ** ** ** ** ** ** **server_status_client(c);
** ** ** ** }
**}
Index: server-fn.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/server-fn.c,v
retrieving revision 1.79
diff -u -p -r1.79 server-fn.c
--- server-fn.c 31 Mar 2015 17:45:10 -0000** ** ** 1.79
+++ server-fn.c 13 Apr 2015 21:29:22 -0000
@@ -101,6 +101,14 @@ server_status_client(struct client *c)
**}
**void
+server_set_key_table(struct client *c, const char *name)
+{
+** ** ** **key_bindings_unref_table(c->keytable);
+** ** ** **c->keytable = key_bindings_get_table(name, 1);
+** ** ** **c->keytable->references++;
+}
+
+void
**server_redraw_session(struct session *s)
**{
** ** ** ** struct client** ***c;
Index: tmux.1
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.1,v
retrieving revision 1.417
diff -u -p -r1.417 tmux.1
--- tmux.1** ** ** 10 Apr 2015 16:00:08 -0000** ** ** 1.417
+++ tmux.1** ** ** 13 Apr 2015 21:29:23 -0000
@@ -838,6 +838,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
@@ -855,6 +856,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
interpreted from
+.Ar key-table .
**.El
**.Sh WINDOWS AND PANES
**A
@@ -1905,6 +1909,7 @@ Commands related to key bindings are as
**.It Xo Ic bind-key
**.Op Fl cnr
**.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
**.Ar key Ar command Op Ar arguments
**.Xc
**.D1 (alias: Ic bind )
@@ -1940,18 +1945,41 @@ or for normal mode without.
**To view the default bindings and possible commands, see the
**.Ic list-keys
**command.
-.It Ic list-keys Op Fl t Ar key-table
+.Pp
+If
+.Fl T
+is present,
+.Ar key
+is bound in
+.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 Xo Ic list-keys
+.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
+.Xc
**.D1 (alias: Ic lsk )
**List all key bindings.
**Without
**.Fl t
-the primary key bindings - those executed when preceded by the prefix key -
-are printed.
+or
+.Fl T
+all key tables are printed.
+With
+.Fl T
+only
+.Ar key-table .
**.Pp
**With
**.Fl t ,
**the key bindings in
-.Ar key-table
+.Ar mode-table
**.Em vi-edit ,
**.Em emacs-edit ,
@@ -1992,6 +2020,7 @@ the secondary prefix key, to a window as
**.It Xo Ic unbind-key
**.Op Fl acn
**.Op Fl t Ar mode-table
+.Op Fl T Ar key-table
**.Ar key
**.Xc
**.D1 (alias: Ic unbind )
@@ -2017,6 +2046,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
+.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
Index: tmux.h
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.h,v
retrieving revision 1.490
diff -u -p -r1.490 tmux.h
--- tmux.h** ** ** 6 Feb 2015 17:21:08 -0000** ** ** **1.490
+++ tmux.h** ** ** 13 Apr 2015 21:29:23 -0000
@@ -89,10 +89,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. */
@@ -1287,7 +1286,7 @@ struct client {
** ** ** ** struct screen** ** status;
**#define CLIENT_TERMINAL 0x1
-#define CLIENT_PREFIX 0x2
+/* 0x2 unused */
**#define CLIENT_EXIT 0x4
**#define CLIENT_REDRAW 0x8
**#define CLIENT_STATUS 0x10
@@ -1306,6 +1305,7 @@ struct client {
**#define CLIENT_256COLOURS 0x20000
**#define CLIENT_IDENTIFIED 0x40000
** ** ** ** int** ** ** ** ** ** ** flags;
+** ** ** **struct key_table *keytable;
** ** ** ** struct event** ** **identify_timer;
@@ -1423,15 +1423,24 @@ struct cmd_entry {
** ** ** ** enum cmd_retval** (*exec)(struct cmd *, struct cmd_q *);
**};
-/* Key binding. */
+/* Key binding and key table. */
**struct key_binding {
-** ** ** **int** ** ** ** ** ** ** key;
-** ** ** **struct cmd_list *cmdlist;
-** ** ** **int** ** ** ** ** ** ** can_repeat;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** key;
+** ** ** **struct cmd_list** ** ** ** ***cmdlist;
+** ** ** **int** ** ** ** ** ** ** ** ** ** ** can_repeat;
-** ** ** **RB_ENTRY(key_binding) entry;
+** ** ** **RB_ENTRY(key_binding)** ** entry;
**};
**RB_HEAD(key_bindings, key_binding);
+struct key_table {
+** ** ** **const char** ** ** ** ** ** ** ***name;
+** ** ** **struct key_bindings** ** ** key_bindings;
+
+** ** ** **u_int** ** ** ** ** ** ** ** ** ** references;
+
+** ** ** **RB_ENTRY(key_table)** ** ** entry;
+};
+RB_HEAD(key_tables, key_table);
**/*
** * Option table entries. The option table is the user-visible part of
the
@@ -1848,12 +1857,16 @@ void** ** cmd_wait_for_flush(void);
**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_tables, key_table, entry, key_table_cmp);
+extern struct key_tables key_tables;
+int** ** **key_table_cmp(struct key_table *, struct key_table *);
+int** ** **key_bindings_cmp(struct key_binding *, struct key_binding
*);
+struct** ** ** ** ** key_table *key_bindings_get_table(const char *,
int);
+void** ** key_bindings_unref_table(struct key_table *);
+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
*);
@@ -1889,6 +1902,7 @@ void** ** ** **server_write_session(struct sessio
** ** ** ** ** ** **size_t);
**void** ** server_redraw_client(struct client *);
**void** ** server_status_client(struct client *);
+void** ** server_set_key_table(struct client *, const char *);
**void** ** server_redraw_session(struct session *);
**void** ** server_redraw_session_group(struct session *);
**void** ** server_status_session(struct session *);
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
Or are you thinking of creating the table after running the
set
Post by Nicholas Marriott
command?
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
This shouldn't work - you shouldn't be able to set a client to
a
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
nonexistent table.
I guess I don't super care what happens since I won't personally
be
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
writing any switch-client -T into an empty table but I had
assumed we
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
would allow switching to a new [and ephemeral] table since
otherwise
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.
I can't think of a case where I would want it to automatically
create a
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
table, except to confuse people who make a typo in the table name.
Do
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
you have a use in mind?
Not specifically, I just prefer to see tools degrade what might be
described as "uniformly" where possible.** Nonuniform behaviour
makes
Post by Nicholas Marriott
Post by Keith Amling
tools dangerous for automated use because when writing a script
you're
Post by Nicholas Marriott
Post by Keith Amling
unlikely to think of things like "what if this table is empty" or
"what
Post by Nicholas Marriott
Post by Keith Amling
if this command barfs on no args when really no args means I just
didn't
Post by Nicholas Marriott
Post by Keith Amling
need it to do anything".
I'll switch it and the next set of patches will be up shortly.
Keith
--------------------------------------------------------------------------
Post by Nicholas Marriott
----
Post by Keith Amling
"Accelerate Dev Cycles with Automated Cross-Browser Testing - For
FREE
Post by Nicholas Marriott
Post by Keith Amling
Instantly run your Selenium tests across 300+ browser/OS combos.
Get unparalleled scalability from the best Selenium testing platform
available
Post by Keith Amling
Simple to use. Nothing to install. Get started now for free."
[4]http://p.sf.net/sfu/SauceLabs
Has work on this continued?** I made an issue about it
([5]https://sourceforge.net/p/tmux/tickets/172/) in an effort to bring
attention to this patch, but Thomas said it's pretty much not going
anywhere
Post by Nicholas Marriott
unless the original patch author continues or makes an effort to get
it
Post by Nicholas Marriott
merged himself.
I would love to see this landed in tmux!** My current workaround can
be seen
Post by Nicholas Marriott
at
[6]https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
Post by Nicholas Marriott
and
[7]https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
Post by Nicholas Marriott
cords.sh.
------------------------------------------------------------------------------
Post by Nicholas Marriott
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live
exercises
Post by Nicholas Marriott
[8]http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual-
event?utm_
Post by Nicholas Marriott
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
tmux-users mailing list
[10]https://lists.sourceforge.net/lists/listinfo/tmux-users
References
Visible links
2. http://table_search.name/
3. http://palantir.com/
4. http://p.sf.net/sfu/SauceLabs
5. https://sourceforge.net/p/tmux/tickets/172/
6. https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
7. https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
8. http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual-
10. https://lists.sourceforge.net/lists/listinfo/tmux-users
Nicholas Marriott
2015-04-20 15:41:53 UTC
Permalink
I applied this to OpenBSD now; it'll be in SF later sometime.
Post by Nicholas Marriott
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
Or are you thinking of creating the table after running the set
command?
Post by Keith Amling
Post by Nicholas Marriott
Post by Nicholas Marriott
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
I guess I don't super care what happens since I won't personally be
writing any switch-client -T into an empty table but I had assumed we
would allow switching to a new [and ephemeral] table since otherwise
switch-client -T can fail which just seems weird to me.
I'd say it should fail if the table doesn't exist.
I can't think of a case where I would want it to automatically create a
table, except to confuse people who make a typo in the table name. Do
you have a use in mind?
Not specifically, I just prefer to see tools degrade what might be
described as "uniformly" where possible. Nonuniform behaviour makes
tools dangerous for automated use because when writing a script you're
unlikely to think of things like "what if this table is empty" or "what
if this command barfs on no args when really no args means I just didn't
need it to do anything".
I'll switch it and the next set of patches will be up shortly.
Keith
--------------------------------------------------------------------------
----
Post by Keith Amling
"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
Post by Keith Amling
Simple to use. Nothing to install. Get started now for free."
http://p.sf.net/sfu/SauceLabs
Has work on this continued? I made an issue about it
(https://sourceforge.net/p/tmux/tickets/172/) in an effort to bring
attention to this patch, but Thomas said it's pretty much not going anywhere
unless the original patch author continues or makes an effort to get it
merged himself.
I would love to see this landed in tmux! My current workaround can be seen
at https://github.com/rosshadden/dotfiles/blob/master/src/.tmux.conf#L79-L85
and https://github.com/rosshadden/dotfiles/blob/master/src/lib/tmux-
cords.sh.
------------------------------------------------------------------------------
BPM Camp - Free Virtual Workshop May 6th at 10am PDT/1PM EDT
Develop your own process in accordance with the BPMN 2 standard
Learn Process modeling best practices with Bonita BPM through live exercises
http://www.bonitasoft.com/be-part-of-it/events/bpm-camp-virtual- event?utm_
source=Sourceforge_BPM_Camp_5_6_15&utm_medium=email&utm_campaign=VA_SF
_______________________________________________
tmux-users mailing list
https://lists.sourceforge.net/lists/listinfo/tmux-users
Keith Amling
2014-05-13 22:42:05 UTC
Permalink
Post by Nicholas Marriott
Well, I agree it needs to be destroyed eventually but the issue is that
if a user thinks of the client as being set to [read from] a keytable
with a name then they will likely think binding a key into a keytable of
that name will be the same table even if the bind happens after the set.
This will still work if the key table is a pointer, both the bind and
the set will use the same key table.
Or are you thinking of creating the table after running the set command?
This shouldn't work - you shouldn't be able to set a client to a
nonexistent table.
I guess I don't super care what happens since I won't personally be
writing any switch-client -T into an empty table but I had assumed we
would allow switching to a new [and ephemeral] table since otherwise
switch-client -T can fail which just seems weird to me.

In many ways it is a different view of things. I was thinking of key
bindings as now indexed by the table and the key rather than just the
key whereas the direction this patch is heading is that the table itself
as a map is an exposed part of the model rather than an implementation
detail.

That said, reiterate your commitment once more to failing switch-client
-T into an empty table and I'm happy to do it.
Post by Nicholas Marriott
My opinion about safety is completely countered by ref counting (it was
the final unbind's deletion having to know about and clean up
outstanding references from clients that was worrying me) and the
weirdness with stale keytables of duplicate names isn't that big of a
deal for the sanity we're gonna save in code.
They don't need to know about the specific references from clients, they
can check every client in the clients list. All clients will be in the
list and nobody will have more than at most a few hundred so walking
them all would be fine.
Or reference counting the tables would work too if you prefer.
Reference counting it is for sure. Performance wasn't really my
concern, the spooky ownership-at-a-distance was. It just seems fragile
for clients to hold a reference they don't really own and have the
actual owner (key-bindings.c) have to be aware of these extra
references. With reference counting it's entirely moot though.

Keith
Keith Amling
2014-05-13 20:41:07 UTC
Permalink
The various things I didn't quote are all straight-forward and will be
my pleasure to fix.
Post by Nicholas Marriott
I don't think it'd be that much compatibility code.
We could just maintain a flag for the "option-set" prefix in the root
table, and that can be used for prefix/send-prefix. The root table can
never be deleted so that isn't an issue.
prefix2 can go away once there is an alternative.
Still, if you want to make the other changes first I will look again and
we can decide on this later.
Sorry, I'm obviously falling behind here; I just don't follow what
you're proposing with enough clarity to implement it. Are you
suggesting another KEYC_* flag (or two?) for "this is the binding set by
`set-option prefix[2]`"? Are you proposing an extra field (or two?) in
key_bindings to hold the prefix in the root table? I don't mean to be a
drag and I'd rather do what I can to minimize your work explaining your
vision to chumps and/or having to clean up our crappy patches but I'm
just not following this one.

I'll clean the patch up for the other stuff first anyway.
Post by Nicholas Marriott
Post by Nicholas Marriott
- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).
I agree xstrdup/free all over the place is a mess but keeping a pointer
to an actual table seems like an accident waiting to happen in terms of
safety.
You could say this about any pointer.
Additionally it provides weirdness if e.g. a user sets a
keytable, then unbinds the last key in the keytable and rebinds it
(causing the client to point to a stale empty keytable).
I don't see that this will be able to happen - when the user unbinds the
last key the table needs to be destroyed, at that point the client
should be fixed up (or the table destroy deferred until the client is
finished with it).
Well, I agree it needs to be destroyed eventually but the issue is that
if a user thinks of the client as being set to [read from] a keytable
with a name then they will likely think binding a key into a keytable of
that name will be the same table even if the bind happens after the set.

My opinion about safety is completely countered by ref counting (it was
the final unbind's deletion having to know about and clean up
outstanding references from clients that was worrying me) and the
weirdness with stale keytables of duplicate names isn't that big of a
deal for the sanity we're gonna save in code.

Keith
Keith Amling
2014-05-13 05:09:53 UTC
Permalink
Post by Nicholas Marriott
There should be no need for a prefix key anymore. Instead, the default
root table should have a binding for C-b to change the key table.
That way the prefix table is not special, the only special table is the
root table.
(Although we'll need some compatibility goo to keep the prefix option
and send-prefix working.)
I had considered that but turned it down to the [in my opinion]
excessive amount of goo that would be required to deal with it. In such
a world there isn't a prefix key, there aren't two prefix keys (there's
apparently already a "prefix2"!), there's just whatever the heck you
decided to bind or unbind. For what it's worth I think such a world is
strictly superior architecturally but I just don't see it net worth it
with the terrible backwards compatibility mess included.

This is by far to me the most unclear concern in terms of what to do
about it. I just don't see a way to translate an arbitrary collection
of key bindings in to a legacy "prefix[2]" option view. Are we to
special case a read of the "prefix" option to return the first root key
binding for "set-keytable prefix" it can find (and "prefix2" to return
the 2nd)? Presumably setting "prefix" will add the binding and delete
the old value's binding (as long as "prefix2" isn't set to the same)?
I'm just not seeing it.
Post by Nicholas Marriott
- We should make set-keytable an argument to bind-key or (maybe better)
switch-client rather than a new command.
I'm not sure I personally agree. This seems like heading in the
direction of git's "one command does fifteen barely-related things".
That said it is your project and I'm happy to hack it that way if that's
your final call.
Post by Nicholas Marriott
- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).
I agree xstrdup/free all over the place is a mess but keeping a pointer
to an actual table seems like an accident waiting to happen in terms of
safety. Additionally it provides weirdness if e.g. a user sets a
keytable, then unbinds the last key in the keytable and rebinds it
(causing the client to point to a stale empty keytable). Maybe move
tablename getting/setting into functions to abstract it away? Then we
can even do something fancy like have canonical non-managed copies of
the "ROOT" and "PREFIX" strings.
Post by Nicholas Marriott
- I don't like the table names in uppercase, "root" and "prefix" would
be better.
As you wish. While you're at it if you want them named something
different entirely that would be reasonable, I pretty much just picked
the two strings off the cuff.
Post by Nicholas Marriott
- I think it is safe to remove dead_key_bindings now but please do it as
a separate diff. Likewise I would pull the mode-key renaming bits out
separately.
Oh, yeah. I had originally made the first a separate change on my local
branch but then just smooshed it all to send without thinking about it.

I'm also curious what the history on that is, it wasn't clear to me why
that was there in the first place.
Post by Nicholas Marriott
- You will save me a little time if you use "for (;;)" instead of "while
(1)".
Uh, as you wish although I'm not sure I understand the subtext. Just
for my curiosity is it just that you prefer "for (;;)"?

Keith
Post by Nicholas Marriott
Date: Tue, 13 May 2014 02:04:42 +0100
Subject: Re: [PATCH] Allow custom key tables for e.g. multiple keystroke
bindings.
Thanks - I think this is the right idea, but not quite there.
There should be no need for a prefix key anymore. Instead, the default
root table should have a binding for C-b to change the key table.
That way the prefix table is not special, the only special table is the
root table.
(Although we'll need some compatibility goo to keep the prefix option
and send-prefix working.)
- We should make set-keytable an argument to bind-key or (maybe better)
switch-client rather than a new command.
- I think it would be better to point the client to a struct
key_bindings, not to a string with the name. I know it won't save the
lookup but it's neater than doing xstrdup/free all over the
place. When a table is deleted you can walk the clients and reset them
(or alternatively, reference count).
- I don't like the table names in uppercase, "root" and "prefix" would
be better.
- I think it is safe to remove dead_key_bindings now but please do it as
a separate diff. Likewise I would pull the mode-key renaming bits out
separately.
- You will save me a little time if you use "for (;;)" instead of "while
(1)".
Post by Keith Amling
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$ */
+
+/*
+ *
+ * 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
.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
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
+.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
+.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
------------------------------------------------------------------------------
"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."
https://urldefense.proofpoint.com/v1/url?u=http://p.sf.net/sfu/SauceLabs&k=fDZpZZQMmYwf27OU23GmAQ%3D%3D%0A&r=BTdtcPIZXD8V7r6BhVE1Cy1S1ITG2lF8LZPYHbBpv%2B0%3D%0A&m=bi1uWxNPLiV%2BM52G7zv4qsFMczpysaL4AG0%2B1C7o1%2BA%3D%0A&s=c4a65022a4d6b8349d946735d01b625ad7de4b11b6efb75ba34521c9be5196a7
_______________________________________________
tmux-users mailing list
https://urldefense.proofpoint.com/v1/url?u=https://lists.sourceforge.net/lists/listinfo/tmux-users&k=fDZpZZQMmYwf27OU23GmAQ%3D%3D%0A&r=BTdtcPIZXD8V7r6BhVE1Cy1S1ITG2lF8LZPYHbBpv%2B0%3D%0A&m=bi1uWxNPLiV%2BM52G7zv4qsFMczpysaL4AG0%2B1C7o1%2BA%3D%0A&s=27481cdf90a90d18d51c90ade176c49c5f38f3831394fe992fd0a0ad99b81223
Loading...