From 67b4d5b6094c8845af344d1386bb3fed86f54b5a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 22 Mar 2013 10:33:50 +0000 Subject: [PATCH] Support the latest theory for mouse input, this is enabled/disabled with SM/RM 1006 and is similar in style to SGR input: \033[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 += utf8_split2(m->xb + 32, &buf[len]); len += utf8_split2(m->x + 33, &buf[len]); diff --git a/input.c b/input.c index f44f602c..30f4f95d 100644 --- a/input.c +++ b/input.c @@ -1260,6 +1260,9 @@ input_csi_dispatch(struct input_ctx *ictx) case 1005: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); break; + case 1006: + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); + break; case 47: case 1047: window_pane_alternate_off(wp, &ictx->cell, 0); @@ -1320,6 +1323,9 @@ input_csi_dispatch(struct input_ctx *ictx) case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); break; + case 1006: + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); + break; case 47: case 1047: window_pane_alternate_on(wp, &ictx->cell, 0); diff --git a/screen-write.c b/screen-write.c index 9cbb5bb9..bee6f6d7 100644 --- a/screen-write.c +++ b/screen-write.c @@ -41,7 +41,6 @@ screen_write_start( } /* Finish writing. */ -/* ARGSUSED */ void screen_write_stop(unused struct screen_write_ctx *ctx) { @@ -56,7 +55,9 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_reset_tabs(s); 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_cursormove(ctx, 0, 0); diff --git a/tmux.h b/tmux.h index afe55560..192a386f 100644 --- a/tmux.h +++ b/tmux.h @@ -665,7 +665,8 @@ struct mode_key_table { #define MODE_MOUSE_BUTTON 0x40 #define MODE_MOUSE_ANY 0x80 #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) @@ -1153,6 +1154,9 @@ LIST_HEAD(tty_terms, tty_term); * - bits 3, 4 and 5 are for keys * - bit 6 is set for dragging * - 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 { u_int xb; @@ -1165,6 +1169,10 @@ struct mouse_event { u_int ly; 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 clicks; diff --git a/tty-keys.c b/tty-keys.c index 7fb96786..cb34df93 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -364,8 +364,6 @@ tty_keys_build(struct tty *tty) tty_keys_add(tty, s, tdkc->key); } - - tty_keys_add(tty, "abc", 'x'); } /* Free the entire key tree. */ @@ -569,7 +567,6 @@ complete_key: } /* Key timer callback. */ -/* ARGSUSED */ void tty_keys_callback(unused int fd, unused short events, void *data) { @@ -590,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 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 - * 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 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; - 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') return (-1); if (len == 1) @@ -612,50 +615,99 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (-1); if (len == 2) return (1); - if (buf[2] != 'M') - return (-1); - if (len == 3) - return (1); - /* Read the three inputs. */ - *size = 3; - for (i = 0; i < 3; i++) { - if (len < *size) - return (1); + /* + * Third byte is M in old standard and UTF-8 extension, < in SGR + * extension. + */ + 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 (utf8_open(&utf8data, buf[*size])) { - if (utf8data.size != 2) - return (-1); + if (tty->mode & MODE_MOUSE_UTF8) { + if (utf8_open(&utf8data, buf[*size])) { + if (utf8data.size != 2) + return (-1); + (*size)++; + if (len <= *size) + return (1); + utf8_append(&utf8data, buf[*size]); + value = utf8_combine(&utf8data); + } else + value = (u_char) buf[*size]; (*size)++; - if (len < *size) - return (1); - utf8_append(&utf8data, buf[*size]); - value = utf8_combine(&utf8data); - } else - value = (unsigned char)buf[*size]; - (*size)++; - } else { - value = (unsigned char)buf[*size]; - (*size)++; + } else { + value = (u_char) buf[*size]; + (*size)++; + } + + if (i == 0) + b = value; + else if (i == 1) + x = value; + else + y = value; } + log_debug("mouse input: %.*s", (int) *size, buf); - if (i == 0) - b = value; - else if (i == 1) - x = value; - else - y = value; - } - log_debug("mouse input: %.*s", (int) *size, buf); + /* Check and return the mouse input. */ + if (b < 32 || x < 33 || y < 33) + return (-1); + b -= 32; + x -= 33; + y -= 33; + } else if (buf[2] == '<') { + /* 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. */ - if (b < 32 || x < 33 || y < 33) + /* Check and return the mouse input. */ + 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); - b -= 32; - x -= 33; - y -= 33; - log_debug("mouse position: x=%u y=%u b=%u", x, y, b); /* Fill in mouse structure. */ if (~m->event & MOUSE_EVENT_WHEEL) { @@ -663,6 +715,9 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) m->ly = m->y; } m->xb = b; + m->sgr = sgr; + m->sgr_xb = sgr_b; + m->sgr_rel = sgr_rel; if (b & 64) { /* wheel button */ b &= 3; if (b == 0)