xterm-keys has been on by default for 5 years and all other modern terminals

use these key sequences by default. Merge the code into the main tty and input
tree processing (convering the latter to use a tree rather than a table at the
same time) and make the option a no-op.
This commit is contained in:
Nicholas Marriott
2020-05-15 10:31:54 +01:00
parent c4d8100b2f
commit 5ee4d991b6
9 changed files with 471 additions and 449 deletions

View File

@@ -184,8 +184,7 @@ dist_tmux_SOURCES = \
window-tree.c \ window-tree.c \
window.c \ window.c \
xmalloc.c \ xmalloc.c \
xmalloc.h \ xmalloc.h
xterm-keys.c
nodist_tmux_SOURCES = osdep-@PLATFORM@.c nodist_tmux_SOURCES = osdep-@PLATFORM@.c
# Add compat file for forkpty. # Add compat file for forkpty.

View File

@@ -32,112 +32,331 @@
static void input_key_mouse(struct window_pane *, struct mouse_event *); static void input_key_mouse(struct window_pane *, struct mouse_event *);
struct input_key_ent { /* Entry in the key tree. */
struct input_key_entry {
key_code key; key_code key;
const char *data; const char *data;
int flags; RB_ENTRY(input_key_entry) entry;
#define INPUTKEY_KEYPAD 0x1 /* keypad key */
#define INPUTKEY_CURSOR 0x2 /* cursor key */
}; };
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. */ /* Paste keys. */
{ KEYC_PASTE_START, "\033[200~", 0 }, { .key = KEYC_PASTE_START,
{ KEYC_PASTE_END, "\033[201~", 0 }, .data = "\033[200~"
},
{ .key = KEYC_PASTE_END,
.data = "\033[201~"
},
/* Function keys. */ /* Function keys. */
{ KEYC_F1, "\033OP", 0 }, { .key = KEYC_F1,
{ KEYC_F2, "\033OQ", 0 }, .data = "\033OP"
{ KEYC_F3, "\033OR", 0 }, },
{ KEYC_F4, "\033OS", 0 }, { .key = KEYC_F2,
{ KEYC_F5, "\033[15~", 0 }, .data = "\033OQ"
{ KEYC_F6, "\033[17~", 0 }, },
{ KEYC_F7, "\033[18~", 0 }, { .key = KEYC_F3,
{ KEYC_F8, "\033[19~", 0 }, .data = "\033OR"
{ KEYC_F9, "\033[20~", 0 }, },
{ KEYC_F10, "\033[21~", 0 }, { .key = KEYC_F4,
{ KEYC_F11, "\033[23~", 0 }, .data = "\033OS"
{ KEYC_F12, "\033[24~", 0 }, },
{ KEYC_F1|KEYC_SHIFT, "\033[25~", 0 }, { .key = KEYC_F5,
{ KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, .data = "\033[15~"
{ KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, },
{ KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, { .key = KEYC_F6,
{ KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, .data = "\033[17~"
{ KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, },
{ KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, { .key = KEYC_F7,
{ KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, .data = "\033[18~"
{ KEYC_IC, "\033[2~", 0 }, },
{ KEYC_DC, "\033[3~", 0 }, { .key = KEYC_F8,
{ KEYC_HOME, "\033[1~", 0 }, .data = "\033[19~"
{ KEYC_END, "\033[4~", 0 }, },
{ KEYC_NPAGE, "\033[6~", 0 }, { .key = KEYC_F9,
{ KEYC_PPAGE, "\033[5~", 0 }, .data = "\033[20~"
{ KEYC_BTAB, "\033[Z", 0 }, },
{ .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. */
* Arrow keys. Cursor versions must come first. The codes are toggled { .key = KEYC_UP|KEYC_CURSOR,
* between CSI and SS3 versions when ctrl is pressed. .data = "\033OA"
*/ },
{ KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, { .key = KEYC_DOWN|KEYC_CURSOR,
{ KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, .data = "\033OB"
{ KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, },
{ KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, { .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 }, /* Keypad keys. */
{ KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, { .key = KEYC_KP_SLASH|KEYC_KEYPAD,
{ KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, .data = "\033Oo"
{ KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, },
{ .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 }, /* Keys with an embedded modifier. */
{ KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, { .key = KEYC_F1|KEYC_XTERM,
{ KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, .data = "\033[1;_P"
{ KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, },
{ .key = KEYC_F2|KEYC_XTERM,
{ KEYC_UP, "\033[A", 0 }, .data = "\033[1;_Q"
{ KEYC_DOWN, "\033[B", 0 }, },
{ KEYC_RIGHT, "\033[C", 0 }, { .key = KEYC_F3|KEYC_XTERM,
{ KEYC_LEFT, "\033[D", 0 }, .data = "\033[1;_R"
},
/* Keypad keys. Keypad versions must come first. */ { .key = KEYC_F4|KEYC_XTERM,
{ KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, .data = "\033[1;_S"
{ KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, },
{ KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, { .key = KEYC_F5|KEYC_XTERM,
{ KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, .data = "\033[15;_~"
{ KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, },
{ KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, { .key = KEYC_F6|KEYC_XTERM,
{ KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, .data = "\033[17;_~"
{ KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, },
{ KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, { .key = KEYC_F7|KEYC_XTERM,
{ KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, .data = "\033[18;_~"
{ KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, },
{ KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, { .key = KEYC_F8|KEYC_XTERM,
{ KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, .data = "\033[19;_~"
{ KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, },
{ KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, { .key = KEYC_F9|KEYC_XTERM,
{ KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, .data = "\033[20;_~"
},
{ KEYC_KP_SLASH, "/", 0 }, { .key = KEYC_F10|KEYC_XTERM,
{ KEYC_KP_STAR, "*", 0 }, .data = "\033[21;_~"
{ KEYC_KP_MINUS, "-", 0 }, },
{ KEYC_KP_SEVEN, "7", 0 }, { .key = KEYC_F11|KEYC_XTERM,
{ KEYC_KP_EIGHT, "8", 0 }, .data = "\033[23;_~"
{ KEYC_KP_NINE, "9", 0 }, },
{ KEYC_KP_PLUS, "+", 0 }, { .key = KEYC_F12|KEYC_XTERM,
{ KEYC_KP_FOUR, "4", 0 }, .data = "\033[24;_~"
{ KEYC_KP_FIVE, "5", 0 }, },
{ KEYC_KP_SIX, "6", 0 }, { .key = KEYC_UP|KEYC_XTERM,
{ KEYC_KP_ONE, "1", 0 }, .data = "\033[1;_A"
{ KEYC_KP_TWO, "2", 0 }, },
{ KEYC_KP_THREE, "3", 0 }, { .key = KEYC_DOWN|KEYC_XTERM,
{ KEYC_KP_ENTER, "\n", 0 }, .data = "\033[1;_B"
{ KEYC_KP_ZERO, "0", 0 }, },
{ KEYC_KP_PERIOD, ".", 0 }, { .key = KEYC_RIGHT|KEYC_XTERM,
.data = "\033[1;_C"
},
{ .key = KEYC_LEFT|KEYC_XTERM,
.data = "\033[1;_D"
},
{ .key = KEYC_HOME|KEYC_XTERM,
.data = "\033[1;_H"
},
{ .key = KEYC_END|KEYC_XTERM,
.data = "\033[1;_F"
},
{ .key = KEYC_PPAGE|KEYC_XTERM,
.data = "\033[5;_~"
},
{ .key = KEYC_NPAGE|KEYC_XTERM,
.data = "\033[6;_~"
},
{ .key = KEYC_IC|KEYC_XTERM,
.data = "\033[2;_~"
},
{ .key = KEYC_DC|KEYC_XTERM,
.data = "\033[3;_~" }
}; };
static const key_code input_key_modifiers[] = {
0,
0,
KEYC_SHIFT|KEYC_XTERM,
KEYC_ESCAPE|KEYC_XTERM,
KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM,
KEYC_CTRL|KEYC_XTERM,
KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM,
KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM,
KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM
};
/* 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);
}
/* Split a character into two UTF-8 bytes. */ /* Split a character into two UTF-8 bytes. */
static size_t static size_t
input_split2(u_int c, u_char *dst) input_key_split2(u_int c, u_char *dst)
{ {
if (c > 0x7f) { if (c > 0x7f) {
dst[0] = (c >> 6) | 0xc0; dst[0] = (c >> 6) | 0xc0;
@@ -148,30 +367,61 @@ input_split2(u_int c, u_char *dst)
return (1); return (1);
} }
/* Build input key tree. */
void
input_key_build(void)
{
struct input_key_entry *ike, *new;
u_int i, j;
char *data;
for (i = 0; i < nitems(input_key_defaults); i++) {
ike = &input_key_defaults[i];
if (~ike->key & KEYC_XTERM) {
RB_INSERT(input_key_tree, &input_key_tree, ike);
continue;
}
for (j = 2; j < nitems(input_key_modifiers); j++) {
data = xstrdup(ike->data);
data[strcspn(data, "_")] = '0' + j;
new = xcalloc(1, sizeof *new);
new->key = ike->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), ike->data);
}
}
/* Translate a key code into an output key sequence for a pane. */ /* Translate a key code into an output key sequence for a pane. */
int int
input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
{ {
if (log_get_level() != 0) {
log_debug("writing key 0x%llx (%s) to %%%u", key, log_debug("writing key 0x%llx (%s) to %%%u", key,
key_string_lookup_key(key), wp->id); key_string_lookup_key(key), wp->id);
}
if (KEYC_IS_MOUSE(key)) { if (KEYC_IS_MOUSE(key)) {
if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
input_key_mouse(wp, m); input_key_mouse(wp, m);
return (0); 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. */ /* Translate a key code into an output key sequence. */
int int
input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev, input_key(struct screen *s, struct bufferevent *bev, key_code key)
key_code key)
{ {
const struct input_key_ent *ike; struct input_key_entry *ike, entry;
u_int i; size_t datalen;
size_t dlen;
char *out;
key_code justkey, newkey; key_code justkey, newkey;
struct utf8_data ud; struct utf8_data ud;
@@ -216,43 +466,25 @@ input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev,
} }
/* /*
* Then try to look this up as an xterm key, if the flag to output them * Look up in the tree. If not in application keypad or cursor mode,
* is set. * remove the flags from the key.
*/ */
if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) { if (~s->mode & MODE_KKEYPAD)
if ((out = xterm_keys_lookup(key)) != NULL) { key &= ~KEYC_KEYPAD;
bufferevent_write(bev, out, strlen(out)); if (~s->mode & MODE_KCURSOR)
free(out); key &= ~KEYC_CURSOR;
return (0); entry.key = key;
} if ((ike = RB_FIND(input_key_tree, &input_key_tree, &entry)) == NULL) {
}
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;
}
if (i == nitems(input_keys)) {
log_debug("key 0x%llx missing", key); log_debug("key 0x%llx missing", key);
return (-1); return (-1);
} }
dlen = strlen(ike->data); datalen = strlen(ike->data);
log_debug("found key 0x%llx: \"%s\"", key, ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data);
/* Prefix a \033 for escape. */ /* Prefix a \033 for escape. */
if (key & KEYC_ESCAPE) if (key & KEYC_ESCAPE)
bufferevent_write(bev, "\033", 1); bufferevent_write(bev, "\033", 1);
bufferevent_write(bev, ike->data, dlen); bufferevent_write(bev, ike->data, datalen);
return (0); return (0);
} }
@@ -308,9 +540,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) if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33)
return (0); return (0);
len = xsnprintf(buf, sizeof buf, "\033[M"); len = xsnprintf(buf, sizeof buf, "\033[M");
len += input_split2(m->b + 32, &buf[len]); len += input_key_split2(m->b + 32, &buf[len]);
len += input_split2(x + 33, &buf[len]); len += input_key_split2(x + 33, &buf[len]);
len += input_split2(y + 33, &buf[len]); len += input_key_split2(y + 33, &buf[len]);
} else { } else {
if (m->b > 223) if (m->b > 223)
return (0); return (0);

View File

@@ -1052,11 +1052,12 @@ const struct options_table_entry options_table[] = {
"bottom." "bottom."
}, },
{ .name = "xterm-keys", { .name = "xterm-keys", /* no longer used */
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
.default_num = 1, .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. */ /* Hook options. */

View File

@@ -328,7 +328,7 @@ popup_key_cb(struct client *c, struct key_event *event)
bufferevent_write(job_get_event(pd->job), buf, len); bufferevent_write(job_get_event(pd->job), buf, len);
return (0); return (0);
} }
input_key(NULL, &pd->s, job_get_event(pd->job), event->key); input_key(&pd->s, job_get_event(pd->job), event->key);
return (0); return (0);
} }

View File

@@ -198,6 +198,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
"tty ps", NULL) != 0) "tty ps", NULL) != 0)
fatal("pledge failed"); fatal("pledge failed");
input_key_build();
RB_INIT(&windows); RB_INIT(&windows);
RB_INIT(&all_window_panes); RB_INIT(&all_window_panes);
TAILQ_INIT(&clients); TAILQ_INIT(&clients);

10
tmux.1
View File

@@ -4054,16 +4054,6 @@ option.
.Xc .Xc
If this option is set, searches will wrap around the end of the pane contents. If this option is set, searches will wrap around the end of the pane contents.
The default is on. 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 .El
.Pp .Pp
Available pane options are: Available pane options are:

10
tmux.h
View File

@@ -125,6 +125,8 @@ struct winlink;
#define KEYC_SHIFT 0x0400000000000ULL #define KEYC_SHIFT 0x0400000000000ULL
#define KEYC_XTERM 0x0800000000000ULL #define KEYC_XTERM 0x0800000000000ULL
#define KEYC_LITERAL 0x1000000000000ULL #define KEYC_LITERAL 0x1000000000000ULL
#define KEYC_KEYPAD 0x2000000000000ULL
#define KEYC_CURSOR 0x4000000000000ULL
/* Available user keys. */ /* Available user keys. */
#define KEYC_NUSER 1000 #define KEYC_NUSER 1000
@@ -2417,16 +2419,12 @@ void input_parse_screen(struct input_ctx *, struct screen *,
screen_write_init_ctx_cb, void *, u_char *, size_t); screen_write_init_ctx_cb, void *, u_char *, size_t);
/* input-key.c */ /* input-key.c */
void input_key_build(void);
int input_key_pane(struct window_pane *, key_code, struct mouse_event *); int input_key_pane(struct window_pane *, key_code, struct mouse_event *);
int input_key(struct window_pane *, struct screen *, struct bufferevent *, int input_key(struct screen *, struct bufferevent *, key_code);
key_code);
int input_key_get_mouse(struct screen *, struct mouse_event *, u_int, int input_key_get_mouse(struct screen *, struct mouse_event *, u_int,
u_int, const char **, size_t *); u_int, const char **, size_t *);
/* xterm-keys.c */
char *xterm_keys_lookup(key_code);
int xterm_keys_find(const char *, size_t, size_t *, key_code *);
/* colour.c */ /* colour.c */
int colour_find_rgb(u_char, u_char, u_char); int colour_find_rgb(u_char, u_char, u_char);
int colour_join_rgb(u_char, u_char, u_char); int colour_join_rgb(u_char, u_char, u_char);

View File

@@ -69,33 +69,33 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
* put the terminal into keypad_xmit mode. Translation of numbers * put the terminal into keypad_xmit mode. Translation of numbers
* mode/applications mode is done in input-keys.c. * mode/applications mode is done in input-keys.c.
*/ */
{ "\033Oo", KEYC_KP_SLASH }, { "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD },
{ "\033Oj", KEYC_KP_STAR }, { "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD },
{ "\033Om", KEYC_KP_MINUS }, { "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD },
{ "\033Ow", KEYC_KP_SEVEN }, { "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD },
{ "\033Ox", KEYC_KP_EIGHT }, { "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD },
{ "\033Oy", KEYC_KP_NINE }, { "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD },
{ "\033Ok", KEYC_KP_PLUS }, { "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD },
{ "\033Ot", KEYC_KP_FOUR }, { "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD },
{ "\033Ou", KEYC_KP_FIVE }, { "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD },
{ "\033Ov", KEYC_KP_SIX }, { "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD },
{ "\033Oq", KEYC_KP_ONE }, { "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD },
{ "\033Or", KEYC_KP_TWO }, { "\033Or", KEYC_KP_TWO|KEYC_KEYPAD },
{ "\033Os", KEYC_KP_THREE }, { "\033Os", KEYC_KP_THREE|KEYC_KEYPAD },
{ "\033OM", KEYC_KP_ENTER }, { "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD },
{ "\033Op", KEYC_KP_ZERO }, { "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD },
{ "\033On", KEYC_KP_PERIOD }, { "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD },
/* Arrow keys. */ /* Arrow keys. */
{ "\033OA", KEYC_UP }, { "\033OA", KEYC_UP|KEYC_CURSOR },
{ "\033OB", KEYC_DOWN }, { "\033OB", KEYC_DOWN|KEYC_CURSOR },
{ "\033OC", KEYC_RIGHT }, { "\033OC", KEYC_RIGHT|KEYC_CURSOR },
{ "\033OD", KEYC_LEFT }, { "\033OD", KEYC_LEFT|KEYC_CURSOR },
{ "\033[A", KEYC_UP }, { "\033[A", KEYC_UP|KEYC_CURSOR },
{ "\033[B", KEYC_DOWN }, { "\033[B", KEYC_DOWN|KEYC_CURSOR },
{ "\033[C", KEYC_RIGHT }, { "\033[C", KEYC_RIGHT|KEYC_CURSOR },
{ "\033[D", KEYC_LEFT }, { "\033[D", KEYC_LEFT|KEYC_CURSOR },
/* Other (xterm) "cursor" keys. */ /* Other (xterm) "cursor" keys. */
{ "\033OH", KEYC_HOME }, { "\033OH", KEYC_HOME },
@@ -182,11 +182,59 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[201~", KEYC_PASTE_END }, { "\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_XTERM,
KEYC_ESCAPE|KEYC_XTERM,
KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM,
KEYC_CTRL|KEYC_XTERM,
KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM,
KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM,
KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM
};
/* /*
* Default terminfo(5) keys. Any keys that have builtin modifiers * Default terminfo(5) keys. Any keys that have builtin modifiers (that is,
* (that is, where the key itself contains the modifiers) has the * where the key itself contains the modifiers) has the KEYC_XTERM flag set so
* KEYC_XTERM flag set so a leading escape is not treated as meta (and * a leading escape is not treated as meta (and probably removed).
* probably removed).
*/ */
struct tty_default_key_code { struct tty_default_key_code {
enum tty_code_code code; enum tty_code_code code;
@@ -272,10 +320,10 @@ static const struct tty_default_key_code tty_default_code_keys[] = {
{ TTYC_KCBT, KEYC_BTAB }, { TTYC_KCBT, KEYC_BTAB },
/* Arrow keys from terminfo. */ /* Arrow keys from terminfo. */
{ TTYC_KCUU1, KEYC_UP }, { TTYC_KCUU1, KEYC_UP|KEYC_CURSOR },
{ TTYC_KCUD1, KEYC_DOWN }, { TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR },
{ TTYC_KCUB1, KEYC_LEFT }, { TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR },
{ TTYC_KCUF1, KEYC_RIGHT }, { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR },
/* Key and modifier capabilities. */ /* Key and modifier capabilities. */
{ TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM },
@@ -403,17 +451,30 @@ void
tty_keys_build(struct tty *tty) tty_keys_build(struct tty *tty)
{ {
const struct tty_default_key_raw *tdkr; const struct tty_default_key_raw *tdkr;
const struct tty_default_key_xterm *tdkx;
const struct tty_default_key_code *tdkc; const struct tty_default_key_code *tdkc;
u_int i; u_int i, j;
const char *s; const char *s;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a; struct options_array_item *a;
union options_value *ov; union options_value *ov;
char copy[16];
key_code key;
if (tty->key_tree != NULL) if (tty->key_tree != NULL)
tty_keys_free(tty); tty_keys_free(tty);
tty->key_tree = NULL; 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++) { for (i = 0; i < nitems(tty_default_raw_keys); i++) {
tdkr = &tty_default_raw_keys[i]; tdkr = &tty_default_raw_keys[i];
@@ -516,7 +577,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
enum utf8_state more; enum utf8_state more;
u_int i; u_int i;
wchar_t wc; wchar_t wc;
int n;
log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len,
(int)len, buf, expired); (int)len, buf, expired);
@@ -534,13 +594,6 @@ tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
return (0); 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? */ /* Is this valid UTF-8? */
more = utf8_open(&ud, (u_char)*buf); more = utf8_open(&ud, (u_char)*buf);
if (more == UTF8_MORE) { if (more == UTF8_MORE) {

View File

@@ -1,252 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
/*
* xterm-style function keys append one of the following values before the last
* character:
*
* 2 Shift
* 3 Alt
* 4 Shift + Alt
* 5 Ctrl
* 6 Shift + Ctrl
* 7 Alt + Ctrl
* 8 Shift + Alt + Ctrl
*
* Rather than parsing them, just match against a table.
*
* There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P).
* We accept any but always output the latter (it comes first in the table).
*/
static int xterm_keys_match(const char *, const char *, size_t, size_t *,
key_code *);
static int xterm_keys_modifiers(const char *, size_t, size_t *,
key_code *);
struct xterm_keys_entry {
key_code key;
const char *template;
};
static const struct xterm_keys_entry xterm_keys_table[] = {
{ KEYC_F1, "\033[1;_P" },
{ KEYC_F1, "\033O1;_P" },
{ KEYC_F1, "\033O_P" },
{ KEYC_F2, "\033[1;_Q" },
{ KEYC_F2, "\033O1;_Q" },
{ KEYC_F2, "\033O_Q" },
{ KEYC_F3, "\033[1;_R" },
{ KEYC_F3, "\033O1;_R" },
{ KEYC_F3, "\033O_R" },
{ KEYC_F4, "\033[1;_S" },
{ KEYC_F4, "\033O1;_S" },
{ KEYC_F4, "\033O_S" },
{ KEYC_F5, "\033[15;_~" },
{ KEYC_F6, "\033[17;_~" },
{ KEYC_F7, "\033[18;_~" },
{ KEYC_F8, "\033[19;_~" },
{ KEYC_F9, "\033[20;_~" },
{ KEYC_F10, "\033[21;_~" },
{ KEYC_F11, "\033[23;_~" },
{ KEYC_F12, "\033[24;_~" },
{ KEYC_UP, "\033[1;_A" },
{ KEYC_DOWN, "\033[1;_B" },
{ KEYC_RIGHT, "\033[1;_C" },
{ KEYC_LEFT, "\033[1;_D" },
{ KEYC_HOME, "\033[1;_H" },
{ KEYC_END, "\033[1;_F" },
{ KEYC_PPAGE, "\033[5;_~" },
{ KEYC_NPAGE, "\033[6;_~" },
{ KEYC_IC, "\033[2;_~" },
{ KEYC_DC, "\033[3;_~" },
{ '!', "\033[27;_;33~" },
{ '#', "\033[27;_;35~" },
{ '(', "\033[27;_;40~" },
{ ')', "\033[27;_;41~" },
{ '+', "\033[27;_;43~" },
{ ',', "\033[27;_;44~" },
{ '-', "\033[27;_;45~" },
{ '.', "\033[27;_;46~" },
{ '0', "\033[27;_;48~" },
{ '1', "\033[27;_;49~" },
{ '2', "\033[27;_;50~" },
{ '3', "\033[27;_;51~" },
{ '4', "\033[27;_;52~" },
{ '5', "\033[27;_;53~" },
{ '6', "\033[27;_;54~" },
{ '7', "\033[27;_;55~" },
{ '8', "\033[27;_;56~" },
{ '9', "\033[27;_;57~" },
{ ':', "\033[27;_;58~" },
{ ';', "\033[27;_;59~" },
{ '<', "\033[27;_;60~" },
{ '=', "\033[27;_;61~" },
{ '>', "\033[27;_;62~" },
{ '?', "\033[27;_;63~" },
{ '\'', "\033[27;_;39~" },
{ '\r', "\033[27;_;13~" },
{ '\t', "\033[27;_;9~" },
};
/*
* Match key against buffer, treating _ as a wildcard. Return -1 for no match,
* 0 for match, 1 if the end of the buffer is reached (need more data).
*/
static int
xterm_keys_match(const char *template, const char *buf, size_t len,
size_t *size, key_code *modifiers)
{
size_t pos;
int retval;
*modifiers = 0;
if (len == 0)
return (0);
pos = 0;
do {
if (*template == '_') {
retval = xterm_keys_modifiers(buf, len, &pos,
modifiers);
if (retval != 0)
return (retval);
continue;
}
if (buf[pos] != *template)
return (-1);
pos++;
} while (*++template != '\0' && pos != len);
if (*template != '\0') /* partial */
return (1);
*size = pos;
return (0);
}
/* Find modifiers from buffer. */
static int
xterm_keys_modifiers(const char *buf, size_t len, size_t *pos,
key_code *modifiers)
{
u_int flags;
if (len - *pos < 2)
return (1);
if (buf[*pos] < '0' || buf[*pos] > '9')
return (-1);
flags = buf[(*pos)++] - '0';
if (buf[*pos] >= '0' && buf[*pos] <= '9')
flags = (flags * 10) + (buf[(*pos)++] - '0');
flags -= 1;
*modifiers = 0;
if (flags & 1)
*modifiers |= KEYC_SHIFT;
if (flags & 2)
*modifiers |= KEYC_ESCAPE;
if (flags & 4)
*modifiers |= KEYC_CTRL;
if (flags & 8)
*modifiers |= KEYC_ESCAPE;
return (0);
}
/*
* Lookup key from a buffer against the table. Returns 0 for found (and the
* key), -1 for not found, 1 for partial match.
*/
int
xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key)
{
const struct xterm_keys_entry *entry;
u_int i;
int matched;
key_code modifiers;
for (i = 0; i < nitems(xterm_keys_table); i++) {
entry = &xterm_keys_table[i];
matched = xterm_keys_match(entry->template, buf, len, size,
&modifiers);
if (matched == -1)
continue;
if (matched == 0)
*key = (entry->key|modifiers|KEYC_XTERM);
return (matched);
}
return (-1);
}
/* Lookup a key number from the table. */
char *
xterm_keys_lookup(key_code key)
{
const struct xterm_keys_entry *entry;
u_int i;
key_code modifiers;
char *out;
modifiers = 1;
if (key & KEYC_SHIFT)
modifiers += 1;
if (key & KEYC_ESCAPE)
modifiers += 2;
if (key & KEYC_CTRL)
modifiers += 4;
/*
* If the key has no modifiers, return NULL and let it fall through to
* the normal lookup.
*/
if (modifiers == 1)
return (NULL);
/*
* If this has the escape modifier, but was not originally an xterm
* key, it may be a genuine escape + key. So don't pass it through as
* an xterm key or programs like vi may be confused.
*/
if ((key & (KEYC_ESCAPE|KEYC_XTERM)) == KEYC_ESCAPE)
return (NULL);
/* Otherwise, find the key in the table. */
key &= KEYC_MASK_KEY;
for (i = 0; i < nitems(xterm_keys_table); i++) {
entry = &xterm_keys_table[i];
if (key == entry->key)
break;
}
if (i == nitems(xterm_keys_table))
return (NULL);
/* Copy the template and replace the modifier. */
out = xstrdup(entry->template);
out[strcspn(out, "_")] = '0' + modifiers;
return (out);
}