Support the latest theory for mouse input, this is enabled/disabled with SM/RM

1006 and is similar in style to SGR input: \033[<b;x;yM or \033[b;x;ym. From
Egmont Koblinger.
This commit is contained in:
Nicholas Marriott 2013-02-18 14:52:27 +00:00
parent d8261019f1
commit e5eee7de0c
7 changed files with 145 additions and 48 deletions

1
TODO
View File

@ -171,7 +171,6 @@ TERMINAL ISSUES
- support for bce - support for bce
- use screen-256color when started on 256 colour terminal?? - use screen-256color when started on 256 colour terminal??
- if-shell/run-shell should block further command execution in the same command - if-shell/run-shell should block further command execution in the same command
- possibly support rxvt-unicode extended mouse input (1015)
- wrap/no wrap esc seq DEC CSI ? 7 h/l - wrap/no wrap esc seq DEC CSI ? 7 h/l
* We need a tmux terminfo entry to document the extensions we are using in * We need a tmux terminfo entry to document the extensions we are using in
upstream terminfo. Must NOT change (only add or remove) anything from upstream terminfo. Must NOT change (only add or remove) anything from

View File

@ -201,12 +201,25 @@ input_key(struct window_pane *wp, int key)
void void
input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
{ {
char buf[10]; char buf[40];
size_t len; size_t len;
struct paste_buffer *pb; struct paste_buffer *pb;
if (wp->screen->mode & ALL_MOUSE_MODES) { if (wp->screen->mode & ALL_MOUSE_MODES) {
if (wp->screen->mode & MODE_MOUSE_UTF8) { /*
* 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 && (wp->screen->mode & MODE_MOUSE_SGR)) {
len = xsnprintf(buf, sizeof buf, "\033[<%d;%d;%d%c",
m->sgr_xb, m->x + 1, m->y + 1, m->sgr_rel ? 'm' : 'M');
} else if (wp->screen->mode & MODE_MOUSE_UTF8) {
len = xsnprintf(buf, sizeof buf, "\033[M"); len = xsnprintf(buf, sizeof buf, "\033[M");
len += utf8_split2(m->xb + 32, &buf[len]); len += utf8_split2(m->xb + 32, &buf[len]);
len += utf8_split2(m->x + 33, &buf[len]); len += utf8_split2(m->x + 33, &buf[len]);

View File

@ -1260,6 +1260,9 @@ input_csi_dispatch(struct input_ctx *ictx)
case 1005: case 1005:
screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8);
break; break;
case 1006:
screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR);
break;
case 47: case 47:
case 1047: case 1047:
window_pane_alternate_off(wp, &ictx->cell, 0); window_pane_alternate_off(wp, &ictx->cell, 0);
@ -1320,6 +1323,9 @@ input_csi_dispatch(struct input_ctx *ictx)
case 1005: case 1005:
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8);
break; break;
case 1006:
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR);
break;
case 47: case 47:
case 1047: case 1047:
window_pane_alternate_on(wp, &ictx->cell, 0); window_pane_alternate_on(wp, &ictx->cell, 0);

View File

@ -55,7 +55,9 @@ screen_write_reset(struct screen_write_ctx *ctx)
screen_reset_tabs(s); screen_reset_tabs(s);
screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|ALL_MOUSE_MODES);
s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD);
s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR);
screen_write_clearscreen(ctx); screen_write_clearscreen(ctx);
screen_write_cursormove(ctx, 0, 0); screen_write_cursormove(ctx, 0, 0);

10
tmux.h
View File

@ -661,7 +661,8 @@ struct mode_key_table {
#define MODE_MOUSE_BUTTON 0x40 #define MODE_MOUSE_BUTTON 0x40
#define MODE_MOUSE_ANY 0x80 #define MODE_MOUSE_ANY 0x80
#define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_UTF8 0x100
#define MODE_BRACKETPASTE 0x200 #define MODE_MOUSE_SGR 0x200
#define MODE_BRACKETPASTE 0x400
#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY)
@ -1149,6 +1150,9 @@ LIST_HEAD(tty_terms, tty_term);
* - bits 3, 4 and 5 are for keys * - bits 3, 4 and 5 are for keys
* - bit 6 is set for dragging * - bit 6 is set for dragging
* - bit 7 for buttons 4 and 5 * - bit 7 for buttons 4 and 5
*
* With the SGR 1006 extension the released button becomes known. Store these
* in separate fields and store the value converted to the old format in xb.
*/ */
struct mouse_event { struct mouse_event {
u_int xb; u_int xb;
@ -1161,6 +1165,10 @@ struct mouse_event {
u_int ly; u_int ly;
u_int sy; u_int sy;
u_int sgr; /* whether the input arrived in SGR format */
u_int sgr_xb; /* only for SGR: the unmangled button */
u_int sgr_rel; /* only for SGR: whether it is a release event */
u_int button; u_int button;
u_int clicks; u_int clicks;

View File

@ -587,20 +587,26 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
{ {
struct mouse_event *m = &tty->mouse; struct mouse_event *m = &tty->mouse;
struct utf8_data utf8data; struct utf8_data utf8data;
u_int i, value, x, y, b; u_int i, value, x, y, b, sgr, sgr_b, sgr_rel;
unsigned char c;
/* /*
* Standard mouse sequences are \033[M followed by three characters * Standard mouse sequences are \033[M followed by three characters
* indicating buttons, X and Y, all based at 32 with 1,1 top-left. * indicating button, X and Y, all based at 32 with 1,1 top-left.
* *
* UTF-8 mouse sequences are similar but the three are expressed as * UTF-8 mouse sequences are similar but the three are expressed as
* UTF-8 characters. * UTF-8 characters.
*
* SGR extended mouse sequences are \033[< followed by three numbers in
* decimal and separated by semicolons indicating button, X and Y. A
* trailing 'M' is click or scroll and trailing 'm' release. All are
* based at 0 with 1,1 top-left.
*/ */
*size = 0; *size = 0;
x = y = b = 0; x = y = b = sgr = sgr_b = sgr_rel = 0;
/* First three bytes are always \033[M. */ /* First two bytes are always \033[. */
if (buf[0] != '\033') if (buf[0] != '\033')
return (-1); return (-1);
if (len == 1) if (len == 1)
@ -609,50 +615,99 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
return (-1); return (-1);
if (len == 2) if (len == 2)
return (1); return (1);
if (buf[2] != 'M')
return (-1);
if (len == 3)
return (1);
/* Read the three inputs. */ /*
*size = 3; * Third byte is M in old standard and UTF-8 extension, < in SGR
for (i = 0; i < 3; i++) { * extension.
if (len < *size) */
return (1); if (buf[2] == 'M') {
/* Read the three inputs. */
*size = 3;
for (i = 0; i < 3; i++) {
if (len <= *size)
return (1);
if (tty->mode & MODE_MOUSE_UTF8) { if (tty->mode & MODE_MOUSE_UTF8) {
if (utf8_open(&utf8data, buf[*size])) { if (utf8_open(&utf8data, buf[*size])) {
if (utf8data.size != 2) if (utf8data.size != 2)
return (-1); return (-1);
(*size)++;
if (len <= *size)
return (1);
utf8_append(&utf8data, buf[*size]);
value = utf8_combine(&utf8data);
} else
value = (u_char) buf[*size];
(*size)++; (*size)++;
if (len < *size) } else {
return (1); value = (u_char) buf[*size];
utf8_append(&utf8data, buf[*size]); (*size)++;
value = utf8_combine(&utf8data); }
} else
value = (unsigned char)buf[*size]; if (i == 0)
(*size)++; b = value;
} else { else if (i == 1)
value = (unsigned char)buf[*size]; x = value;
(*size)++; else
y = value;
} }
log_debug("mouse input: %.*s", (int) *size, buf);
if (i == 0) /* Check and return the mouse input. */
b = value; if (b < 32 || x < 33 || y < 33)
else if (i == 1) return (-1);
x = value; b -= 32;
else x -= 33;
y = value; y -= 33;
} } else if (buf[2] == '<') {
log_debug("mouse input: %.*s", (int) *size, buf); /* Read the three inputs. */
*size = 3;
while (1) {
if (len <= *size)
return (1);
c = (u_char)buf[(*size)++];
if (c == ';')
break;
if (c < '0' || c > '9')
return (-1);
sgr_b = 10 * sgr_b + (c - '0');
}
while (1) {
if (len <= *size)
return (1);
c = (u_char)buf[(*size)++];
if (c == ';')
break;
if (c < '0' || c > '9')
return (-1);
x = 10 * x + (c - '0');
}
while (1) {
if (len <= *size)
return (1);
c = (u_char) buf[(*size)++];
if (c == 'M' || c == 'm')
break;
if (c < '0' || c > '9')
return (-1);
y = 10 * y + (c - '0');
}
log_debug("mouse input (sgr): %.*s", (int) *size, buf);
/* Check and return the mouse input. */ /* Check and return the mouse input. */
if (b < 32 || x < 33 || y < 33) if (x < 1 || y < 1)
return (-1);
x--;
y--;
sgr = 1;
sgr_rel = (c == 'm');
/* Figure out what b would be in old format. */
b = sgr_b;
if (sgr_rel)
b |= 3;
} else
return (-1); return (-1);
b -= 32;
x -= 33;
y -= 33;
log_debug("mouse position: x=%u y=%u b=%u", x, y, b);
/* Fill in mouse structure. */ /* Fill in mouse structure. */
if (~m->event & MOUSE_EVENT_WHEEL) { if (~m->event & MOUSE_EVENT_WHEEL) {
@ -660,6 +715,9 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
m->ly = m->y; m->ly = m->y;
} }
m->xb = b; m->xb = b;
m->sgr = sgr;
m->sgr_xb = sgr_b;
m->sgr_rel = sgr_rel;
if (b & 64) { /* wheel button */ if (b & 64) { /* wheel button */
b &= 3; b &= 3;
if (b == 0) if (b == 0)

15
tty.c
View File

@ -218,7 +218,7 @@ tty_start_tty(struct tty *tty)
tty_putcode(tty, TTYC_CNORM); tty_putcode(tty, TTYC_CNORM);
if (tty_term_has(tty->term, TTYC_KMOUS)) if (tty_term_has(tty->term, TTYC_KMOUS))
tty_puts(tty, "\033[?1000l"); tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l");
if (tty_term_has(tty->term, TTYC_XT)) if (tty_term_has(tty->term, TTYC_XT))
tty_puts(tty, "\033[c\033[>4;1m"); tty_puts(tty, "\033[c\033[>4;1m");
@ -281,7 +281,7 @@ tty_stop_tty(struct tty *tty)
tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
if (tty_term_has(tty->term, TTYC_KMOUS)) if (tty_term_has(tty->term, TTYC_KMOUS))
tty_raw(tty, "\033[?1000l"); tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l");
if (tty_term_has(tty->term, TTYC_XT)) if (tty_term_has(tty->term, TTYC_XT))
tty_puts(tty, "\033[>4m"); tty_puts(tty, "\033[>4m");
@ -491,8 +491,17 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
} }
if (changed & ALL_MOUSE_MODES) { if (changed & ALL_MOUSE_MODES) {
if (mode & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) {
/*
* Enable the UTF-8 (1005) extension if configured to.
* Enable the SGR (1006) extension unconditionally, as
* this is safe from misinterpretation. Do it in this
* order, because in some terminals it's the last one
* that takes effect and SGR is the preferred one.
*/
if (mode & MODE_MOUSE_UTF8) if (mode & MODE_MOUSE_UTF8)
tty_puts(tty, "\033[?1005h"); tty_puts(tty, "\033[?1005h");
tty_puts(tty, "\033[?1006h");
if (mode & MODE_MOUSE_ANY) if (mode & MODE_MOUSE_ANY)
tty_puts(tty, "\033[?1003h"); tty_puts(tty, "\033[?1003h");
else if (mode & MODE_MOUSE_BUTTON) else if (mode & MODE_MOUSE_BUTTON)
@ -506,6 +515,8 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
tty_puts(tty, "\033[?1002l"); tty_puts(tty, "\033[?1002l");
else if (tty->mode & MODE_MOUSE_STANDARD) else if (tty->mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000l"); tty_puts(tty, "\033[?1000l");
tty_puts(tty, "\033[?1006l");
if (tty->mode & MODE_MOUSE_UTF8) if (tty->mode & MODE_MOUSE_UTF8)
tty_puts(tty, "\033[?1005l"); tty_puts(tty, "\033[?1005l");
} }