mirror of
https://github.com/tmux/tmux.git
synced 2024-11-08 04:08:49 +00:00
Sync OpenBSD patchset 508:
Switch tty key input over to happen on a read event. This is a bit more complicated because of escape input, but in that case instead of processing a key immediately, schedule a timer and reprocess the bufer when it expires. This currently assumes that keys will be atomic (ie that if eg F1 is pressed the entire sequence is present in the buffer). This is usually but not always true, a change in the tree format so it can differentiate potential (partial) key sequences will happens soon and will allow this to be fixed.
This commit is contained in:
parent
ac6092c27f
commit
66957412d5
162
server-client.c
162
server-client.c
@ -1,4 +1,4 @@
|
||||
/* $Id: server-client.c,v 1.18 2009-11-08 23:24:59 tcunha Exp $ */
|
||||
/* $Id: server-client.c,v 1.19 2009-11-08 23:26:56 tcunha Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
@ -26,10 +26,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 *);
|
||||
@ -225,73 +226,36 @@ server_client_status_timer(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Client functions that need to happen every loop. */
|
||||
/* Handle data key input from client. */
|
||||
void
|
||||
server_client_loop(void)
|
||||
server_client_handle_key(int key, struct mouse_event *mouse, void *data)
|
||||
{
|
||||
struct client *c;
|
||||
struct client *c = data;
|
||||
struct session *s;
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
u_int i;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
|
||||
c = ARRAY_ITEM(&clients, i);
|
||||
if (c == NULL || c->session == NULL)
|
||||
continue;
|
||||
|
||||
server_client_handle_data(c);
|
||||
if (c->session != NULL)
|
||||
server_client_check_redraw(c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Any windows will have been redrawn as part of clients, so clear
|
||||
* their flags now.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
|
||||
w = ARRAY_ITEM(&windows, i);
|
||||
if (w == NULL)
|
||||
continue;
|
||||
|
||||
w->flags &= ~WINDOW_REDRAW;
|
||||
TAILQ_FOREACH(wp, &w->panes, entry)
|
||||
wp->flags &= ~PANE_REDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle data input or output from client. */
|
||||
void
|
||||
server_client_handle_data(struct client *c)
|
||||
{
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
struct screen *s;
|
||||
struct options *oo;
|
||||
struct timeval tv, tv_now;
|
||||
struct timeval tv;
|
||||
struct key_binding *bd;
|
||||
struct keylist *keylist;
|
||||
struct mouse_event mouse;
|
||||
int key, status, xtimeout, mode, isprefix;
|
||||
int xtimeout, 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) {
|
||||
/* Check the client is good to accept input. */
|
||||
if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
|
||||
return;
|
||||
if (c->session == NULL)
|
||||
return;
|
||||
w = c->session->curw->window;
|
||||
wp = w->active; /* could die */
|
||||
oo = &c->session->options;
|
||||
s = c->session;
|
||||
|
||||
/* 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);
|
||||
/* 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') {
|
||||
@ -299,27 +263,29 @@ server_client_handle_data(struct client *c)
|
||||
if (wp != NULL && window_pane_visible(wp))
|
||||
window_set_active_pane(w, wp);
|
||||
server_clear_identify(c);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle status line. */
|
||||
status_message_clear(c);
|
||||
server_clear_identify(c);
|
||||
if (c->prompt_string != NULL) {
|
||||
status_prompt_key(c, key);
|
||||
continue;
|
||||
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);
|
||||
window_set_active_at(w, mouse->x, mouse->y);
|
||||
wp = w->active;
|
||||
}
|
||||
window_pane_mouse(wp, c, &mouse);
|
||||
continue;
|
||||
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)) {
|
||||
@ -339,7 +305,7 @@ server_client_handle_data(struct client *c)
|
||||
else
|
||||
key_bindings_dispatch(bd, c);
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prefix key already pressed. Reset prefix and lookup key. */
|
||||
@ -353,7 +319,7 @@ server_client_handle_data(struct client *c)
|
||||
else
|
||||
window_pane_key(wp, c, key);
|
||||
}
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If already repeating, but this key can't repeat, skip it. */
|
||||
@ -363,10 +329,11 @@ server_client_handle_data(struct client *c)
|
||||
c->flags |= CLIENT_PREFIX;
|
||||
else
|
||||
window_pane_key(wp, c, key);
|
||||
continue;
|
||||
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;
|
||||
|
||||
@ -378,24 +345,59 @@ server_client_handle_data(struct client *c)
|
||||
|
||||
/* Dispatch the command. */
|
||||
key_bindings_dispatch(bd, c);
|
||||
}
|
||||
|
||||
/* Client functions that need to happen every loop. */
|
||||
void
|
||||
server_client_loop(void)
|
||||
{
|
||||
struct client *c;
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
u_int i;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
|
||||
c = ARRAY_ITEM(&clients, i);
|
||||
if (c == NULL || c->session == NULL)
|
||||
continue;
|
||||
|
||||
server_client_check_redraw(c);
|
||||
server_client_reset_state(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.
|
||||
* Any windows will have been redrawn as part of clients, so clear
|
||||
* their flags now.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
|
||||
w = ARRAY_ITEM(&windows, i);
|
||||
if (w == NULL)
|
||||
continue;
|
||||
|
||||
w->flags &= ~WINDOW_REDRAW;
|
||||
TAILQ_FOREACH(wp, &w->panes, entry)
|
||||
wp->flags &= ~PANE_REDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_reset_state(struct client *c)
|
||||
{
|
||||
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;
|
||||
|
||||
tty_region(&c->tty, 0, c->tty.sy - 1);
|
||||
|
||||
status = options_get_number(oo, "status");
|
||||
@ -714,6 +716,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);
|
||||
|
||||
|
8
tmux.h
8
tmux.h
@ -1,4 +1,4 @@
|
||||
/* $Id: tmux.h,v 1.507 2009-11-08 23:24:59 tcunha Exp $ */
|
||||
/* $Id: tmux.h,v 1.508 2009-11-08 23:26:56 tcunha Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
@ -1001,7 +1001,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;
|
||||
@ -1359,7 +1361,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(
|
||||
|
83
tty-keys.c
83
tty-keys.c
@ -1,4 +1,4 @@
|
||||
/* $Id: tty-keys.c,v 1.39 2009-11-08 22:58:38 tcunha Exp $ */
|
||||
/* $Id: tty-keys.c,v 1.40 2009-11-08 23:26:56 tcunha Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
@ -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;
|
||||
struct mouse_event mouse;
|
||||
char *buf;
|
||||
u_char ch;
|
||||
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);
|
||||
|
||||
/* Give up and return the escape. */
|
||||
/*
|
||||
* 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';
|
||||
key = '\033';
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
15
tty.c
15
tty.c
@ -1,4 +1,4 @@
|
||||
/* $Id: tty.c,v 1.170 2009-11-08 22:58:38 tcunha Exp $ */
|
||||
/* $Id: tty.c,v 1.171 2009-11-08 23:26:56 tcunha Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
@ -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);
|
||||
|
||||
@ -124,6 +125,15 @@ tty_open(struct tty *tty, const char *overrides, char **cause)
|
||||
return (0);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -264,6 +274,7 @@ tty_close(struct tty *tty)
|
||||
tty->log_fd = -1;
|
||||
}
|
||||
|
||||
evtimer_del(&tty->key_timer);
|
||||
tty_stop_tty(tty);
|
||||
|
||||
if (tty->flags & TTY_OPENED) {
|
||||
|
Loading…
Reference in New Issue
Block a user