summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server-client.c280
-rw-r--r--tmux.h6
-rw-r--r--tty-keys.c91
-rw-r--r--tty.c13
4 files changed, 210 insertions, 180 deletions
diff --git a/server-client.c b/server-client.c
index 5c15d502..dd386001 100644
--- a/server-client.c
+++ b/server-client.c
@@ -27,10 +27,11 @@
#include "tmux.h"
-void server_client_handle_data(struct client *);
+void server_client_handle_key(int, struct mouse_event *, void *);
void server_client_repeat_timer(int, short, void *);
void server_client_check_redraw(struct client *);
void server_client_set_title(struct client *);
+void server_client_reset_state(struct client *);
int server_client_msg_dispatch(struct client *);
void server_client_msg_command(struct client *, struct msg_command_data *);
@@ -226,6 +227,127 @@ server_client_status_timer(void)
}
}
+/* Handle data key input from client. */
+void
+server_client_handle_key(int key, struct mouse_event *mouse, void *data)
+{
+ struct client *c = data;
+ struct session *s;
+ struct window *w;
+ struct window_pane *wp;
+ struct options *oo;
+ struct timeval tv;
+ struct key_binding *bd;
+ struct keylist *keylist;
+ int xtimeout, isprefix;
+ u_int i;
+
+ /* Check the client is good to accept input. */
+ if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
+ return;
+ if (c->session == NULL)
+ return;
+ s = c->session;
+
+ /* Update the activity timer. */
+ if (gettimeofday(&c->activity_time, NULL) != 0)
+ fatal("gettimeofday failed");
+ memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
+
+ w = c->session->curw->window;
+ wp = w->active;
+ oo = &c->session->options;
+
+ /* Special case: number keys jump to pane in identify mode. */
+ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
+ wp = window_pane_at_index(w, key - '0');
+ if (wp != NULL && window_pane_visible(wp))
+ window_set_active_pane(w, wp);
+ server_clear_identify(c);
+ return;
+ }
+
+ /* Handle status line. */
+ status_message_clear(c);
+ server_clear_identify(c);
+ if (c->prompt_string != NULL) {
+ status_prompt_key(c, key);
+ return;
+ }
+
+ /* Check for mouse keys. */
+ if (key == KEYC_MOUSE) {
+ if (options_get_number(oo, "mouse-select-pane")) {
+ window_set_active_at(w, mouse->x, mouse->y);
+ wp = w->active;
+ }
+ window_pane_mouse(wp, c, mouse);
+ return;
+ }
+
+ /* Is this a prefix key? */
+ keylist = options_get_data(&c->session->options, "prefix");
+ isprefix = 0;
+ for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
+ if (key == ARRAY_ITEM(keylist, i)) {
+ isprefix = 1;
+ break;
+ }
+ }
+
+ /* No previous prefix key. */
+ if (!(c->flags & CLIENT_PREFIX)) {
+ if (isprefix)
+ c->flags |= CLIENT_PREFIX;
+ else {
+ /* Try as a non-prefix key binding. */
+ if ((bd = key_bindings_lookup(key)) == NULL)
+ window_pane_key(wp, c, key);
+ else
+ key_bindings_dispatch(bd, c);
+ }
+ return;
+ }
+
+ /* Prefix key already pressed. Reset prefix and lookup key. */
+ c->flags &= ~CLIENT_PREFIX;
+ 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
+ window_pane_key(wp, c, key);
+ }
+ 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
+ window_pane_key(wp, c, key);
+ return;
+ }
+
+ /* If this key can repeat, reset the repeat flags and timer. */
+ xtimeout = options_get_number(&c->session->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);
+ }
+
+ /* Dispatch the command. */
+ key_bindings_dispatch(bd, c);
+}
+
/* Client functions that need to happen every loop. */
void
server_client_loop(void)
@@ -240,9 +362,8 @@ server_client_loop(void)
if (c == NULL || c->session == NULL)
continue;
- server_client_handle_data(c);
- if (c->session != NULL)
- server_client_check_redraw(c);
+ server_client_check_redraw(c);
+ server_client_reset_state(c);
}
/*
@@ -260,143 +381,24 @@ server_client_loop(void)
}
}
-/* Handle data input or output from client. */
+/*
+ * Update cursor position and mode settings. The scroll region and attributes
+ * are cleared when idle (waiting for an event) as this is the most likely time
+ * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
+ * compromise between excessive resets and likelihood of an interrupt.
+ *
+ * tty_region/tty_reset/tty_update_mode already take care of not resetting
+ * things that are already in their default state.
+ */
void
-server_client_handle_data(struct client *c)
+server_client_reset_state(struct client *c)
{
- struct window *w;
- struct window_pane *wp;
- struct screen *s;
- struct options *oo;
- struct timeval tv, tv_now;
- struct key_binding *bd;
- struct keylist *keylist;
- struct mouse_event mouse;
- int key, status, xtimeout, mode, isprefix;
- u_int i;
-
- /* Get the time for the activity timer. */
- if (gettimeofday(&tv_now, NULL) != 0)
- fatal("gettimeofday failed");
- xtimeout = options_get_number(&c->session->options, "repeat-time");
-
- /* Process keys. */
- keylist = options_get_data(&c->session->options, "prefix");
- while (tty_keys_next(&c->tty, &key, &mouse) == 0) {
- if (c->session == NULL)
- return;
- w = c->session->curw->window;
- wp = w->active; /* could die */
- oo = &c->session->options;
-
- /* Update activity timer. */
- memcpy(&c->activity_time, &tv_now, sizeof c->activity_time);
- memcpy(&c->session->activity_time,
- &tv_now, sizeof c->session->activity_time);
-
- /* Special case: number keys jump to pane in identify mode. */
- if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
- wp = window_pane_at_index(w, key - '0');
- if (wp != NULL && window_pane_visible(wp))
- window_set_active_pane(w, wp);
- server_clear_identify(c);
- continue;
- }
-
- status_message_clear(c);
- server_clear_identify(c);
- if (c->prompt_string != NULL) {
- status_prompt_key(c, key);
- continue;
- }
+ struct window *w = c->session->curw->window;
+ struct window_pane *wp = w->active;
+ struct screen *s = wp->screen;
+ struct options *oo = &c->session->options;
+ int status, mode;
- /* Check for mouse keys. */
- if (key == KEYC_MOUSE) {
- if (options_get_number(oo, "mouse-select-pane")) {
- window_set_active_at(w, mouse.x, mouse.y);
- wp = w->active;
- }
- window_pane_mouse(wp, c, &mouse);
- continue;
- }
-
- /* Is this a prefix key? */
- isprefix = 0;
- for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
- if (key == ARRAY_ITEM(keylist, i)) {
- isprefix = 1;
- break;
- }
- }
-
- /* No previous prefix key. */
- if (!(c->flags & CLIENT_PREFIX)) {
- if (isprefix)
- c->flags |= CLIENT_PREFIX;
- else {
- /* Try as a non-prefix key binding. */
- if ((bd = key_bindings_lookup(key)) == NULL)
- window_pane_key(wp, c, key);
- else
- key_bindings_dispatch(bd, c);
- }
- continue;
- }
-
- /* Prefix key already pressed. Reset prefix and lookup key. */
- c->flags &= ~CLIENT_PREFIX;
- 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
- window_pane_key(wp, c, key);
- }
- continue;
- }
-
- /* 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
- window_pane_key(wp, c, key);
- continue;
- }
-
- /* If this key can repeat, reset the repeat flags and timer. */
- 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);
- }
-
- /* Dispatch the command. */
- key_bindings_dispatch(bd, c);
- }
- if (c->session == NULL)
- return;
- w = c->session->curw->window;
- wp = w->active;
- oo = &c->session->options;
- s = wp->screen;
-
- /*
- * Update cursor position and mode settings. The scroll region and
- * attributes are cleared across poll(2) as this is the most likely
- * time a user may interrupt tmux, for example with ~^Z in ssh(1). This
- * is a compromise between excessive resets and likelihood of an
- * interrupt.
- *
- * tty_region/tty_reset/tty_update_mode already take care of not
- * resetting things that are already in their default state.
- */
tty_region(&c->tty, 0, c->tty.sy - 1);
status = options_get_number(oo, "status");
@@ -715,6 +717,8 @@ server_client_msg_identify(
c->tty.term_flags |= TERM_256COLOURS;
else if (data->flags & IDENTIFY_88COLOURS)
c->tty.term_flags |= TERM_88COLOURS;
+ c->tty.key_callback = server_client_handle_key;
+ c->tty.key_data = c;
tty_resize(&c->tty);
diff --git a/tmux.h b/tmux.h
index f3263f4a..f1b0f7ac 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1002,7 +1002,9 @@ struct tty {
int term_flags;
- struct timeval key_timer;
+ void (*key_callback)(int, struct mouse_event *, void *);
+ void *key_data;
+ struct event key_timer;
size_t ksize; /* maximum key size */
RB_HEAD(tty_keys, tty_key) ktree;
@@ -1360,7 +1362,7 @@ int tty_keys_cmp(struct tty_key *, struct tty_key *);
RB_PROTOTYPE(tty_keys, tty_key, entry, tty_keys_cmp);
void tty_keys_init(struct tty *);
void tty_keys_free(struct tty *);
-int tty_keys_next(struct tty *, int *, struct mouse_event *);
+int tty_keys_next(struct tty *);
/* options-cmd.c */
const char *set_option_print(
diff --git a/tty-keys.c b/tty-keys.c
index 995f2994..c5c14541 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -30,6 +30,7 @@
*/
void tty_keys_add(struct tty *, const char *, int, int);
+void tty_keys_callback(int, short, void *);
int tty_keys_mouse(char *, size_t, size_t *, struct mouse_event *);
struct tty_key_ent {
@@ -294,25 +295,27 @@ tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size)
}
int
-tty_keys_next(struct tty *tty, int *key, struct mouse_event *mouse)
+tty_keys_next(struct tty *tty)
{
- struct tty_key *tk;
- struct timeval tv;
- char *buf;
- u_char ch;
- size_t len, size;
- cc_t bspace;
+ struct tty_key *tk;
+ struct timeval tv;
+ struct mouse_event mouse;
+ char *buf;
+ size_t len, size;
+ cc_t bspace;
+ int key;
+ u_char ch;
buf = EVBUFFER_DATA(tty->event->input);
len = EVBUFFER_LENGTH(tty->event->input);
if (len == 0)
- return (1);
+ return (0);
log_debug("keys are %zu (%.*s)", len, (int) len, buf);
/* If a normal key, return it. */
if (*buf != '\033') {
bufferevent_read(tty->event, &ch, 1);
- *key = ch;
+ key = ch;
/*
* Check for backspace key using termios VERASE - the terminfo
@@ -320,8 +323,8 @@ tty_keys_next(struct tty *tty, int *key, struct mouse_event *mouse)
* used. termios should have a better idea.
*/
bspace = tty->tio.c_cc[VERASE];
- if (bspace != _POSIX_VDISABLE && *key == bspace)
- *key = KEYC_BSPACE;
+ if (bspace != _POSIX_VDISABLE && key == bspace)
+ key = KEYC_BSPACE;
goto found;
}
@@ -329,36 +332,24 @@ tty_keys_next(struct tty *tty, int *key, struct mouse_event *mouse)
tk = tty_keys_find(tty, buf + 1, len - 1, &size);
if (tk != NULL) {
evbuffer_drain(tty->event->input, size + 1);
- *key = tk->key;
+ key = tk->key;
goto found;
}
/* Not found. Is this a mouse key press? */
- *key = tty_keys_mouse(buf, len, &size, mouse);
- if (*key != KEYC_NONE) {
+ key = tty_keys_mouse(buf, len, &size, &mouse);
+ if (key != KEYC_NONE) {
evbuffer_drain(tty->event->input, size);
goto found;
}
/* Not found. Try to parse a key with an xterm-style modifier. */
- *key = xterm_keys_find(buf, len, &size);
- if (*key != KEYC_NONE) {
+ key = xterm_keys_find(buf, len, &size);
+ if (key != KEYC_NONE) {
evbuffer_drain(tty->event->input, size);
goto found;
}
- /* Escape but no key string. If the timer isn't started, start it. */
- if (!(tty->flags & TTY_ESCAPE)) {
- tv.tv_sec = 0;
- tv.tv_usec = ESCAPE_PERIOD * 1000L;
- if (gettimeofday(&tty->key_timer, NULL) != 0)
- fatal("gettimeofday failed");
- timeradd(&tty->key_timer, &tv, &tty->key_timer);
-
- tty->flags |= TTY_ESCAPE;
- return (1);
- }
-
/* Skip the escape. */
buf++;
len--;
@@ -367,7 +358,7 @@ tty_keys_next(struct tty *tty, int *key, struct mouse_event *mouse)
if (len != 0 && *buf != '\033') {
evbuffer_drain(tty->event->input, 1);
bufferevent_read(tty->event, &ch, 1);
- *key = ch | KEYC_ESCAPE;
+ key = ch | KEYC_ESCAPE;
goto found;
}
@@ -376,24 +367,46 @@ tty_keys_next(struct tty *tty, int *key, struct mouse_event *mouse)
tk = tty_keys_find(tty, buf + 1, len - 1, &size);
if (tk != NULL) {
evbuffer_drain(tty->event->input, size + 2);
- *key = tk->key | KEYC_ESCAPE;
+ key = tk->key | KEYC_ESCAPE;
goto found;
}
}
- /* If the timer hasn't expired, keep waiting. */
- if (gettimeofday(&tv, NULL) != 0)
- fatal("gettimeofday failed");
- if (timercmp(&tty->key_timer, &tv, >))
- return (1);
+ /*
+ * Escape but no key string. If have, already seen an escape, then the
+ * timer must have expired, so give up waiting and send the escape.
+ */
+ if (tty->flags & TTY_ESCAPE) {
+ evbuffer_drain(tty->event->input, 1);
+ key = '\033';
+ goto found;
+ }
- /* Give up and return the escape. */
- evbuffer_drain(tty->event->input, 1);
- *key = '\033';
+ /* Start the timer and wait for expiry or more data. */
+ tv.tv_sec = 0;
+ tv.tv_usec = ESCAPE_PERIOD * 1000L;
+
+ evtimer_del(&tty->key_timer);
+ evtimer_set(&tty->key_timer, tty_keys_callback, tty);
+ evtimer_add(&tty->key_timer, &tv);
+
+ tty->flags |= TTY_ESCAPE;
+ return (0);
found:
+ evtimer_del(&tty->key_timer);
+ tty->key_callback(key, &mouse, tty->key_data);
tty->flags &= ~TTY_ESCAPE;
- return (0);
+ return (1);
+}
+
+void
+tty_keys_callback(unused int fd, unused short events, void *data)
+{
+ struct tty *tty = data;
+
+ if (tty->flags & TTY_ESCAPE)
+ tty_keys_next(tty);
}
int
diff --git a/tty.c b/tty.c
index e194febb..6ab3a211 100644
--- a/tty.c
+++ b/tty.c
@@ -28,6 +28,7 @@
#include "tmux.h"
+void tty_read_callback(struct bufferevent *, void *);
void tty_error_callback(struct bufferevent *, short, void *);
void tty_fill_acs(struct tty *);
@@ -113,7 +114,7 @@ tty_open(struct tty *tty, const char *overrides, char **cause)
tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE);
tty->event = bufferevent_new(
- tty->fd, NULL, NULL, tty_error_callback, tty);
+ tty->fd, tty_read_callback, NULL, tty_error_callback, tty);
tty_start_tty(tty);
@@ -125,6 +126,15 @@ tty_open(struct tty *tty, const char *overrides, char **cause)
}
void
+tty_read_callback(unused struct bufferevent *bufev, void *data)
+{
+ struct tty *tty = data;
+
+ while (tty_keys_next(tty))
+ ;
+}
+
+void
tty_error_callback(
unused struct bufferevent *bufev, unused short what, unused void *data)
{
@@ -259,6 +269,7 @@ tty_close(struct tty *tty)
tty->log_fd = -1;
}
+ evtimer_del(&tty->key_timer);
tty_stop_tty(tty);
if (tty->flags & TTY_OPENED) {