diff options
-rw-r--r-- | input-keys.c | 571 | ||||
-rw-r--r-- | input.c | 19 | ||||
-rw-r--r-- | options-table.c | 13 | ||||
-rw-r--r-- | tmux.1 | 18 | ||||
-rw-r--r-- | tty-features.c | 17 | ||||
-rw-r--r-- | tty-keys.c | 484 | ||||
-rw-r--r-- | tty-term.c | 2 | ||||
-rw-r--r-- | tty.c | 21 |
8 files changed, 809 insertions, 336 deletions
diff --git a/input-keys.c b/input-keys.c index e9c595b4..19134c1a 100644 --- a/input-keys.c +++ b/input-keys.c @@ -33,112 +33,340 @@ static void input_key_mouse(struct window_pane *, struct mouse_event *); -struct input_key_ent { - key_code key; - const char *data; +/* Entry in the key tree. */ +struct input_key_entry { + key_code key; + const char *data; - int flags; -#define INPUTKEY_KEYPAD 0x1 /* keypad key */ -#define INPUTKEY_CURSOR 0x2 /* cursor key */ + RB_ENTRY(input_key_entry) entry; }; +RB_HEAD(input_key_tree, input_key_entry); -static const struct input_key_ent input_keys[] = { +/* Tree of input keys. */ +static int input_key_cmp(struct input_key_entry *, + struct input_key_entry *); +RB_GENERATE_STATIC(input_key_tree, input_key_entry, entry, input_key_cmp); +struct input_key_tree input_key_tree = RB_INITIALIZER(&input_key_tree); + +/* List of default keys, the tree is built from this. */ +static struct input_key_entry input_key_defaults[] = { /* Paste keys. */ - { KEYC_PASTE_START, "\033[200~", 0 }, - { KEYC_PASTE_END, "\033[201~", 0 }, + { .key = KEYC_PASTE_START, + .data = "\033[200~" + }, + { .key = KEYC_PASTE_END, + .data = "\033[201~" + }, /* Function keys. */ - { KEYC_F1, "\033OP", 0 }, - { KEYC_F2, "\033OQ", 0 }, - { KEYC_F3, "\033OR", 0 }, - { KEYC_F4, "\033OS", 0 }, - { KEYC_F5, "\033[15~", 0 }, - { KEYC_F6, "\033[17~", 0 }, - { KEYC_F7, "\033[18~", 0 }, - { KEYC_F8, "\033[19~", 0 }, - { KEYC_F9, "\033[20~", 0 }, - { KEYC_F10, "\033[21~", 0 }, - { KEYC_F11, "\033[23~", 0 }, - { KEYC_F12, "\033[24~", 0 }, - { KEYC_F1|KEYC_SHIFT, "\033[25~", 0 }, - { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, - { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, - { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, - { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, - { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, - { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, - { KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, - { KEYC_IC, "\033[2~", 0 }, - { KEYC_DC, "\033[3~", 0 }, - { KEYC_HOME, "\033[1~", 0 }, - { KEYC_END, "\033[4~", 0 }, - { KEYC_NPAGE, "\033[6~", 0 }, - { KEYC_PPAGE, "\033[5~", 0 }, - { KEYC_BTAB, "\033[Z", 0 }, - - /* - * Arrow keys. Cursor versions must come first. The codes are toggled - * between CSI and SS3 versions when ctrl is pressed. - */ - { KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, - { KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, - { KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, - { KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, - - { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, - { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, - { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, - { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, - - { KEYC_UP|KEYC_CTRL, "\033OA", 0 }, - { KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, - { KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, - { KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, - - { KEYC_UP, "\033[A", 0 }, - { KEYC_DOWN, "\033[B", 0 }, - { KEYC_RIGHT, "\033[C", 0 }, - { KEYC_LEFT, "\033[D", 0 }, - - /* Keypad keys. Keypad versions must come first. */ - { KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, - { KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, - { KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, - { KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, - { KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, - { KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, - { KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, - { KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, - { KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, - { KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, - { KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, - { KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, - { KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, - { KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, - { KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, - { KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, - - { KEYC_KP_SLASH, "/", 0 }, - { KEYC_KP_STAR, "*", 0 }, - { KEYC_KP_MINUS, "-", 0 }, - { KEYC_KP_SEVEN, "7", 0 }, - { KEYC_KP_EIGHT, "8", 0 }, - { KEYC_KP_NINE, "9", 0 }, - { KEYC_KP_PLUS, "+", 0 }, - { KEYC_KP_FOUR, "4", 0 }, - { KEYC_KP_FIVE, "5", 0 }, - { KEYC_KP_SIX, "6", 0 }, - { KEYC_KP_ONE, "1", 0 }, - { KEYC_KP_TWO, "2", 0 }, - { KEYC_KP_THREE, "3", 0 }, - { KEYC_KP_ENTER, "\n", 0 }, - { KEYC_KP_ZERO, "0", 0 }, - { KEYC_KP_PERIOD, ".", 0 }, + { .key = KEYC_F1, + .data = "\033OP" + }, + { .key = KEYC_F2, + .data = "\033OQ" + }, + { .key = KEYC_F3, + .data = "\033OR" + }, + { .key = KEYC_F4, + .data = "\033OS" + }, + { .key = KEYC_F5, + .data = "\033[15~" + }, + { .key = KEYC_F6, + .data = "\033[17~" + }, + { .key = KEYC_F7, + .data = "\033[18~" + }, + { .key = KEYC_F8, + .data = "\033[19~" + }, + { .key = KEYC_F9, + .data = "\033[20~" + }, + { .key = KEYC_F10, + .data = "\033[21~" + }, + { .key = KEYC_F11, + .data = "\033[23~" + }, + { .key = KEYC_F12, + .data = "\033[24~" + }, + { .key = KEYC_F1|KEYC_SHIFT, + .data = "\033[25~" + }, + { .key = KEYC_F2|KEYC_SHIFT, + .data = "\033[26~" + }, + { .key = KEYC_F3|KEYC_SHIFT, + .data = "\033[28~" + }, + { .key = KEYC_F4|KEYC_SHIFT, + .data = "\033[29~" + }, + { .key = KEYC_F5|KEYC_SHIFT, + .data = "\033[31~" + }, + { .key = KEYC_F6|KEYC_SHIFT, + .data = "\033[32~" + }, + { .key = KEYC_F7|KEYC_SHIFT, + .data = "\033[33~" + }, + { .key = KEYC_F8|KEYC_SHIFT, + .data = "\033[34~" + }, + { .key = KEYC_IC, + .data = "\033[2~" + }, + { .key = KEYC_DC, + .data = "\033[3~" + }, + { .key = KEYC_HOME, + .data = "\033[1~" + }, + { .key = KEYC_END, + .data = "\033[4~" + }, + { .key = KEYC_NPAGE, + .data = "\033[6~" + }, + { .key = KEYC_PPAGE, + .data = "\033[5~" + }, + { .key = KEYC_BTAB, + .data = "\033[Z" + }, + + /* Arrow keys. */ + { .key = KEYC_UP|KEYC_CURSOR, + .data = "\033OA" + }, + { .key = KEYC_DOWN|KEYC_CURSOR, + .data = "\033OB" + }, + { .key = KEYC_RIGHT|KEYC_CURSOR, + .data = "\033OC" + }, + { .key = KEYC_LEFT|KEYC_CURSOR, + .data = "\033OD" + }, + { .key = KEYC_UP, + .data = "\033[A" + }, + { .key = KEYC_DOWN, + .data = "\033[B" + }, + { .key = KEYC_RIGHT, + .data = "\033[C" + }, + { .key = KEYC_LEFT, + .data = "\033[D" + }, + + /* Keypad keys. */ + { .key = KEYC_KP_SLASH|KEYC_KEYPAD, + .data = "\033Oo" + }, + { .key = KEYC_KP_STAR|KEYC_KEYPAD, + .data = "\033Oj" + }, + { .key = KEYC_KP_MINUS|KEYC_KEYPAD, + .data = "\033Om" + }, + { .key = KEYC_KP_SEVEN|KEYC_KEYPAD, + .data = "\033Ow" + }, + { .key = KEYC_KP_EIGHT|KEYC_KEYPAD, + .data = "\033Ox" + }, + { .key = KEYC_KP_NINE|KEYC_KEYPAD, + .data = "\033Oy" + }, + { .key = KEYC_KP_PLUS|KEYC_KEYPAD, + .data = "\033Ok" + }, + { .key = KEYC_KP_FOUR|KEYC_KEYPAD, + .data = "\033Ot" + }, + { .key = KEYC_KP_FIVE|KEYC_KEYPAD, + .data = "\033Ou" + }, + { .key = KEYC_KP_SIX|KEYC_KEYPAD, + .data = "\033Ov" + }, + { .key = KEYC_KP_ONE|KEYC_KEYPAD, + .data = "\033Oq" + }, + { .key = KEYC_KP_TWO|KEYC_KEYPAD, + .data = "\033Or" + }, + { .key = KEYC_KP_THREE|KEYC_KEYPAD, + .data = "\033Os" + }, + { .key = KEYC_KP_ENTER|KEYC_KEYPAD, + .data = "\033OM" + }, + { .key = KEYC_KP_ZERO|KEYC_KEYPAD, + .data = "\033Op" + }, + { .key = KEYC_KP_PERIOD|KEYC_KEYPAD, + .data = "\033On" + }, + { .key = KEYC_KP_SLASH, + .data = "/" + }, + { .key = KEYC_KP_STAR, + .data = "*" + }, + { .key = KEYC_KP_MINUS, + .data = "-" + }, + { .key = KEYC_KP_SEVEN, + .data = "7" + }, + { .key = KEYC_KP_EIGHT, + .data = "8" + }, + { .key = KEYC_KP_NINE, + .data = "9" + }, + { .key = KEYC_KP_PLUS, + .data = "+" + }, + { .key = KEYC_KP_FOUR, + .data = "4" + }, + { .key = KEYC_KP_FIVE, + .data = "5" + }, + { .key = KEYC_KP_SIX, + .data = "6" + }, + { .key = KEYC_KP_ONE, + .data = "1" + }, + { .key = KEYC_KP_TWO, + .data = "2" + }, + { .key = KEYC_KP_THREE, + .data = "3" + }, + { .key = KEYC_KP_ENTER, + .data = "\n" + }, + { .key = KEYC_KP_ZERO, + .data = "0" + }, + { .key = KEYC_KP_PERIOD, + .data = "." + }, + + /* Keys with an embedded modifier. */ + { .key = KEYC_F1|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_P" + }, + { .key = KEYC_F2|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_Q" + }, + { .key = KEYC_F3|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_R" + }, + { .key = KEYC_F4|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_S" + }, + { .key = KEYC_F5|KEYC_BUILD_MODIFIERS, + .data = "\033[15;_~" + }, + { .key = KEYC_F6|KEYC_BUILD_MODIFIERS, + .data = "\033[17;_~" + }, + { .key = KEYC_F7|KEYC_BUILD_MODIFIERS, + .data = "\033[18;_~" + }, + { .key = KEYC_F8|KEYC_BUILD_MODIFIERS, + .data = "\033[19;_~" + }, + { .key = KEYC_F9|KEYC_BUILD_MODIFIERS, + .data = "\033[20;_~" + }, + { .key = KEYC_F10|KEYC_BUILD_MODIFIERS, + .data = "\033[21;_~" + }, + { .key = KEYC_F11|KEYC_BUILD_MODIFIERS, + .data = "\033[23;_~" + }, + { .key = KEYC_F12|KEYC_BUILD_MODIFIERS, + .data = "\033[24;_~" + }, + { .key = KEYC_UP|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_A" + }, + { .key = KEYC_DOWN|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_B" + }, + { .key = KEYC_RIGHT|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_C" + }, + { .key = KEYC_LEFT|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_D" + }, + { .key = KEYC_HOME|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_H" + }, + { .key = KEYC_END|KEYC_BUILD_MODIFIERS, + .data = "\033[1;_F" + }, + { .key = KEYC_PPAGE|KEYC_BUILD_MODIFIERS, + .data = "\033[5;_~" + }, + { .key = KEYC_NPAGE|KEYC_BUILD_MODIFIERS, + .data = "\033[6;_~" + }, + { .key = KEYC_IC|KEYC_BUILD_MODIFIERS, + .data = "\033[2;_~" + }, + { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, + .data = "\033[3;_~" } +}; +static const key_code input_key_modifiers[] = { + 0, + 0, + KEYC_SHIFT, + KEYC_META|KEYC_IMPLIED_META, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, + KEYC_CTRL, + KEYC_SHIFT|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }; +/* Input key comparison function. */ +static int +input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2) +{ + if (ike1->key < ike2->key) + return (-1); + if (ike1->key > ike2->key) + return (1); + return (0); +} + +/* Look for key in tree. */ +static struct input_key_entry * +input_key_get (key_code key) +{ + struct input_key_entry entry = { .key = key }; + + return (RB_FIND(input_key_tree, &input_key_tree, &entry)); +} + /* Split a character into two UTF-8 bytes. */ static size_t -input_split2(u_int c, u_char *dst) +input_key_split2(u_int c, u_char *dst) { if (c > 0x7f) { dst[0] = (c >> 6) | 0xc0; @@ -149,32 +377,65 @@ input_split2(u_int c, u_char *dst) return (1); } +/* Build input key tree. */ +void +input_key_build(void) +{ + struct input_key_entry *ike, *new; + u_int i, j; + char *data; + key_code key; + + for (i = 0; i < nitems(input_key_defaults); i++) { + ike = &input_key_defaults[i]; + if (~ike->key & KEYC_BUILD_MODIFIERS) { + RB_INSERT(input_key_tree, &input_key_tree, ike); + continue; + } + + for (j = 2; j < nitems(input_key_modifiers); j++) { + key = (ike->key & ~KEYC_BUILD_MODIFIERS); + data = xstrdup(ike->data); + data[strcspn(data, "_")] = '0' + j; + + new = xcalloc(1, sizeof *new); + new->key = key|input_key_modifiers[j]; + new->data = data; + RB_INSERT(input_key_tree, &input_key_tree, new); + } + } + + RB_FOREACH(ike, input_key_tree, &input_key_tree) { + log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key, + key_string_lookup_key(ike->key, 1), ike->data); + } +} + /* Translate a key code into an output key sequence for a pane. */ int input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) { - log_debug("writing key 0x%llx (%s) to %%%u", key, - key_string_lookup_key(key), wp->id); + if (log_get_level() != 0) { + log_debug("writing key 0x%llx (%s) to %%%u", key, + key_string_lookup_key(key, 1), wp->id); + } if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); return (0); } - return (input_key(wp, wp->screen, wp->event, key)); + return (input_key(wp->screen, wp->event, key)); } /* Translate a key code into an output key sequence. */ int -input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, - key_code key) +input_key(struct screen *s, struct bufferevent *bev, key_code key) { - const struct input_key_ent *ike; - u_int i; - size_t dlen; - char *out; - key_code justkey, newkey; - struct utf8_data ud; + struct input_key_entry *ike; + key_code justkey, newkey, outkey; + struct utf8_data ud; + char tmp[64], modifier; /* Mouse keys need a pane. */ if (KEYC_IS_MOUSE(key)) @@ -192,16 +453,16 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, newkey = options_get_number(global_options, "backspace"); if (newkey >= 0x7f) newkey = '\177'; - key = newkey|(key & KEYC_MASK_MOD); + key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS)); } /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ - justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE)); + justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META)); if (justkey <= 0x7f) { - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); ud.data[0] = justkey; bufferevent_write(bev, &ud.data[0], 1); @@ -210,51 +471,69 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return (-1); - if (key & KEYC_ESCAPE) + if (key & KEYC_META) bufferevent_write(bev, "\033", 1); bufferevent_write(bev, ud.data, ud.size); return (0); } /* - * Then try to look this up as an xterm key, if the flag to output them - * is set. + * Look up in the tree. If not in application keypad or cursor mode, + * remove the flags from the key. */ - if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) { - if ((out = xterm_keys_lookup(key)) != NULL) { - bufferevent_write(bev, out, strlen(out)); - free(out); - return (0); - } + if (~s->mode & MODE_KKEYPAD) + key &= ~KEYC_KEYPAD; + if (~s->mode & MODE_KCURSOR) + key &= ~KEYC_CURSOR; + ike = input_key_get(key); + if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) + ike = input_key_get(key & ~KEYC_META); + if (ike != NULL) { + log_debug("found key 0x%llx: \"%s\"", key, ike->data); + if (key & KEYC_META && (~key & KEYC_IMPLIED_META)) + bufferevent_write(bev, "\033", 1); + bufferevent_write(bev, ike->data, strlen(ike->data)); + return (0); } - key &= ~KEYC_XTERM; - - /* Otherwise look the key up in the table. */ - for (i = 0; i < nitems(input_keys); i++) { - ike = &input_keys[i]; - if ((ike->flags & INPUTKEY_KEYPAD) && (~s->mode & MODE_KKEYPAD)) - continue; - if ((ike->flags & INPUTKEY_CURSOR) && (~s->mode & MODE_KCURSOR)) - continue; - - if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) - break; - if (ike->key == key) - break; + /* No builtin key sequence; construct an extended key sequence. */ + if (~s->mode & MODE_KEXTENDED) { + if ((key & KEYC_MASK_MODIFIERS) == KEYC_CTRL && + (key & KEYC_MASK_KEY) < KEYC_BASE) + return (input_key(s, bev, key & ~KEYC_CTRL)); + goto missing; } - if (i == nitems(input_keys)) { - log_debug("key 0x%llx missing", key); - return (-1); + outkey = (key & KEYC_MASK_KEY); + switch (key & KEYC_MASK_MODIFIERS) { + case KEYC_SHIFT: + modifier = '2'; + break; + case KEYC_META: + modifier = '3'; + break; + case KEYC_SHIFT|KEYC_META: + modifier = '4'; + break; + case KEYC_CTRL: + modifier = '5'; + break; + case KEYC_SHIFT|KEYC_CTRL: + modifier = '6'; + break; + case KEYC_META|KEYC_CTRL: + modifier = '7'; + break; + case KEYC_SHIFT|KEYC_META|KEYC_CTRL: + modifier = '8'; + break; } - dlen = strlen(ike->data); - log_debug("found key 0x%llx: \"%s\"", key, ike->data); - - /* Prefix a \033 for escape. */ - if (key & KEYC_ESCAPE) - bufferevent_write(bev, "\033", 1); - bufferevent_write(bev, ike->data, dlen); + xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); + bufferevent_write(bev, tmp, strlen(tmp)); return (0); + +missing: + log_debug("key 0x%llx missing", key); + return (-1); } /* Get mouse event string. */ @@ -309,9 +588,9 @@ input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y, if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) return (0); len = xsnprintf(buf, sizeof buf, "\033[M"); - len += input_split2(m->b + 32, &buf[len]); - len += input_split2(x + 33, &buf[len]); - len += input_split2(y + 33, &buf[len]); + len += input_key_split2(m->b + 32, &buf[len]); + len += input_key_split2(x + 33, &buf[len]); + len += input_key_split2(y + 33, &buf[len]); } else { if (m->b > 223) return (0); @@ -241,6 +241,8 @@ enum input_csi_type { INPUT_CSI_HPA, INPUT_CSI_ICH, INPUT_CSI_IL, + INPUT_CSI_MODOFF, + INPUT_CSI_MODSET, INPUT_CSI_RCP, INPUT_CSI_REP, INPUT_CSI_RM, @@ -289,7 +291,9 @@ static const struct input_table_entry input_csi_table[] = { { 'l', "", INPUT_CSI_RM }, { 'l', "?", INPUT_CSI_RM_PRIVATE }, { 'm', "", INPUT_CSI_SGR }, + { 'm', ">", INPUT_CSI_MODSET }, { 'n', "", INPUT_CSI_DSR }, + { 'n', ">", INPUT_CSI_MODOFF }, { 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', ">", INPUT_CSI_XDA }, { 'r', "", INPUT_CSI_DECSTBM }, @@ -1380,6 +1384,19 @@ input_csi_dispatch(struct input_ctx *ictx) if (n != -1 && m != -1) screen_write_cursormove(sctx, m - 1, n - 1, 1); break; + case INPUT_CSI_MODSET: + n = input_get(ictx, 0, 0, 0); + m = input_get(ictx, 1, 0, 0); + if (n == 0 || (n == 4 && m == 0)) + screen_write_mode_clear(sctx, MODE_KEXTENDED); + else if (n == 4 && (m == 1 || m == 2)) + screen_write_mode_set(sctx, MODE_KEXTENDED); + break; + case INPUT_CSI_MODOFF: + n = input_get(ictx, 0, 0, 0); + if (n == 4) + screen_write_mode_clear(sctx, MODE_KEXTENDED); + break; case INPUT_CSI_WINOPS: input_csi_dispatch_winops(ictx); break; @@ -1593,7 +1610,7 @@ input_csi_dispatch(struct input_ctx *ictx) break; case INPUT_CSI_XDA: n = input_get(ictx, 0, 0, 0); - if (n != 0) + if (n == 0) input_reply(ictx, "\033P>|tmux %s\033\\", getversion()); break; diff --git a/options-table.c b/options-table.c index 4ac0d0c3..87670b12 100644 --- a/options-table.c +++ b/options-table.c @@ -252,6 +252,14 @@ const struct options_table_entry options_table[] = { "clients." }, + { .name = "extended-keys", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, + .default_num = 0, + .text = "Whether to request extended key sequences from terminals " + "that support it." + }, + { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, @@ -1053,11 +1061,12 @@ const struct options_table_entry options_table[] = { "bottom." }, - { .name = "xterm-keys", + { .name = "xterm-keys", /* no longer used */ .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1, - .text = "Whether xterm-style function key sequences should be sent." + .text = "Whether xterm-style function key sequences should be sent. " + "This option is no longer used." }, /* Hook options. */ @@ -3225,6 +3225,12 @@ sessions. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. +.It Xo Ic extended-keys +.Op Ic on | off +.Xc +When enabled, extended keys are requested from the terminal and if supported +are recognised by +.Nm . .It Xo Ic focus-events .Op Ic on | off .Xc @@ -4054,16 +4060,6 @@ option. .Xc If this option is set, searches will wrap around the end of the pane contents. The default is on. -.Pp -.It Xo Ic xterm-keys -.Op Ic on | off -.Xc -If this option is set, -.Nm -will generate -.Xr xterm 1 -style -function key sequences; these have a number included to indicate modifiers such -as Shift, Alt or Ctrl. .El .Pp Available pane options are: @@ -5754,6 +5750,8 @@ Disable and enable bracketed paste. These are set automatically if the .Em XT capability is present. +.It Em \&Dseks , \&Eneks +Disable and enable extended keys. .It Em \&Dsfcs , \&Enfcs Disable and enable focus reporting. These are set automatically if the diff --git a/tty-features.c b/tty-features.c index 30d3d1a0..26344b90 100644 --- a/tty-features.c +++ b/tty-features.c @@ -190,6 +190,18 @@ static const struct tty_feature tty_feature_sync = { 0 }; +/* Terminal supports extended keys. */ +static const char *tty_feature_extkeys_capabilities[] = { + "Eneks=\\E[>4;1m", + "Dseks=\\E[>4m", + NULL +}; +static const struct tty_feature tty_feature_extkeys = { + "extkeys", + tty_feature_extkeys_capabilities, + 0 +}; + /* Terminal supports DECSLRM margins. */ static const char *tty_feature_margins_capabilities[] = { "Enmg=\\E[?69h", @@ -218,6 +230,7 @@ static const struct tty_feature *tty_features[] = { &tty_feature_ccolour, &tty_feature_clipboard, &tty_feature_cstyle, + &tty_feature_extkeys, &tty_feature_focus, &tty_feature_margins, &tty_feature_overline, @@ -321,7 +334,7 @@ tty_default_features(int *feat, const char *name, u_int version) } table[] = { #define TTY_FEATURES_BASE_MODERN_XTERM "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,margins,overline" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,margins,overline" }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,overline,usstyle" @@ -333,7 +346,7 @@ tty_default_features(int *feat, const char *name, u_int version) .features = TTY_FEATURES_BASE_MODERN_XTERM ",cstyle,margins,sync" }, { .name = "XTerm", - .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,focus,margins,rectfill" + .features = TTY_FEATURES_BASE_MODERN_XTERM ",ccolour,cstyle,extkeys,focus,margins,rectfill" } }; u_int i; @@ -21,6 +21,7 @@ #include <netinet/in.h> +#include <ctype.h> #include <limits.h> #include <resolv.h> #include <stdlib.h> @@ -46,6 +47,8 @@ static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, size_t *, int); static void tty_keys_callback(int, short, void *); +static int tty_keys_extended_key(struct tty *, const char *, size_t, + size_t *, key_code *); static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, struct mouse_event *); static int tty_keys_clipboard(struct tty *, const char *, size_t, @@ -69,33 +72,33 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { * put the terminal into keypad_xmit mode. Translation of numbers * mode/applications mode is done in input-keys.c. */ - { "\033Oo", KEYC_KP_SLASH }, - { "\033Oj", KEYC_KP_STAR }, - { "\033Om", KEYC_KP_MINUS }, - { "\033Ow", KEYC_KP_SEVEN }, - { "\033Ox", KEYC_KP_EIGHT }, - { "\033Oy", KEYC_KP_NINE }, - { "\033Ok", KEYC_KP_PLUS }, - { "\033Ot", KEYC_KP_FOUR }, - { "\033Ou", KEYC_KP_FIVE }, - { "\033Ov", KEYC_KP_SIX }, - { "\033Oq", KEYC_KP_ONE }, - { "\033Or", KEYC_KP_TWO }, - { "\033Os", KEYC_KP_THREE }, - { "\033OM", KEYC_KP_ENTER }, - { "\033Op", KEYC_KP_ZERO }, - { "\033On", KEYC_KP_PERIOD }, + { "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD }, + { "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD }, + { "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD }, + { "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD }, + { "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD }, + { "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD }, + { "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD }, + { "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD }, + { "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD }, + { "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD }, + { "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD }, + { "\033Or", KEYC_KP_TWO|KEYC_KEYPAD }, + { "\033Os", KEYC_KP_THREE|KEYC_KEYPAD }, + { "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD }, + { "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD }, + { "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD }, /* Arrow keys. */ - { "\033OA", KEYC_UP }, - { "\033OB", KEYC_DOWN }, - { "\033OC", KEYC_RIGHT }, - { "\033OD", KEYC_LEFT }, + { "\033OA", KEYC_UP|KEYC_CURSOR }, + { "\033OB", KEYC_DOWN|KEYC_CURSOR }, + { "\033OC", KEYC_RIGHT|KEYC_CURSOR }, + { "\033OD", KEYC_LEFT|KEYC_CURSOR }, - { "\033[A", KEYC_UP }, - { "\033[B", KEYC_DOWN }, - { "\033[C", KEYC_RIGHT }, - { "\033[D", KEYC_LEFT }, + { "\033[A", KEYC_UP|KEYC_CURSOR }, + { "\033[B", KEYC_DOWN|KEYC_CURSOR }, + { "\033[C", KEYC_RIGHT|KEYC_CURSOR }, + { "\033[D", KEYC_LEFT|KEYC_CURSOR }, /* Other (xterm) "cursor" keys. */ { "\033OH", KEYC_HOME }, @@ -182,11 +185,59 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[201~", KEYC_PASTE_END }, }; +/* Default xterm keys. */ +struct tty_default_key_xterm { + const char *template; + key_code key; +}; +static const struct tty_default_key_xterm tty_default_xterm_keys[] = { + { "\033[1;_P", KEYC_F1 }, + { "\033O1;_P", KEYC_F1 }, + { "\033O_P", KEYC_F1 }, + { "\033[1;_Q", KEYC_F2 }, + { "\033O1;_Q", KEYC_F2 }, + { "\033O_Q", KEYC_F2 }, + { "\033[1;_R", KEYC_F3 }, + { "\033O1;_R", KEYC_F3 }, + { "\033O_R", KEYC_F3 }, + { "\033[1;_S", KEYC_F4 }, + { "\033O1;_S", KEYC_F4 }, + { "\033O_S", KEYC_F4 }, + { "\033[15;_~", KEYC_F5 }, + { "\033[17;_~", KEYC_F6 }, + { "\033[18;_~", KEYC_F7 }, + { "\033[19;_~", KEYC_F8 }, + { "\033[20;_~", KEYC_F9 }, + { "\033[21;_~", KEYC_F10 }, + { "\033[23;_~", KEYC_F11 }, + { "\033[24;_~", KEYC_F12 }, + { "\033[1;_A", KEYC_UP }, + { "\033[1;_B", KEYC_DOWN }, + { "\033[1;_C", KEYC_RIGHT }, + { "\033[1;_D", KEYC_LEFT }, + { "\033[1;_H", KEYC_HOME }, + { "\033[1;_F", KEYC_END }, + { "\033[5;_~", KEYC_PPAGE }, + { "\033[6;_~", KEYC_NPAGE }, + { "\033[2;_~", KEYC_IC }, + { "\033[3;_~", KEYC_DC }, +}; +static const key_code tty_default_xterm_modifiers[] = { + 0, + 0, + KEYC_SHIFT, + KEYC_META|KEYC_IMPLIED_META, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, + KEYC_CTRL, + KEYC_SHIFT|KEYC_CTRL, + KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, + KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL +}; + /* - * Default terminfo(5) keys. Any keys that have builtin modifiers - * (that is, where the key itself contains the modifiers) has the - * KEYC_XTERM flag set so a leading escape is not treated as meta (and - * probably removed). + * Default terminfo(5) keys. Any keys that have builtin modifiers (that is, + * where the key itself contains the modifiers) has the KEYC_XTERM flag set so + * a leading escape is not treated as meta (and probably removed). */ struct tty_default_key_code { enum tty_code_code code; @@ -207,61 +258,61 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KF11, KEYC_F11 }, { TTYC_KF12, KEYC_F12 }, - { TTYC_KF13, KEYC_F1|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF14, KEYC_F2|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF15, KEYC_F3|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF16, KEYC_F4|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF17, KEYC_F5|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF18, KEYC_F6|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF19, KEYC_F7|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF20, KEYC_F8|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF21, KEYC_F9|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF22, KEYC_F10|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF23, KEYC_F11|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF24, KEYC_F12|KEYC_SHIFT|KEYC_XTERM }, - - { TTYC_KF25, KEYC_F1|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF26, KEYC_F2|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF27, KEYC_F3|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF28, KEYC_F4|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF29, KEYC_F5|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF30, KEYC_F6|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF31, KEYC_F7|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF32, KEYC_F8|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF33, KEYC_F9|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF34, KEYC_F10|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF35, KEYC_F11|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF36, KEYC_F12|KEYC_CTRL|KEYC_XTERM }, - - { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - - { TTYC_KF49, KEYC_F1|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF50, KEYC_F2|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF51, KEYC_F3|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF52, KEYC_F4|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF53, KEYC_F5|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF54, KEYC_F6|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF55, KEYC_F7|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF56, KEYC_F8|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF57, KEYC_F9|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF58, KEYC_F10|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF59, KEYC_F11|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KF60, KEYC_F12|KEYC_ESCAPE|KEYC_XTERM }, - - { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, + { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, + { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, + { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, + { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, + { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, + { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, + { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, + { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, + { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, + { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, + { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, + { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, + + { TTYC_KF25, KEYC_F1|KEYC_CTRL }, + { TTYC_KF26, KEYC_F2|KEYC_CTRL }, + { TTYC_KF27, KEYC_F3|KEYC_CTRL }, + { TTYC_KF28, KEYC_F4|KEYC_CTRL }, + { TTYC_KF29, KEYC_F5|KEYC_CTRL }, + { TTYC_KF30, KEYC_F6|KEYC_CTRL }, + { TTYC_KF31, KEYC_F7|KEYC_CTRL }, + { TTYC_KF32, KEYC_F8|KEYC_CTRL }, + { TTYC_KF33, KEYC_F9|KEYC_CTRL }, + { TTYC_KF34, KEYC_F10|KEYC_CTRL }, + { TTYC_KF35, KEYC_F11|KEYC_CTRL }, + { TTYC_KF36, KEYC_F12|KEYC_CTRL }, + + { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, + + { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_IMPLIED_META }, + + { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, + { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, + { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, @@ -272,74 +323,74 @@ static const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KCBT, KEYC_BTAB }, /* Arrow keys from terminfo. */ - { TTYC_KCUU1, KEYC_UP }, - { TTYC_KCUD1, KEYC_DOWN }, - { TTYC_KCUB1, KEYC_LEFT }, - { TTYC_KCUF1, KEYC_RIGHT }, + { TTYC_KCUU1, KEYC_UP|KEYC_CURSOR }, + { TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR }, + { TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR }, + { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR }, /* Key and modifier capabilities. */ - { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDC3, KEYC_DC|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND2, KEYC_END|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KEND3, KEYC_END|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KEND5, KEYC_END|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC2, KEYC_IC|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KIC3, KEYC_IC|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KIC5, KEYC_IC|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KRI, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, - { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, - { TTYC_KUP5, KEYC_UP|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, - { TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, + { TTYC_KDC2, KEYC_DC|KEYC_SHIFT }, + { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, + { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, + { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, + { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, + { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KEND5, KEYC_END|KEYC_CTRL }, + { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, + { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, + { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, + { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, + { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, + { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, + { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, + { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, + { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, + { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, + { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, + { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, + { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, + { TTYC_KRI, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, + { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, + { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, + { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, }; /* Add key to tree. */ @@ -350,7 +401,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) size_t size; const char *keystr; - keystr = key_string_lookup_key(key); + keystr = key_string_lookup_key(key, 1); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); @@ -403,17 +454,30 @@ void tty_keys_build(struct tty *tty) { const struct tty_default_key_raw *tdkr; + const struct tty_default_key_xterm *tdkx; const struct tty_default_key_code *tdkc; - u_int i; + u_int i, j; const char *s; struct options_entry *o; struct options_array_item *a; union options_value *ov; + char copy[16]; + key_code key; if (tty->key_tree != NULL) tty_keys_free(tty); tty->key_tree = NULL; + for (i = 0; i < nitems(tty_default_xterm_keys); i++) { + tdkx = &tty_default_xterm_keys[i]; + for (j = 2; j < nitems(tty_default_xterm_modifiers); j++) { + strlcpy(copy, tdkx->template, sizeof copy); + copy[strcspn(copy, "_")] = '0' + j; + + key = tdkx->key|tty_default_xterm_modifiers[j]; + tty_keys_add(tty, copy, key); + } + } for (i = 0; i < nitems(tty_default_raw_keys); i++) { tdkr = &tty_default_raw_keys[i]; @@ -516,7 +580,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, enum utf8_state more; u_int i; wchar_t wc; - int n; log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, (int)len, buf, expired); @@ -534,13 +597,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, return (0); } - /* Is this an an xterm(1) key? */ - n = xterm_keys_find(buf, len, size, key); - if (n == 0) - return (0); - if (n == 1 && !expired) - return (1); - /* Is this valid UTF-8? */ more = utf8_open(&ud, (u_char)*buf); if (more == UTF8_MORE) { @@ -637,6 +693,16 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this an extended key press? */ + switch (tty_keys_extended_key(tty, buf, len, &size, &key)) { + case 0: /* yes */ + goto complete_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + first_key: /* Try to lookup complete key. */ n = tty_keys_next1(tty, buf, len, &key, &size, expired); @@ -653,7 +719,7 @@ first_key: /* Look for a key without the escape. */ n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired); if (n == 0) { /* found */ - if (key & KEYC_XTERM) { + if (key & KEYC_IMPLIED_META) { /* * We want the escape key as well as the xterm * key, because the xterm sequence implicitly @@ -665,7 +731,7 @@ first_key: size = 1; goto complete_key; } - key |= KEYC_ESCAPE; + key |= KEYC_META; size++; goto complete_key; } @@ -678,7 +744,7 @@ first_key: * escape). So pass it through even if the timer has not expired. */ if (*buf == '\033' && len >= 2) { - key = (u_char)buf[1] | KEYC_ESCAPE; + key = (u_char)buf[1] | KEYC_META; size = 2; } else { key = (u_char)buf[0]; @@ -723,7 +789,7 @@ complete_key: */ bspace = tty->tio.c_cc[VERASE]; if (bspace != _POSIX_VDISABLE && (key & KEYC_MASK_KEY) == bspace) - key = (key & KEYC_MASK_MOD) | KEYC_BSPACE; + key = (key & KEYC_MASK_MODIFIERS)|KEYC_BSPACE; /* Remove data from buffer. */ evbuffer_drain(tty->in, size); @@ -775,6 +841,96 @@ tty_keys_callback(__unused int fd, __unused short events, void *data) } /* + * Handle extended key input. This has two forms: \033[27;m;k~ and \033[k;mu, + * where k is key as a number and m is a modifier. Returns 0 for success, -1 + * for failure, 1 for partial; + */ +static int +tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, + size_t *size, key_code *key) +{ + struct client *c = tty->client; + size_t end; + u_int number, modifiers; + char tmp[64]; + + *size = 0; + + /* First two bytes are always \033[. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + + /* + * Look for a terminator. Stop at either '~' or anything that isn't a + * number or ';'. + */ + for (end = 2; end < len && end != sizeof tmp; end++) { + if (buf[end] == '~') + break; + if (!isdigit((u_char)buf[end]) && buf[end] != ';') + break; + } + if (end == len) + return (1); + if (end == sizeof tmp || (buf[end] != '~' && buf[end] != 'u')) + return (-1); + + /* Copy to the buffer. */ + memcpy(tmp, buf + 2, end); + tmp[end] = '\0'; + + /* Try to parse either form of key. */ + if (buf[end] == '~') { + if (sscanf(tmp, "27;%u;%u", &modifiers, &number) != 2) + return (-1); + } else { + if (sscanf(tmp ,"%u;%u", &number, &modifiers) != 2) + return (-1); + } + *size = end + 1; + + /* Store the key and modifiers. */ + *key = number; + switch (modifiers) { + case 2: + (*key) |= KEYC_SHIFT; + break; + case 3: + (*key) |= (KEYC_META|KEYC_IMPLIED_META); + break; + case 4: + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); + break; + case 5: + (*key) |= KEYC_CTRL; + break; + case 6: + (*key) |= (KEYC_SHIFT|KEYC_CTRL); + break; + case 7: + (*key) |= (KEYC_META|KEYC_CTRL); + break; + case 8: + (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); + break; + default: + *key = KEYC_NONE; + break; + } + if (log_get_level() != 0) { + log_debug("%s: extended key %.*s is %llx (%s)", c->name, + (int)*size, buf, *key, key_string_lookup_key(*key, 1)); + } + return (0); +} + +/* * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). */ @@ -1137,7 +1293,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, else if (strncmp(tmp, "tmux ", 5) == 0) tty_default_features(&c->term_features, "tmux", 0); else if (strncmp(tmp, "XTerm(", 6) == 0) - tty_default_features(&c->term_features, "xterm", 0); + tty_default_features(&c->term_features, "XTerm", 0); else if (strncmp(tmp, "mintty ", 7) == 0) tty_default_features(&c->term_features, "mintty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); @@ -83,6 +83,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DSEKS] = { TTYCODE_STRING, "Dseks" }, [TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" }, [TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" }, [TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" }, @@ -93,6 +94,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, [TTYC_ENBP] = { TTYCODE_STRING, "Enbp" }, + [TTYC_ENEKS] = { TTYCODE_STRING, "Eneks" }, [TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" }, [TTYC_ENMG] = { TTYCODE_STRING, "Enmg" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, @@ -286,6 +286,8 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); + if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0) + tty_update_features(tty); tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); } @@ -329,13 +331,6 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1006l\033[?1005l"); } - if (options_get_number(global_options, "focus-events")) { - tty->flags |= TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); - } - if (tty->term->flags & TERM_VT100LIKE) - tty_puts(tty, "\033[?7727h"); - evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -415,12 +410,10 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1006l\033[?1005l"); } - if (tty->flags & TTY_FOCUS) { - tty->flags &= ~TTY_FOCUS; - tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); - } if (tty->term->flags & TERM_VT100LIKE) tty_raw(tty, "\033[?7727l"); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); + tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS)); if (tty_use_margin(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); @@ -471,6 +464,12 @@ tty_update_features(struct tty *tty) if (tty_use_margin(tty)) tty_putcode(tty, TTYC_ENMG); + if (options_get_number(global_options, "extended-keys")) + tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS)); + if (options_get_number(global_options, "focus-events")) + tty_raw(tty, tty_term_string(tty->term, TTYC_ENFCS)); + if (tty->term->flags & TERM_VT100LIKE) + tty_puts(tty, "\033[?7727h"); } void |