=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tmux/server-client.c,v retrieving revision 1.131 retrieving revision 1.132 diff -u -r1.131 -r1.132 --- src/usr.bin/tmux/server-client.c 2015/04/19 21:34:21 1.131 +++ src/usr.bin/tmux/server-client.c 2015/04/20 15:34:56 1.132 @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.131 2015/04/19 21:34:21 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.132 2015/04/20 15:34:56 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 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; + } - /* 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_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; - } + /* + * Take a reference to this table to make sure the key binding + * doesn't disappear. + */ + table->references++; - /* 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) { + /* + * 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 @@ 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); } }