mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	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:
		
							
								
								
									
										1
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								TODO
									
									
									
									
									
								
							@@ -171,7 +171,6 @@ TERMINAL ISSUES
 | 
			
		||||
- support for bce
 | 
			
		||||
- use screen-256color when started on 256 colour terminal??
 | 
			
		||||
- 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
 | 
			
		||||
* 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								input-keys.c
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								input-keys.c
									
									
									
									
									
								
							@@ -201,12 +201,25 @@ input_key(struct window_pane *wp, int key)
 | 
			
		||||
void
 | 
			
		||||
input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
 | 
			
		||||
{
 | 
			
		||||
	char			 buf[10];
 | 
			
		||||
	char			 buf[40];
 | 
			
		||||
	size_t			 len;
 | 
			
		||||
	struct paste_buffer	*pb;
 | 
			
		||||
 | 
			
		||||
	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 += utf8_split2(m->xb + 32, &buf[len]);
 | 
			
		||||
			len += utf8_split2(m->x + 33, &buf[len]);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								input.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								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);
 | 
			
		||||
 
 | 
			
		||||
@@ -55,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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								tmux.h
									
									
									
									
									
								
							@@ -661,7 +661,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)
 | 
			
		||||
 | 
			
		||||
@@ -1149,6 +1150,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;
 | 
			
		||||
@@ -1161,6 +1165,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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										140
									
								
								tty-keys.c
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								tty-keys.c
									
									
									
									
									
								
							@@ -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 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)
 | 
			
		||||
@@ -609,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) {
 | 
			
		||||
@@ -660,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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								tty.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								tty.c
									
									
									
									
									
								
							@@ -218,7 +218,7 @@ tty_start_tty(struct tty *tty)
 | 
			
		||||
 | 
			
		||||
	tty_putcode(tty, TTYC_CNORM);
 | 
			
		||||
	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))
 | 
			
		||||
		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));
 | 
			
		||||
	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))
 | 
			
		||||
		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 (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)
 | 
			
		||||
				tty_puts(tty, "\033[?1005h");
 | 
			
		||||
			tty_puts(tty, "\033[?1006h");
 | 
			
		||||
 | 
			
		||||
			if (mode & MODE_MOUSE_ANY)
 | 
			
		||||
				tty_puts(tty, "\033[?1003h");
 | 
			
		||||
			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");
 | 
			
		||||
			else if (tty->mode & MODE_MOUSE_STANDARD)
 | 
			
		||||
				tty_puts(tty, "\033[?1000l");
 | 
			
		||||
 | 
			
		||||
			tty_puts(tty, "\033[?1006l");
 | 
			
		||||
			if (tty->mode & MODE_MOUSE_UTF8)
 | 
			
		||||
				tty_puts(tty, "\033[?1005l");
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user