#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static void input_key_mouse(struct window_pane *, struct mouse_event *);
struct input_key_entry {
key_code key;
const char *data;
RB_ENTRY(input_key_entry) entry;
};
RB_HEAD(input_key_tree, input_key_entry);
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);
static struct input_key_entry input_key_defaults[] = {
{ .key = KEYC_PASTE_START,
.data = "\033[200~"
},
{ .key = KEYC_PASTE_START|KEYC_IMPLIED_META,
.data = "\033[200~"
},
{ .key = KEYC_PASTE_END,
.data = "\033[201~"
},
{ .key = KEYC_PASTE_END|KEYC_IMPLIED_META,
.data = "\033[201~"
},
{ .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_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"
},
{ .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"
},
{ .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 = "."
},
{ .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
};
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);
}
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));
}
static size_t
input_key_split2(u_int c, u_char *dst)
{
if (c > 0x7f) {
dst[0] = (c >> 6) | 0xc0;
dst[1] = (c & 0x3f) | 0x80;
return (2);
}
dst[0] = c;
return (1);
}
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);
}
}
int
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,
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->screen, wp->event, key));
}
static void
input_key_write(const char *from, struct bufferevent *bev, const char *data,
size_t size)
{
log_debug("%s: %.*s", from, (int)size, data);
bufferevent_write(bev, data, size);
}
static int
input_key_extended(struct bufferevent *bev, key_code key)
{
char tmp[64], modifier;
struct utf8_data ud;
wchar_t wc;
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;
default:
return (-1);
}
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key & KEYC_MASK_KEY, &ud);
if (utf8_towc(&ud, &wc) == UTF8_DONE)
key = wc;
else
return (-1);
} else
key &= KEYC_MASK_KEY;
if (options_get_number(global_options, "extended-keys-format") == 1)
xsnprintf(tmp, sizeof tmp, "\033[27;%c;%llu~", modifier, key);
else
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", key, modifier);
input_key_write(__func__, bev, tmp, strlen(tmp));
return (0);
}
static int
input_key_vt10x(struct bufferevent *bev, key_code key)
{
struct utf8_data ud;
key_code onlykey;
char *p;
static const char *standard_map[2] = {
"1!9(0)=+;:'\",<.>/-8? 2",
"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
};
log_debug("%s: key in %llx", __func__, key);
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
onlykey = key & KEYC_MASK_KEY;
if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
key &= ~KEYC_CTRL;
if (key & KEYC_CTRL) {
p = strchr(standard_map[0], onlykey);
if (p != NULL)
key = standard_map[1][p - standard_map[0]];
else if (onlykey >= '3' && onlykey <= '7')
key = onlykey - '\030';
else if (onlykey >= '@' && onlykey <= '~')
key = onlykey & 0x1f;
else
return (-1);
}
log_debug("%s: key out %llx", __func__, key);
ud.data[0] = key & 0x7f;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
static int
input_key_mode1(struct bufferevent *bev, key_code key)
{
key_code onlykey;
log_debug("%s: key in %llx", __func__, key);
if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META)
return (input_key_vt10x(bev, key));
onlykey = key & KEYC_MASK_KEY;
if ((key & KEYC_CTRL) &&
(onlykey == ' ' ||
onlykey == '/' ||
onlykey == '@' ||
onlykey == '^' ||
(onlykey >= '2' && onlykey <= '8') ||
(onlykey >= '@' && onlykey <= '~')))
return (input_key_vt10x(bev, key));
return (-1);
}
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
{
struct input_key_entry *ike = NULL;
key_code newkey;
struct utf8_data ud;
if (KEYC_IS_MOUSE(key))
return (0);
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
log_debug("%s: key 0x%llx is backspace -> 0x%llx", __func__,
key, newkey);
if ((key & KEYC_MASK_MODIFIERS) == 0) {
ud.data[0] = 255;
if ((newkey & KEYC_MASK_MODIFIERS) == 0)
ud.data[0] = newkey;
else if ((newkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) {
newkey &= KEYC_MASK_KEY;
if (newkey == '?')
ud.data[0] = 0x7f;
else if (newkey >= '@' && newkey <= '_')
ud.data[0] = newkey - 0x40;
else if (newkey >= 'a' && newkey <= 'z')
ud.data[0] = newkey - 0x60;
}
if (ud.data[0] != 255)
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
key = newkey|(key & (KEYC_MASK_FLAGS|KEYC_MASK_MODIFIERS));
}
if ((key & KEYC_MASK_KEY) == KEYC_BTAB) {
if (s->mode & MODE_KEYS_EXTENDED_2) {
key = '\011' | (key & ~KEYC_MASK_KEY) | KEYC_SHIFT;
} else {
key &= ~KEYC_MASK_MODIFIERS;
}
}
if (!(key & ~KEYC_MASK_KEY)) {
if (key == C0_HT ||
key == C0_CR ||
key == C0_ESC ||
(key >= 0x20 && key <= 0x7f)) {
ud.data[0] = key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
}
if (~s->mode & MODE_KKEYPAD)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
if (ike == NULL)
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 && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike != NULL) {
log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
ike->data);
if (KEYC_IS_PASTE(key) && (~s->mode & MODE_BRACKETPASTE))
return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
return (0);
}
if (KEYC_IS_USER(key) || KEYC_IS_SPECIAL(key) || KEYC_IS_MOUSE(key)) {
log_debug("%s: ignoring key 0x%llx", __func__, key);
return (0);
}
switch (s->mode & EXTENDED_KEY_MODES) {
case MODE_KEYS_EXTENDED_2:
return (input_key_extended(bev, key));
case MODE_KEYS_EXTENDED:
if (input_key_mode1(bev, key) == -1)
return (input_key_extended(bev, key));
return (0);
default:
return (input_key_vt10x(bev, key));
}
}
int
input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,
const char **rbuf, size_t *rlen)
{
static char buf[40];
size_t len;
*rbuf = NULL;
*rlen = 0;
if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0)
return (0);
if ((s->mode & ALL_MOUSE_MODES) == 0)
return (0);
if (m->sgr_type != ' ') {
if (MOUSE_DRAG(m->sgr_b) &&
MOUSE_RELEASE(m->sgr_b) &&
(~s->mode & MODE_MOUSE_ALL))
return (0);
} else {
if (MOUSE_DRAG(m->b) &&
MOUSE_RELEASE(m->b) &&
MOUSE_RELEASE(m->lb) &&
(~s->mode & MODE_MOUSE_ALL))
return (0);
}
if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) {
len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
m->sgr_b, x + 1, y + 1, m->sgr_type);
} else if (s->mode & MODE_MOUSE_UTF8) {
if (m->b > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_BTN_OFF ||
x > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF ||
y > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF)
return (0);
len = xsnprintf(buf, sizeof buf, "\033[M");
len += input_key_split2(m->b + MOUSE_PARAM_BTN_OFF, &buf[len]);
len += input_key_split2(x + MOUSE_PARAM_POS_OFF, &buf[len]);
len += input_key_split2(y + MOUSE_PARAM_POS_OFF, &buf[len]);
} else {
if (m->b + MOUSE_PARAM_BTN_OFF > MOUSE_PARAM_MAX)
return (0);
len = xsnprintf(buf, sizeof buf, "\033[M");
buf[len++] = m->b + MOUSE_PARAM_BTN_OFF;
if (x + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX)
buf[len++] = MOUSE_PARAM_MAX;
else
buf[len++] = x + MOUSE_PARAM_POS_OFF;
if (y + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX)
buf[len++] = MOUSE_PARAM_MAX;
else
buf[len++] = y + MOUSE_PARAM_POS_OFF;
}
*rbuf = buf;
*rlen = len;
return (1);
}
static void
input_key_mouse(struct window_pane *wp, struct mouse_event *m)
{
struct screen *s = wp->screen;
u_int x, y;
const char *buf;
size_t len;
if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0)
return;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
if (!window_pane_visible(wp))
return;
if (!input_key_get_mouse(s, m, x, y, &buf, &len))
return;
log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
input_key_write(__func__, wp->event, buf, len);
}