Add a terminal feature for enable/disable extended keys (supported by

xterm and mintty) and add an option to make tmux send it. Only forward
extended keys if the application has requested them, even though we use
the CSI u sequence and xterm uses CSI 27 ~ - this is what mintty does as
well.
pull/2219/head
nicm 2020-05-16 16:44:54 +00:00
parent 292b335ca5
commit 0ab82d9531
8 changed files with 802 additions and 329 deletions

View File

@ -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 },
{ .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. 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 },
/* 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"
},
{ KEYC_UP, "\033OA", INPUTKEY_CURSOR },
{ KEYC_DOWN, "\033OB", INPUTKEY_CURSOR },
{ KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR },
{ KEYC_LEFT, "\033OD", INPUTKEY_CURSOR },
/* 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 = "."
},
{ 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 },
/* 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);

19
input.c
View File

@ -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;

View File

@ -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. */

18
tmux.1
View File

@ -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

View File

@ -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;

View File

@ -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_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|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_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|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_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_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_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_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_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);
@ -774,6 +840,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);

View File

@ -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" },

21
tty.c
View File

@ -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