Simplify next key matching and fix problems with meta and Unicode keys.

pull/390/head
nicm 2016-04-26 22:19:22 +00:00
parent fb1c929dc6
commit d3546cc85c
1 changed files with 116 additions and 71 deletions

View File

@ -33,14 +33,17 @@
* into a ternary tree. * into a ternary tree.
*/ */
void tty_keys_add1(struct tty_key **, const char *, key_code); static void tty_keys_add1(struct tty_key **, const char *, key_code);
void tty_keys_add(struct tty *, const char *, key_code); static void tty_keys_add(struct tty *, const char *, key_code);
void tty_keys_free1(struct tty_key *); static void tty_keys_free1(struct tty_key *);
struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t,
size_t *); size_t *);
struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); static struct tty_key *tty_keys_find(struct tty *, const char *, size_t,
void tty_keys_callback(int, short, void *); size_t *);
int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); static int tty_keys_next1(struct tty *, const char *, size_t, key_code *,
size_t *);
static void tty_keys_callback(int, short, void *);
static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *);
/* Default raw keys. */ /* Default raw keys. */
struct tty_default_key_raw { struct tty_default_key_raw {
@ -316,7 +319,7 @@ const struct tty_default_key_code tty_default_code_keys[] = {
}; };
/* Add key to tree. */ /* Add key to tree. */
void static void
tty_keys_add(struct tty *tty, const char *s, key_code key) tty_keys_add(struct tty *tty, const char *s, key_code key)
{ {
struct tty_key *tk; struct tty_key *tk;
@ -334,7 +337,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key)
} }
/* Add next node to the tree. */ /* Add next node to the tree. */
void static void
tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) tty_keys_add1(struct tty_key **tkp, const char *s, key_code key)
{ {
struct tty_key *tk; struct tty_key *tk;
@ -409,7 +412,7 @@ tty_keys_free(struct tty *tty)
} }
/* Free a single key. */ /* Free a single key. */
void static void
tty_keys_free1(struct tty_key *tk) tty_keys_free1(struct tty_key *tk)
{ {
if (tk->next != NULL) if (tk->next != NULL)
@ -422,7 +425,7 @@ tty_keys_free1(struct tty_key *tk)
} }
/* Lookup a key in the tree. */ /* Lookup a key in the tree. */
struct tty_key * static struct tty_key *
tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size)
{ {
*size = 0; *size = 0;
@ -430,7 +433,7 @@ tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size)
} }
/* Find the next node. */ /* Find the next node. */
struct tty_key * static struct tty_key *
tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) 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 the node is NULL, this is the end of the tree. No match. */
@ -460,6 +463,56 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size)
return (tty_keys_find1(tk, buf, len, size)); return (tty_keys_find1(tk, buf, len, size));
} }
/* Look up part of the next key. */
static int
tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
size_t *size)
{
struct tty_key *tk, *tk1;
struct utf8_data ud;
enum utf8_state more;
u_int i;
wchar_t wc;
log_debug("next key is %zu (%.*s)", len, (int)len, buf);
/* Empty buffer is a partial key. */
if (len == 0)
return (1);
/* Is this a known key? */
tk = tty_keys_find(tty, buf, len, size);
if (tk != NULL) {
tk1 = tk;
do
log_debug("keys in list: %#llx", tk->key);
while ((tk1 = tk1->next) != NULL);
*key = tk->key;
return (tk->next != NULL);
}
/* Is this valid UTF-8? */
more = utf8_open(&ud, (u_char)*buf);
if (more == UTF8_MORE) {
*size = ud.size;
if (len < ud.size)
return (1);
for (i = 1; i < ud.size; i++)
more = utf8_append(&ud, (u_char)buf[i]);
if (more != UTF8_DONE)
return (0);
if (utf8_combine(&ud, &wc) != UTF8_DONE)
return (0);
*key = wc;
log_debug("UTF-8 key %.*s %#llx", (int)ud.size, buf, *key);
return (0);
}
return (-1);
}
/* /*
* Process at least one key in the buffer and invoke tty->key_callback. Return * Process at least one key in the buffer and invoke tty->key_callback. Return
* 0 if there are no further keys, or 1 if there could be more in the buffer. * 0 if there are no further keys, or 1 if there could be more in the buffer.
@ -467,17 +520,12 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size)
key_code key_code
tty_keys_next(struct tty *tty) tty_keys_next(struct tty *tty)
{ {
struct tty_key *tk; struct timeval tv;
struct timeval tv; const char *buf;
const char *buf; size_t len, size;
size_t len, size; cc_t bspace;
cc_t bspace; int delay, expired = 0;
int delay, expired = 0; key_code key;
key_code key;
struct utf8_data ud;
enum utf8_state more;
u_int i;
wchar_t wc;
/* Get key buffer. */ /* Get key buffer. */
buf = EVBUFFER_DATA(tty->event->input); buf = EVBUFFER_DATA(tty->event->input);
@ -485,7 +533,7 @@ tty_keys_next(struct tty *tty)
if (len == 0) if (len == 0)
return (0); return (0);
log_debug("keys are %zu (%.*s)", len, (int) len, buf); log_debug("keys are %zu (%.*s)", len, (int)len, buf);
/* Is this a mouse key press? */ /* Is this a mouse key press? */
switch (tty_keys_mouse(tty, buf, len, &size)) { switch (tty_keys_mouse(tty, buf, len, &size)) {
@ -501,68 +549,65 @@ tty_keys_next(struct tty *tty)
goto partial_key; goto partial_key;
} }
/* Look for matching key string and return if found. */ first_key:
tk = tty_keys_find(tty, buf, len, &size); /* If escape is at the start, try without it. */
if (tk != NULL) { if (*buf == '\033') {
if (tk->next != NULL) switch (tty_keys_next1 (tty, buf + 1, len - 1, &key, &size)) {
case 0: /* found */
if (key != KEYC_UNKNOWN)
key |= KEYC_ESCAPE;
size++; /* include escape */
goto complete_key;
case -1: /* not found */
break;
case 1:
if (expired)
goto complete_key;
goto partial_key; goto partial_key;
key = tk->key; }
goto complete_key;
} }
/* Try to parse a key with an xterm-style modifier. */ /* Try with the escape. */
switch (tty_keys_next1 (tty, buf, len, &key, &size)) {
case 0: /* found */
goto complete_key;
case -1: /* not found */
break;
case 1:
if (expired)
goto complete_key;
goto partial_key;
}
/* Is this an an xterm(1) key? */
switch (xterm_keys_find(buf, len, &size, &key)) { switch (xterm_keys_find(buf, len, &size, &key)) {
case 0: /* found */ case 0: /* found */
goto complete_key; goto complete_key;
case -1: /* not found */ case -1: /* not found */
break; break;
case 1: case 1:
if (expired)
break;
goto partial_key; goto partial_key;
} }
first_key: /*
/* Is this a meta key? */ * If this starts with escape and is at least two keys, it must be
if (len >= 2 && buf[0] == '\033') { * complete even if the timer has not expired, because otherwise
if (buf[1] != '\033') { * tty_keys_next1 would have found a partial key. If just an escape
key = buf[1] | KEYC_ESCAPE; * alone, it needs to wait for the timer first.
*/
if (*buf == '\033') {
if (len >= 2) {
key = (u_char)buf[1] | KEYC_ESCAPE;
size = 2; size = 2;
goto complete_key; goto complete_key;
} }
if (!expired)
tk = tty_keys_find(tty, buf + 1, len - 1, &size);
if (tk != NULL && (!expired || tk->next == NULL)) {
size++; /* include escape */
if (tk->next != NULL)
goto partial_key;
key = tk->key;
if (key != KEYC_UNKNOWN)
key |= KEYC_ESCAPE;
goto complete_key;
}
}
/* Is this valid UTF-8? */
if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) {
size = ud.size;
if (len < size) {
if (expired)
goto discard_key;
goto partial_key; goto partial_key;
}
for (i = 1; i < size; i++)
more = utf8_append(&ud, (u_char)buf[i]);
if (more != UTF8_DONE)
goto discard_key;
if (utf8_combine(&ud, &wc) != UTF8_DONE)
goto discard_key;
key = wc;
log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key);
goto complete_key;
} }
/* No key found, take first. */ /* No longer key found, use the first character. */
key = (u_char)*buf; key = (u_char)*buf;
size = 1; size = 1;
@ -578,7 +623,7 @@ first_key:
goto complete_key; goto complete_key;
partial_key: partial_key:
log_debug("partial key %.*s", (int) len, buf); log_debug("partial key %.*s", (int)len, buf);
/* If timer is going, check for expiration. */ /* If timer is going, check for expiration. */
if (tty->flags & TTY_TIMER) { if (tty->flags & TTY_TIMER) {
@ -640,7 +685,7 @@ discard_key:
} }
/* Key timer callback. */ /* Key timer callback. */
void static 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;
@ -655,7 +700,7 @@ tty_keys_callback(__unused int fd, __unused short events, void *data)
* Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial
* (probably a mouse sequence but need more data). * (probably a mouse sequence but need more data).
*/ */
int static int
tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
{ {
struct mouse_event *m = &tty->mouse; struct mouse_event *m = &tty->mouse;