From ac3b78a84178a308536a56ea114b0f6f8ce6fb47 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 3 Jan 2011 23:35:21 +0000 Subject: Support for UTF-8 mouse input (\033[1005h). This was added in xterm 262 and supports larger terminals than the older way. If the new mouse-utf8 option is on, UTF-8 mouse input is enabled for all UTF-8 terminals. The option defaults to on if LANG etc are set in the same manner as the utf8 option. With help and based on code from hsim at gmx.li. --- input-keys.c | 20 +++++++++++++++---- input.c | 6 ++++++ options-table.c | 5 +++++ screen-write.c | 12 ++++++++++++ server-client.c | 15 +++++++++++++++ tmux.1 | 4 ++++ tmux.c | 1 + tmux.h | 10 +++++++--- tty-keys.c | 60 ++++++++++++++++++++++++++++++++++++++++++--------------- tty.c | 4 ++++ utf8.c | 13 +++++++++++++ 11 files changed, 128 insertions(+), 22 deletions(-) diff --git a/input-keys.c b/input-keys.c index ac759684..f73ce397 100644 --- a/input-keys.c +++ b/input-keys.c @@ -202,11 +202,23 @@ input_key(struct window_pane *wp, int key) void input_mouse(struct window_pane *wp, struct mouse_event *m) { - char out[8]; + char buf[10]; + size_t len; if (wp->screen->mode & ALL_MOUSE_MODES) { - xsnprintf(out, sizeof out, - "\033[M%c%c%c", m->b + 32, m->x + 33, m->y + 33); - bufferevent_write(wp->event, out, strlen(out)); + if (wp->screen->mode & MODE_MOUSE_UTF8) { + len = xsnprintf(buf, sizeof buf, "\033[M"); + len += utf8_split2(m->b + 32, &buf[len]); + len += utf8_split2(m->x + 33, &buf[len]); + len += utf8_split2(m->y + 33, &buf[len]); + } else { + if (m->b > 223 || m->x >= 222 || m->y > 222) + return; + len = xsnprintf(buf, sizeof buf, "\033[M"); + buf[len++] = m->b + 32; + buf[len++] = m->x + 33; + buf[len++] = m->y + 33; + } + bufferevent_write(wp->event, buf, len); } } diff --git a/input.c b/input.c index 49224e0e..cb8c4e52 100644 --- a/input.c +++ b/input.c @@ -1161,6 +1161,9 @@ input_csi_dispatch(struct input_ctx *ictx) case 1003: screen_write_mousemode_off(&ictx->ctx); break; + case 1005: + screen_write_utf8mousemode(&ictx->ctx, 0); + break; case 1049: window_pane_alternate_off(wp, &ictx->cell); break; @@ -1209,6 +1212,9 @@ input_csi_dispatch(struct input_ctx *ictx) case 1003: screen_write_mousemode_on(&ictx->ctx, MODE_MOUSE_ANY); break; + case 1005: + screen_write_utf8mousemode(&ictx->ctx, 1); + break; case 1049: window_pane_alternate_on(wp, &ictx->cell); break; diff --git a/options-table.c b/options-table.c index 56cb24e5..4e2b8f61 100644 --- a/options-table.c +++ b/options-table.c @@ -198,6 +198,11 @@ const struct options_table_entry session_options_table[] = { .default_num = 0 }, + { .name = "mouse-utf8", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + { .name = "pane-active-border-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 diff --git a/screen-write.c b/screen-write.c index c4e873b1..88c183ae 100644 --- a/screen-write.c +++ b/screen-write.c @@ -829,6 +829,18 @@ screen_write_insertmode(struct screen_write_ctx *ctx, int state) s->mode &= ~MODE_INSERT; } +/* Set UTF-8 mouse mode. */ +void +screen_write_utf8mousemode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_MOUSE_UTF8; + else + s->mode &= ~MODE_MOUSE_UTF8; +} + /* Set mouse mode off. */ void screen_write_mousemode_off(struct screen_write_ctx *ctx) diff --git a/server-client.c b/server-client.c index 76f8d3a8..24a85b56 100644 --- a/server-client.c +++ b/server-client.c @@ -451,6 +451,21 @@ server_client_reset_state(struct client *c) if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && options_get_number(oo, "mouse-select-pane")) mode |= MODE_MOUSE_STANDARD; + + /* + * Set UTF-8 mouse input if required. If the terminal is UTF-8, the + * user has set mouse-utf8 and any mouse mode is in effect, turn on + * UTF-8 mouse input. If the receiving terminal hasn't requested it + * (that is, it isn't in s->mode), then it'll be converted in + * input_mouse. + */ + if ((c->tty.flags & TTY_UTF8) && + (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8")) + mode |= MODE_MOUSE_UTF8; + else + mode &= ~MODE_MOUSE_UTF8; + + /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode); tty_reset(&c->tty); } diff --git a/tmux.1 b/tmux.1 index 079c8b8c..0f86e195 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1806,6 +1806,10 @@ flag to Repeat is enabled for the default keys bound to the .Ic resize-pane command. +.It Xo Ic mouse-utf8 +.Op Ic on | off +.Xc +If enabled, request mouse input as UTF-8 on UTF-8 terminals. .It Xo Ic set-remain-on-exit .Op Ic on | off .Xc diff --git a/tmux.c b/tmux.c index 54abf658..007d81fd 100644 --- a/tmux.c +++ b/tmux.c @@ -334,6 +334,7 @@ main(int argc, char **argv) /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & IDENTIFY_UTF8) { options_set_number(&global_s_options, "status-utf8", 1); + options_set_number(&global_s_options, "mouse-utf8", 1); options_set_number(&global_w_options, "utf8", 1); } diff --git a/tmux.h b/tmux.h index 45bb1a1b..09999b00 100644 --- a/tmux.h +++ b/tmux.h @@ -550,6 +550,7 @@ struct mode_key_table { #define MODE_MOUSE_HIGHLIGHT 0x40 #define MODE_MOUSE_BUTTON 0x80 #define MODE_MOUSE_ANY 0x100 +#define MODE_MOUSE_UTF8 0x200 #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD| \ MODE_MOUSE_HIGHLIGHT|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) @@ -1075,15 +1076,15 @@ struct tty_ctx { */ /* Mouse input. */ struct mouse_event { - u_char b; + u_int b; #define MOUSE_1 0 #define MOUSE_2 1 #define MOUSE_3 2 #define MOUSE_UP 3 #define MOUSE_BUTTON 3 #define MOUSE_45 64 - u_char x; - u_char y; + u_int x; + u_int y; }; /* Saved message entry. */ @@ -1817,6 +1818,7 @@ void screen_write_cursormode(struct screen_write_ctx *, int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_insertmode(struct screen_write_ctx *, int); +void screen_write_utf8mousemode(struct screen_write_ctx *, int); void screen_write_mousemode_on(struct screen_write_ctx *, int); void screen_write_mousemode_off(struct screen_write_ctx *); void screen_write_linefeed(struct screen_write_ctx *, int); @@ -2017,6 +2019,8 @@ void session_group_synchronize1(struct session *, struct session *); void utf8_build(void); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); +u_int utf8_combine(const struct utf8_data *); +u_int utf8_split2(u_int, u_char *); /* procname.c */ char *get_proc_name(int, char *); diff --git a/tty-keys.c b/tty-keys.c index 19964538..3381aefe 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -38,7 +38,7 @@ struct tty_key *tty_keys_find1( struct tty_key *, const char *, size_t, size_t *); struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); -int tty_keys_mouse( +int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, struct mouse_event *); struct tty_key_ent { @@ -462,7 +462,7 @@ tty_keys_next(struct tty *tty) } /* Is this a mouse key press? */ - switch (tty_keys_mouse(buf, len, &size, &mouse)) { + switch (tty_keys_mouse(tty, buf, len, &size, &mouse)) { case 0: /* yes */ evbuffer_drain(tty->event->input, size); key = KEYC_MOUSE; @@ -584,44 +584,74 @@ tty_keys_callback(unused int fd, unused short events, void *data) * (probably a mouse sequence but need more data). */ int -tty_keys_mouse(const char *buf, size_t len, size_t *size, struct mouse_event *m) +tty_keys_mouse(struct tty *tty, + const char *buf, size_t len, size_t *size, struct mouse_event *m) { + struct utf8_data utf8data; + u_int i, value; + /* - * Mouse sequences are \033[M followed by three characters indicating - * buttons, X and Y, all based at 32 with 1,1 top-left. + * Standard mouse sequences are \033[M followed by three characters + * indicating buttons, 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. */ *size = 0; + /* First three bytes are always \033[M. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); - if (buf[1] != '[') return (-1); if (len == 2) return (1); - if (buf[2] != 'M') return (-1); if (len == 3) return (1); - if (len < 6) - return (1); - *size = 6; + /* 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); + (*size)++; + if (len < *size) + return (1); + utf8_append(&utf8data, buf[*size]); + value = utf8_combine(&utf8data); + } else + value = buf[*size]; + (*size)++; + } else { + value = buf[*size]; + (*size)++; + } - log_debug( - "mouse input: %.6s (%hhu,%hhu/%hhu)", buf, buf[4], buf[5], buf[3]); + if (i == 0) + m->b = value; + else if (i == 1) + m->x = value; + else + m->y = value; + } + log_debug("mouse input: %.*s", (int) *size, buf); - m->b = buf[3]; - m->x = buf[4]; - m->y = buf[5]; + /* Check and return the mouse input. */ if (m->b < 32 || m->x < 33 || m->y < 33) return (-1); m->b -= 32; m->x -= 33; m->y -= 33; + log_debug("mouse position: x=%u y=%u b=%u", m->x, m->y, m->b); return (0); } diff --git a/tty.c b/tty.c index f7698708..80afa2d4 100644 --- a/tty.c +++ b/tty.c @@ -405,6 +405,8 @@ tty_update_mode(struct tty *tty, int mode) } if (changed & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) { + if (mode & MODE_MOUSE_UTF8) + tty_puts(tty, "\033[?1005h"); if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); else if (mode & MODE_MOUSE_HIGHLIGHT) @@ -422,6 +424,8 @@ tty_update_mode(struct tty *tty, int mode) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_ANY) tty_puts(tty, "\033[?1003l"); + if (tty->mode & MODE_MOUSE_UTF8) + tty_puts(tty, "\033[?1005l"); } } if (changed & MODE_KKEYPAD) { diff --git a/utf8.c b/utf8.c index 00b1c736..b276d872 100644 --- a/utf8.c +++ b/utf8.c @@ -318,6 +318,19 @@ utf8_combine(const struct utf8_data *utf8data) return (value); } +/* Split a two-byte UTF-8 character. */ +u_int +utf8_split2(u_int uc, u_char *ptr) +{ + if (uc > 0x7f) { + ptr[0] = (uc >> 6) | 0xc0; + ptr[1] = (uc & 0x3f) | 0x80; + return (2); + } + ptr[0] = uc; + return (1); +} + /* Lookup width of UTF-8 data in tree. */ u_int utf8_width(const struct utf8_data *utf8data) -- cgit v1.2.3