Support for multiple key tables to commands to be bound to sequences of

keys. The default key bindings become the "prefix" table and -n the
"root" table. Keys may be bound in new tables with bind -T and
switch-client -T used to specify the table in which the next key should
be looked up. Based on a diff from Keith Amling.
This commit is contained in:
nicm 2015-04-20 15:34:56 +00:00
parent 3497843f02
commit bded743706
9 changed files with 394 additions and 197 deletions

View File

@ -33,8 +33,8 @@ enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int);
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, struct cmd_q *cmdq)
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, struct cmd_q *cmdq)
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, struct cmd_q *cmdq)
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);
}

View File

@ -33,8 +33,8 @@ enum cmd_retval cmd_list_keys_commands(struct cmd *, struct cmd_q *);
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,11 +51,12 @@ 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));
@ -63,46 +64,57 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq)
if (args_has(args, 't'))
return (cmd_list_keys_table(self, cmdq));
width = 0;
RB_FOREACH(bd, key_bindings, &key_bindings) {
key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
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;
tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(cmdq, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
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;
*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 ");
repeat = 1;
used = xsnprintf(tmp, sizeof tmp, "%s%*s ",
flags, (int) (width - strlen(flags)), key);
if (used >= sizeof tmp)
width = strlen(table->name);
if (width > tablewidth)
tablewidth =width;
width = strlen(key);
if (width > keywidth)
keywidth = width;
}
}
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;
cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
cmdq_print(cmdq, "bind-key %s", tmp);
if (!repeat)
r = "";
else if (bd->can_repeat)
r = "-r ";
else
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);
}
cmdq_print(cmdq, "bind-key %s", tmp);
}
}
return (CMD_RETURN_NORMAL);

View File

@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_switch_client_entry = {
"switch-client", "switchc",
"lc:npt:r", 0, 0,
"[-lnpr] [-c target-client] [-t target-session]",
"lc:npt:rT:", 0, 0,
"[-lnpr] [-c target-client] [-t target-session] [-T key-table]",
CMD_READONLY,
cmd_switch_client_exec
};
@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq)
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);
@ -58,6 +59,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq)
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');
if (args_has(args, 'n')) {
if ((s = session_next_session(c->session)) == NULL) {

View File

@ -31,8 +31,8 @@ enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int);
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_entry = {
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,31 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
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);
tablename = args_get(args, 'T');
if (tablename == NULL) {
key_bindings_remove_table("root");
key_bindings_remove_table("prefix");
return (CMD_RETURN_NORMAL);
}
if (key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(cmdq, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
key_bindings_remove_table(tablename);
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');
if (key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(cmdq, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'n'))
tablename = "root";
else
tablename = "prefix";
key_bindings_remove(tablename, key);
return (CMD_RETURN_NORMAL);
}

View File

@ -545,7 +545,11 @@ format_defaults_client(struct format_tree *ft, struct client *c)
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);

View File

@ -25,60 +25,120 @@
#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;
key1 = bd1->key & ~KEYC_PREFIX;
key2 = bd2->key & ~KEYC_PREFIX;
if (key1 != key2)
return (key1 - key2);
if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
return (-1);
if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
return (1);
return (0);
return (bd1->key - bd2->key);
}
struct key_binding *
key_bindings_lookup(int key)
struct key_table *
key_bindings_get_table(const char *name, int create)
{
struct key_binding bd;
struct key_table table_find, *table;
bd.key = key;
return (RB_FIND(key_bindings, &key_bindings, &bd));
table_find.name = name;
table = RB_FIND(key_tables, &key_tables, &table_find);
if (table != NULL || !create)
return (table);
table = xmalloc(sizeof *table);
table->name = xstrdup(name);
RB_INIT(&table->key_bindings);
table->references = 1; /* one reference in key_tables */
RB_INSERT(key_tables, &key_tables, table);
return (table);
}
void
key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
key_bindings_unref_table(struct key_table *table)
{
struct key_binding *bd;
key_bindings_remove(key);
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(const char *name, int key, int can_repeat,
struct cmd_list *cmdlist)
{
struct key_table *table;
struct key_binding bd_find, *bd;
table = key_bindings_get_table(name, 1);
bd_find.key = key;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
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_find, *bd;
if ((bd = key_bindings_lookup(key)) == NULL)
table = key_bindings_get_table(name, 0);
if (table == NULL)
return;
RB_REMOVE(key_bindings, &key_bindings, bd);
bd_find.key = key;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd == NULL)
return;
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) {
RB_REMOVE(key_tables, &key_tables, table);
key_bindings_unref_table(table);
}
}
void
@ -169,8 +229,6 @@ key_bindings_init(void)
int error;
struct cmd_q *cmdq;
RB_INIT(&key_bindings);
cmdq = cmdq_new(NULL);
for (i = 0; i < nitems(defaults); i++) {
error = cmd_string_parse(defaults[i], &cmdlist,

View File

@ -30,6 +30,7 @@
#include "tmux.h"
void server_client_key_table(struct client *, const char *);
void server_client_check_focus(struct window_pane *);
void server_client_check_resize(struct window_pane *);
int server_client_check_mouse(struct client *);
@ -45,6 +46,15 @@ void server_client_msg_command(struct client *, struct imsg *);
void server_client_msg_identify(struct client *, struct imsg *);
void server_client_msg_shell(struct client *);
/* Set client key table. */
void
server_client_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++;
}
/* Create a new client. */
void
server_client_create(int fd)
@ -93,6 +103,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++) {
@ -164,6 +177,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);
@ -527,16 +542,19 @@ void
server_client_handle_key(struct client *c, int key)
{
struct mouse_event *m = &c->tty.mouse;
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_find, *bd;
int xtimeout;
/* Check the client is good to accept input. */
if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
return;
w = s->curw->window;
wp = w->active;
/* No session, do nothing. */
if (c->session == NULL)
@ -552,7 +570,7 @@ server_client_handle_key(struct client *c, int key)
sizeof s->last_activity_time);
memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
/* 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;
@ -593,74 +611,88 @@ server_client_handle_key(struct client *c, int key)
} else
m->valid = 0;
/* 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 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;
server_status_client(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, c, s, key, m);
} else
key_bindings_dispatch(bd, c, m);
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) {
c->flags &= ~CLIENT_REPEAT;
if (isprefix)
c->flags |= CLIENT_PREFIX;
else if (!(c->flags & CLIENT_READONLY))
window_pane_key(wp, c, s, key, m);
}
return;
}
/* 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))
/* 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, c, s, key, m);
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;
retry:
/* Try to see if there is a key binding in the current table. */
bd_find.key = key;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
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_client_key_table(c, "root");
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
goto retry;
}
tv.tv_sec = xtimeout / 1000;
tv.tv_usec = (xtimeout % 1000) * 1000L;
evtimer_del(&c->repeat_timer);
evtimer_add(&c->repeat_timer, &tv);
/*
* 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;
server_client_key_table(c, "root");
}
server_status_client(c);
/* Dispatch the key binding. */
key_bindings_dispatch(bd, c, m);
key_bindings_unref_table(table);
return;
}
/* Dispatch the command. */
key_bindings_dispatch(bd, c, m);
/*
* No match in this table. If repeating, switch the client back to the
* root table and try again.
*/
if (c->flags & CLIENT_REPEAT) {
server_client_key_table(c, "root");
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
goto retry;
}
/* If no match and we're not in the root table, that's it. */
if (strcmp(c->keytable->name, "root") != 0) {
server_client_key_table(c, "root");
server_status_client(c);
return;
}
/*
* 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_client_key_table(c, "prefix");
server_status_client(c);
} else if (!(c->flags & CLIENT_READONLY))
window_pane_key(wp, c, s, key, m);
}
/* Client functions that need to happen every loop. */
@ -848,9 +880,9 @@ server_client_repeat_timer(unused int fd, unused short events, void *data)
struct client *c = data;
if (c->flags & CLIENT_REPEAT) {
if (c->flags & CLIENT_PREFIX)
server_status_client(c);
c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
server_client_key_table(c, "root");
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
}
}

106
tmux.1
View File

@ -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,22 @@ respectively.
toggles whether a client is read-only (see the
.Ic attach-session
command).
.Pp
.Fl T
sets the client's key table; the next key from the client will be interpreted from
.Ar key-table .
This may be used to configure multiple prefix keys, or to bind commands to
sequences of keys.
For example, to make typing
.Ql abc
run the
.Ic list-keys
command:
.Bd -literal -offset indent
bind-key -Ttable2 c list-keys
bind-key -Ttable1 b switch-client -Ttable2
bind-key -Troot a switch-client -Ttable1
.Ed
.El
.Sh WINDOWS AND PANES
A
@ -1931,6 +1948,7 @@ Commands related to key bindings are as follows:
.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 )
@ -1938,16 +1956,40 @@ Bind key
.Ar key
to
.Ar command .
By default (without
.Fl t )
the primary key bindings are modified (those normally activated with the prefix
key); in this case, if
.Fl n
is specified, it is not necessary to use the prefix key,
.Ar command
Keys are bound in a key table.
By default (without -T), the key is bound in
the
.Em prefix
key table.
This table is used for keys pressed after the prefix key (for example,
by default
.Ql c
is bound to
.Ar key
alone.
.Ic new-window
in the
.Em prefix
table, so
.Ql C-b c
creates a new window).
The
.Em root
table is used for keys pressed without the prefix key: binding
.Ql c
to
.Ic new-window
in the
.Em root
table (not recommended) means a plain
.Ql c
will create a new window.
.Fl n
is an alias
for
.Fl T Ar root .
Keys may also be bound in custom key tables and the
.Ic switch-client
.Fl T
command used to switch to them from a key binding.
The
.Fl r
flag indicates this key may repeat, see the
@ -1962,22 +2004,33 @@ is bound in
.Ar mode-table :
the binding for command mode with
.Fl c
or for normal mode without.
or for normal mode without. See the
.Sx WINDOWS AND PANES
section and the
.Ic list-keys
command for information on mode key bindings.
.Pp
To view the default bindings and possible commands, see the
.Ic list-keys
command.
.It Ic list-keys Op Fl t Ar key-table
.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.
.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 ,
@ -2022,31 +2075,22 @@ the secondary prefix key, to a window as if it was pressed.
.It Xo Ic unbind-key
.Op Fl acn
.Op Fl t Ar mode-table
.Op Fl T Ar key-table
.Ar key
.Xc
.D1 (alias: Ic unbind )
Unbind the command bound to
.Ar key .
Without
.Fl c ,
.Fl n ,
.Fl T
and
.Fl t
the primary key bindings are modified; in this case, if
.Fl n
is specified, the command bound to
.Ar key
without a prefix (if any) is removed.
are the same as for
.Ic bind-key .
If
.Fl a
is present, all key bindings are removed.
.Pp
If
.Fl t
is present,
.Ar key
in
.Ar mode-table
is unbound: the binding for command mode with
.Fl c
or for normal mode without.
.El
.Sh OPTIONS
The appearance and behaviour of

39
tmux.h
View File

@ -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)
/* Is this a mouse key? */
@ -1301,7 +1300,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
@ -1320,6 +1319,7 @@ struct client {
#define CLIENT_256COLOURS 0x20000
#define CLIENT_IDENTIFIED 0x40000
int flags;
struct key_table *keytable;
struct event identify_timer;
@ -1440,15 +1440,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
@ -1876,12 +1885,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 *,
struct mouse_event *);