mirror of
https://github.com/tmux/tmux.git
synced 2024-12-12 17:38:48 +00:00
483cc77c1c
rather than only forwarding them to the program inside. From Andrew Onyshchuk in GitHub issue 3431.
688 lines
16 KiB
C
688 lines
16 KiB
C
/* $OpenBSD$ */
|
|
|
|
/*
|
|
* Copyright (c) 2007 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 <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tmux.h"
|
|
|
|
/*
|
|
* This file is rather misleadingly named, it contains the code which takes a
|
|
* key code and translates it into something suitable to be sent to the
|
|
* application running in a pane (similar to input.c does in the other
|
|
* direction with output).
|
|
*/
|
|
|
|
static void input_key_mouse(struct window_pane *, struct mouse_event *);
|
|
|
|
/* Entry in the key tree. */
|
|
struct input_key_entry {
|
|
key_code key;
|
|
const char *data;
|
|
|
|
RB_ENTRY(input_key_entry) entry;
|
|
};
|
|
RB_HEAD(input_key_tree, input_key_entry);
|
|
|
|
/* 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. */
|
|
{ .key = KEYC_PASTE_START,
|
|
.data = "\033[200~"
|
|
},
|
|
{ .key = KEYC_PASTE_END,
|
|
.data = "\033[201~"
|
|
},
|
|
|
|
/* Function keys. */
|
|
{ .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"
|
|
},
|
|
|
|
/* 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;_~"
|
|
},
|
|
|
|
/* Tab and modifiers. */
|
|
{ .key = '\011'|KEYC_CTRL,
|
|
.data = "\011"
|
|
},
|
|
{ .key = '\011'|KEYC_CTRL|KEYC_EXTENDED,
|
|
.data = "\033[9;5u"
|
|
},
|
|
{ .key = '\011'|KEYC_CTRL|KEYC_SHIFT,
|
|
.data = "\033[Z"
|
|
},
|
|
{ .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED,
|
|
.data = "\033[1;5Z"
|
|
}
|
|
};
|
|
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_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);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* Translate a key code into an output key sequence. */
|
|
int
|
|
input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
|
{
|
|
struct input_key_entry *ike = NULL;
|
|
key_code justkey, newkey, outkey, modifiers;
|
|
struct utf8_data ud;
|
|
char tmp[64], modifier;
|
|
|
|
/* Mouse keys need a pane. */
|
|
if (KEYC_IS_MOUSE(key))
|
|
return (0);
|
|
|
|
/* Literal keys go as themselves (can't be more than eight bits). */
|
|
if (key & KEYC_LITERAL) {
|
|
ud.data[0] = (u_char)key;
|
|
input_key_write(__func__, bev, &ud.data[0], 1);
|
|
return (0);
|
|
}
|
|
|
|
/* Is this backspace? */
|
|
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
|
|
newkey = options_get_number(global_options, "backspace");
|
|
if (newkey >= 0x7f)
|
|
newkey = '\177';
|
|
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_META|KEYC_IMPLIED_META));
|
|
if (justkey <= 0x7f) {
|
|
if (key & KEYC_META)
|
|
input_key_write(__func__, bev, "\033", 1);
|
|
ud.data[0] = justkey;
|
|
input_key_write(__func__, bev, &ud.data[0], 1);
|
|
return (0);
|
|
}
|
|
if (KEYC_IS_UNICODE(justkey)) {
|
|
if (key & KEYC_META)
|
|
input_key_write(__func__, bev, "\033", 1);
|
|
utf8_to_data(justkey, &ud);
|
|
input_key_write(__func__, bev, ud.data, ud.size);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Look up in the tree. If not in application keypad or cursor mode,
|
|
* remove the flags from the key.
|
|
*/
|
|
if (~s->mode & MODE_KKEYPAD)
|
|
key &= ~KEYC_KEYPAD;
|
|
if (~s->mode & MODE_KCURSOR)
|
|
key &= ~KEYC_CURSOR;
|
|
if (s->mode & MODE_KEXTENDED)
|
|
ike = input_key_get(key|KEYC_EXTENDED);
|
|
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 && (key & KEYC_EXTENDED))
|
|
ike = input_key_get(key & ~KEYC_EXTENDED);
|
|
if (ike != NULL) {
|
|
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
|
|
if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
|
|
(~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);
|
|
}
|
|
|
|
/* No builtin key sequence; construct an extended key sequence. */
|
|
if (~s->mode & MODE_KEXTENDED) {
|
|
if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL)
|
|
goto missing;
|
|
justkey = (key & KEYC_MASK_KEY);
|
|
switch (justkey) {
|
|
case ' ':
|
|
case '2':
|
|
key = 0|(key & ~KEYC_MASK_KEY);
|
|
break;
|
|
case '|':
|
|
key = 28|(key & ~KEYC_MASK_KEY);
|
|
break;
|
|
case '6':
|
|
key = 30|(key & ~KEYC_MASK_KEY);
|
|
break;
|
|
case '-':
|
|
case '/':
|
|
key = 31|(key & ~KEYC_MASK_KEY);
|
|
break;
|
|
case '?':
|
|
key = 127|(key & ~KEYC_MASK_KEY);
|
|
break;
|
|
default:
|
|
if (justkey >= 'A' && justkey <= '_')
|
|
key = (justkey - 'A')|(key & ~KEYC_MASK_KEY);
|
|
else if (justkey >= 'a' && justkey <= '~')
|
|
key = (justkey - 96)|(key & ~KEYC_MASK_KEY);
|
|
else
|
|
return (0);
|
|
break;
|
|
}
|
|
return (input_key(s, bev, key & ~KEYC_CTRL));
|
|
}
|
|
outkey = (key & KEYC_MASK_KEY);
|
|
modifiers = (key & KEYC_MASK_MODIFIERS);
|
|
if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) {
|
|
outkey = 64 + outkey;
|
|
modifiers |= KEYC_CTRL;
|
|
}
|
|
switch (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:
|
|
goto missing;
|
|
}
|
|
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
|
|
input_key_write(__func__, bev, tmp, strlen(tmp));
|
|
return (0);
|
|
|
|
missing:
|
|
log_debug("key 0x%llx missing", key);
|
|
return (-1);
|
|
}
|
|
|
|
/* Get mouse event string. */
|
|
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 this pane is not in button or all mode, discard motion events. */
|
|
if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0)
|
|
return (0);
|
|
if ((s->mode & ALL_MOUSE_MODES) == 0)
|
|
return (0);
|
|
|
|
/*
|
|
* If this event is a release event and not in all mode, discard it.
|
|
* In SGR mode we can tell absolutely because a release is normally
|
|
* shown by the last character. Without SGR, we check if the last
|
|
* buttons was also a release.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Use the SGR (1006) extension only if the application requested it
|
|
* and the underlying terminal also sent the event in this format (this
|
|
* is because an old style mouse release event cannot be converted into
|
|
* the new SGR format, since the released button is unknown). Otherwise
|
|
* pretend that tmux doesn't speak this extension, and fall back to the
|
|
* UTF-8 (1005) extension if the application requested, or to the
|
|
* legacy format.
|
|
*/
|
|
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;
|
|
|
|
/*
|
|
* The incoming x and y may be out of the range which can be
|
|
* supported by the "normal" mouse protocol. Clamp the
|
|
* coordinates to the supported range.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Translate mouse and output. */
|
|
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;
|
|
|
|
/* Ignore events if no mouse mode or the pane is not visible. */
|
|
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);
|
|
}
|