aboutsummaryrefslogtreecommitdiff
path: root/server-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'server-client.c')
-rw-r--r--server-client.c156
1 files changed, 94 insertions, 62 deletions
diff --git a/server-client.c b/server-client.c
index 3968c50b..b4d1f744 100644
--- a/server-client.c
+++ b/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;
+ /* 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;
+ }
- /* 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_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);
- 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, 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) {
+ /*
+ * 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, c, s, key, m);
+ 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;
}
- /* 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_client_key_table(c, "root");
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;
+ 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_client_key_table(c, "root");
+ server_status_client(c);
+ return;
}
- /* Dispatch the command. */
- key_bindings_dispatch(bd, c, m);
+ /*
+ * 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);
}
}