version 1.15, 2009/11/05 10:44:36 |
version 1.16, 2009/11/05 19:29:41 |
|
|
#include "tmux.h" |
#include "tmux.h" |
|
|
/* |
/* |
* Handle keys input from the outside terminal. |
* Handle keys input from the outside terminal. tty_keys[] is a base table of |
|
* supported keys which are looked up in terminfo(5) and translated into a |
|
* ternary tree (a binary tree of binary trees). |
*/ |
*/ |
|
|
void tty_keys_add(struct tty *, const char *, int); |
void tty_keys_add1(struct tty_key **, const char *, int); |
void tty_keys_callback(int, short, void *); |
void tty_keys_add(struct tty *, const char *, int); |
int tty_keys_mouse(char *, size_t, size_t *, struct mouse_event *); |
void tty_keys_free1(struct tty_key *); |
|
struct tty_key *tty_keys_find1( |
|
struct tty_key *, const char *, size_t, size_t *); |
|
struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); |
|
void tty_keys_callback(int, short, void *); |
|
int tty_keys_mouse(char *, size_t, size_t *, struct mouse_event *); |
|
|
struct tty_key_ent { |
struct tty_key_ent { |
enum tty_code_code code; |
enum tty_code_code code; |
|
|
#define TTYKEY_RAW 0x2 |
#define TTYKEY_RAW 0x2 |
}; |
}; |
|
|
|
/* |
|
* Default key tables. Those flagged with TTYKEY_RAW are inserted directly, |
|
* otherwise they are looked up in terminfo(5). Any keys marked TTYKEY_CTRL |
|
* have their last byte twiddled and are inserted as a Ctrl key as well. |
|
*/ |
struct tty_key_ent tty_keys[] = { |
struct tty_key_ent tty_keys[] = { |
/* Function keys. */ |
/* Function keys. */ |
{ TTYC_KF1, NULL, KEYC_F1, TTYKEY_CTRL }, |
{ TTYC_KF1, NULL, KEYC_F1, TTYKEY_CTRL }, |
|
|
{ TTYC_KUP7, NULL, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL, 0 }, |
{ TTYC_KUP7, NULL, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL, 0 }, |
}; |
}; |
|
|
RB_GENERATE(tty_keys, tty_key, entry, tty_keys_cmp); |
void |
|
tty_keys_add(struct tty *tty, const char *s, int key) |
struct tty_key *tty_keys_find(struct tty *, char *, size_t, size_t *); |
|
|
|
int |
|
tty_keys_cmp(struct tty_key *k1, struct tty_key *k2) |
|
{ |
{ |
return (strcmp(k1->string, k2->string)); |
size_t size; |
|
|
|
if (tty_keys_find(tty, s, strlen(s), &size) == NULL) { |
|
log_debug("new key 0x%x: %s", key, s); |
|
tty_keys_add1(&tty->key_tree, s, key); |
|
} |
} |
} |
|
|
|
/* Add next node to the tree. */ |
void |
void |
tty_keys_add(struct tty *tty, const char *s, int key) |
tty_keys_add1(struct tty_key **tkp, const char *s, int key) |
{ |
{ |
struct tty_key *tk, *tl; |
struct tty_key *tk; |
|
|
tk = xmalloc(sizeof *tk); |
/* Allocate a tree entry if there isn't one already. */ |
tk->string = xstrdup(s); |
tk = *tkp; |
tk->key = key; |
if (tk == NULL) { |
|
tk = *tkp = xcalloc(1, sizeof *tk); |
|
tk->ch = *s; |
|
tk->key = KEYC_NONE; |
|
} |
|
|
if ((tl = RB_INSERT(tty_keys, &tty->ktree, tk)) != NULL) { |
/* Find the next entry. */ |
xfree(tk->string); |
if (*s == tk->ch) { |
xfree(tk); |
/* Move forward in string. */ |
log_debug("key exists: %s (old %x, new %x)", s, tl->key, key); |
s++; |
return; |
|
|
/* If this is the end of the string, no more is necessary. */ |
|
if (*s == '\0') { |
|
tk->key = key; |
|
return; |
|
} |
|
|
|
/* Use the child tree for the next character. */ |
|
tkp = &tk->next; |
|
} else { |
|
if (*s < tk->ch) |
|
tkp = &tk->left; |
|
else if (*s > tk->ch) |
|
tkp = &tk->right; |
} |
} |
|
|
if (strlen(tk->string) > tty->ksize) |
/* And recurse to add it. */ |
tty->ksize = strlen(tk->string); |
tty_keys_add1(tkp, s, key); |
log_debug("new key %x: size now %zu (%s)", key, tty->ksize, tk->string); |
|
} |
} |
|
|
|
/* Initialise a key tree from the table. */ |
void |
void |
tty_keys_init(struct tty *tty) |
tty_keys_init(struct tty *tty) |
{ |
{ |
|
|
const char *s; |
const char *s; |
char tmp[64]; |
char tmp[64]; |
|
|
RB_INIT(&tty->ktree); |
tty->key_tree = NULL; |
|
|
tty->ksize = 0; |
|
for (i = 0; i < nitems(tty_keys); i++) { |
for (i = 0; i < nitems(tty_keys); i++) { |
tke = &tty_keys[i]; |
tke = &tty_keys[i]; |
|
|
|
|
if (!tty_term_has(tty->term, tke->code)) |
if (!tty_term_has(tty->term, tke->code)) |
continue; |
continue; |
s = tty_term_string(tty->term, tke->code); |
s = tty_term_string(tty->term, tke->code); |
if (s[0] != '\033' || s[1] == '\0') |
|
continue; |
|
} |
} |
|
if (s[0] != '\033' || s[1] == '\0') |
|
continue; |
|
|
tty_keys_add(tty, s + 1, tke->key); |
tty_keys_add(tty, s + 1, tke->key); |
if (tke->flags & TTYKEY_CTRL) { |
if (!(tke->flags & TTYKEY_CTRL)) { |
if (strlcpy(tmp, s, sizeof tmp) >= sizeof tmp) |
if (strlcpy(tmp, s, sizeof tmp) >= sizeof tmp) |
continue; |
continue; |
tmp[strlen(tmp) - 1] ^= 0x20; |
tmp[strlen(tmp) - 1] ^= 0x20; |
|
|
} |
} |
} |
} |
|
|
|
/* Free the entire key tree. */ |
void |
void |
tty_keys_free(struct tty *tty) |
tty_keys_free(struct tty *tty) |
{ |
{ |
struct tty_key *tk; |
tty_keys_free1(tty->key_tree); |
|
} |
|
|
while (!RB_EMPTY(&tty->ktree)) { |
/* Free a single key. */ |
tk = RB_ROOT(&tty->ktree); |
void |
RB_REMOVE(tty_keys, &tty->ktree, tk); |
tty_keys_free1(struct tty_key *tk) |
xfree(tk->string); |
{ |
xfree(tk); |
if (tk->next != NULL) |
} |
tty_keys_free1(tk->next); |
|
if (tk->left != NULL) |
|
tty_keys_free1(tk->left); |
|
if (tk->right != NULL) |
|
tty_keys_free1(tk->right); |
|
xfree(tk); |
|
|
} |
} |
|
|
|
/* Lookup a key in the tree. */ |
struct tty_key * |
struct tty_key * |
tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size) |
tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) |
{ |
{ |
struct tty_key *tk, tl; |
*size = 0; |
char *s; |
return (tty_keys_find1(tty->key_tree, buf, len, size)); |
|
} |
|
|
if (len == 0) |
/* Find the next node. */ |
|
struct tty_key * |
|
tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) |
|
{ |
|
/* If the node is NULL, this is the end of the tree. No match. */ |
|
if (tk == NULL) |
return (NULL); |
return (NULL); |
|
|
s = xmalloc(tty->ksize + 1); |
/* Pick the next in the sequence. */ |
for (*size = tty->ksize; (*size) > 0; (*size)--) { |
if (tk->ch == *buf) { |
if ((*size) > len) |
/* Move forward in the string. */ |
continue; |
buf++; len--; |
memcpy(s, buf, *size); |
(*size)++; |
s[*size] = '\0'; |
|
|
|
log_debug2("looking for key: %s", s); |
/* At the end of the string, return the current node. */ |
|
if (len == 0) |
tl.string = s; |
|
tk = RB_FIND(tty_keys, &tty->ktree, &tl); |
|
if (tk != NULL) { |
|
log_debug2("got key: 0x%x", tk->key); |
|
xfree(s); |
|
return (tk); |
return (tk); |
} |
|
|
/* Move into the next tree for the following character. */ |
|
tk = tk->next; |
|
} else { |
|
if (*buf < tk->ch) |
|
tk = tk->left; |
|
else if (*buf > tk->ch) |
|
tk = tk->right; |
} |
} |
xfree(s); |
|
|
|
return (NULL); |
/* Move to the next in the tree. */ |
|
return (tty_keys_find1(tk, buf, len, size)); |
} |
} |
|
|
|
/* |
|
* Process at least one key in the buffer and invoke tty->key_callback. Return |
|
* 1 if there are no further keys, or 0 if there is more in the buffer. |
|
*/ |
int |
int |
tty_keys_next(struct tty *tty) |
tty_keys_next(struct tty *tty) |
{ |
{ |
struct tty_key *tk; |
struct tty_key *tk; |
struct timeval tv; |
struct timeval tv; |
struct mouse_event mouse; |
struct mouse_event mouse; |
char *buf; |
char *buf, *ptr; |
size_t len, size; |
size_t len, size; |
cc_t bspace; |
cc_t bspace; |
int key; |
int key; |
u_char ch; |
|
|
|
buf = EVBUFFER_DATA(tty->event->input); |
buf = EVBUFFER_DATA(tty->event->input); |
len = EVBUFFER_LENGTH(tty->event->input); |
len = EVBUFFER_LENGTH(tty->event->input); |
|
|
|
|
/* If a normal key, return it. */ |
/* If a normal key, return it. */ |
if (*buf != '\033') { |
if (*buf != '\033') { |
bufferevent_read(tty->event, &ch, 1); |
key = *buf; |
key = ch; |
evbuffer_drain(tty->event->input, 1); |
|
|
/* |
/* |
* Check for backspace key using termios VERASE - the terminfo |
* Check for backspace key using termios VERASE - the terminfo |
|
|
bspace = tty->tio.c_cc[VERASE]; |
bspace = tty->tio.c_cc[VERASE]; |
if (bspace != _POSIX_VDISABLE && key == bspace) |
if (bspace != _POSIX_VDISABLE && key == bspace) |
key = KEYC_BSPACE; |
key = KEYC_BSPACE; |
goto found; |
goto handle_key; |
} |
} |
|
|
/* Look for matching key string and return if found. */ |
/* Look for matching key string and return if found. */ |
tk = tty_keys_find(tty, buf + 1, len - 1, &size); |
tk = tty_keys_find(tty, buf + 1, len - 1, &size); |
if (tk != NULL) { |
if (tk != NULL) { |
evbuffer_drain(tty->event->input, size + 1); |
|
key = tk->key; |
key = tk->key; |
goto found; |
goto found_key; |
} |
} |
|
|
/* Not found. Is this a mouse key press? */ |
/* Not found. Is this a mouse key press? */ |
key = tty_keys_mouse(buf, len, &size, &mouse); |
key = tty_keys_mouse(buf, len, &size, &mouse); |
if (key != KEYC_NONE) { |
if (key != KEYC_NONE) { |
evbuffer_drain(tty->event->input, size); |
evbuffer_drain(tty->event->input, size); |
goto found; |
goto handle_key; |
} |
} |
|
|
/* Not found. Try to parse a key with an xterm-style modifier. */ |
/* Not found. Try to parse a key with an xterm-style modifier. */ |
key = xterm_keys_find(buf, len, &size); |
key = xterm_keys_find(buf, len, &size); |
if (key != KEYC_NONE) { |
if (key != KEYC_NONE) { |
evbuffer_drain(tty->event->input, size); |
evbuffer_drain(tty->event->input, size); |
goto found; |
goto handle_key; |
} |
} |
|
|
/* Skip the escape. */ |
/* Skip the escape. */ |
|
|
|
|
/* Is there a normal key following? */ |
/* Is there a normal key following? */ |
if (len != 0 && *buf != '\033') { |
if (len != 0 && *buf != '\033') { |
evbuffer_drain(tty->event->input, 1); |
key = *buf | KEYC_ESCAPE; |
bufferevent_read(tty->event, &ch, 1); |
evbuffer_drain(tty->event->input, 2); |
key = ch | KEYC_ESCAPE; |
goto handle_key; |
goto found; |
|
} |
} |
|
|
/* Or a key string? */ |
/* Or a key string? */ |
if (len > 1) { |
if (len > 1) { |
tk = tty_keys_find(tty, buf + 1, len - 1, &size); |
tk = tty_keys_find(tty, buf + 1, len - 1, &size); |
if (tk != NULL) { |
if (tk != NULL) { |
evbuffer_drain(tty->event->input, size + 2); |
|
key = tk->key | KEYC_ESCAPE; |
key = tk->key | KEYC_ESCAPE; |
goto found; |
size++; /* include escape */ |
|
goto found_key; |
} |
} |
} |
} |
|
|
|
/* Escape and then nothing useful - fall through. */ |
|
|
|
partial_key: |
/* |
/* |
* Escape but no key string. If have, already seen an escape, then the |
* 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. |
* timer must have expired, so give up waiting and send the escape. |
*/ |
*/ |
if (tty->flags & TTY_ESCAPE) { |
if (tty->flags & TTY_ESCAPE) { |
evbuffer_drain(tty->event->input, 1); |
evbuffer_drain(tty->event->input, 1); |
key = '\033'; |
key = '\033'; |
goto found; |
goto handle_key; |
} |
} |
|
|
|
/* Fall through to start the timer. */ |
|
|
|
start_timer: |
/* Start the timer and wait for expiry or more data. */ |
/* Start the timer and wait for expiry or more data. */ |
tv.tv_sec = 0; |
tv.tv_sec = 0; |
tv.tv_usec = ESCAPE_PERIOD * 1000L; |
tv.tv_usec = ESCAPE_PERIOD * 1000L; |
|
|
tty->flags |= TTY_ESCAPE; |
tty->flags |= TTY_ESCAPE; |
return (0); |
return (0); |
|
|
found: |
found_key: |
evtimer_del(&tty->key_timer); |
if (tk->next != NULL) { |
|
/* Partial key. Start the timer if not already expired. */ |
|
if (!(tty->flags & TTY_ESCAPE)) |
|
goto start_timer; |
|
|
|
/* Otherwise, if no key, send the escape alone. */ |
|
if (tk->key == KEYC_NONE) |
|
goto partial_key; |
|
|
|
/* Or fall through to send the partial key found. */ |
|
} |
|
evbuffer_drain(tty->event->input, size + 1); |
|
|
|
goto handle_key; |
|
|
|
handle_key: |
|
evtimer_del(&tty->key_timer); |
|
|
tty->key_callback(key, &mouse, tty->key_data); |
tty->key_callback(key, &mouse, tty->key_data); |
|
|
tty->flags &= ~TTY_ESCAPE; |
tty->flags &= ~TTY_ESCAPE; |
return (1); |
return (1); |
} |
} |
|
|
|
/* Key timer callback. */ |
void |
void |
tty_keys_callback(unused int fd, unused short events, void *data) |
tty_keys_callback(unused int fd, unused short events, void *data) |
{ |
{ |
struct tty *tty = data; |
struct tty *tty = data; |
|
|
if (tty->flags & TTY_ESCAPE) |
if (!(tty->flags & TTY_ESCAPE)) |
tty_keys_next(tty); |
return; |
|
|
|
while (tty_keys_next(tty)) |
|
; |
} |
} |
|
|
|
/* Handle mouse key input. */ |
int |
int |
tty_keys_mouse(char *buf, size_t len, size_t *size, struct mouse_event *m) |
tty_keys_mouse(char *buf, size_t len, size_t *size, struct mouse_event *m) |
{ |
{ |
|
|
* buttons, X and Y, all based at 32 with 1,1 top-left. |
* buttons, X and Y, all based at 32 with 1,1 top-left. |
*/ |
*/ |
|
|
log_debug("mouse input is: %.*s", (int) len, buf); |
|
if (len != 6 || memcmp(buf, "\033[M", 3) != 0) |
if (len != 6 || memcmp(buf, "\033[M", 3) != 0) |
return (KEYC_NONE); |
return (KEYC_NONE); |
*size = 6; |
*size = 6; |
|
|
|
log_debug("mouse input is: %.*s", (int) len, buf); |
|
|
m->b = buf[3]; |
m->b = buf[3]; |
m->x = buf[4]; |
m->x = buf[4]; |