mirror of
https://github.com/tmux/tmux.git
synced 2025-09-02 13:37:12 +00:00
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:
170
server-client.c
170
server-client.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user