mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +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 | - 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 | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								input-keys.c
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								input-keys.c
									
									
									
									
									
								
							| @@ -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]); | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								input.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								input.c
									
									
									
									
									
								
							| @@ -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); | ||||||
|   | |||||||
| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -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; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								tty-keys.c
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								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 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,15 +615,16 @@ 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); |  | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Third byte is M in old standard and UTF-8 extension, < in SGR | ||||||
|  | 	 * extension. | ||||||
|  | 	 */ | ||||||
|  | 	if (buf[2] == 'M') { | ||||||
| 		/* Read the three inputs. */ | 		/* Read the three inputs. */ | ||||||
| 		*size = 3; | 		*size = 3; | ||||||
| 		for (i = 0; i < 3; i++) { | 		for (i = 0; i < 3; i++) { | ||||||
| 		if (len < *size) | 			if (len <= *size) | ||||||
| 				return (1); | 				return (1); | ||||||
|  |  | ||||||
| 			if (tty->mode & MODE_MOUSE_UTF8) { | 			if (tty->mode & MODE_MOUSE_UTF8) { | ||||||
| @@ -625,15 +632,15 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) | |||||||
| 					if (utf8data.size != 2) | 					if (utf8data.size != 2) | ||||||
| 						return (-1); | 						return (-1); | ||||||
| 					(*size)++; | 					(*size)++; | ||||||
| 				if (len < *size) | 					if (len <= *size) | ||||||
| 						return (1); | 						return (1); | ||||||
| 					utf8_append(&utf8data, buf[*size]); | 					utf8_append(&utf8data, buf[*size]); | ||||||
| 					value = utf8_combine(&utf8data); | 					value = utf8_combine(&utf8data); | ||||||
| 				} else | 				} else | ||||||
| 				value = (unsigned char)buf[*size]; | 					value = (u_char) buf[*size]; | ||||||
| 				(*size)++; | 				(*size)++; | ||||||
| 			} else { | 			} else { | ||||||
| 			value = (unsigned char)buf[*size]; | 				value = (u_char) buf[*size]; | ||||||
| 				(*size)++; | 				(*size)++; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -652,7 +659,55 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) | |||||||
| 		b -= 32; | 		b -= 32; | ||||||
| 		x -= 33; | 		x -= 33; | ||||||
| 		y -= 33; | 		y -= 33; | ||||||
| 	log_debug("mouse position: x=%u y=%u b=%u", x, y, b); | 	} 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 (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); | ||||||
|  |  | ||||||
| 	/* 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
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								tty.c
									
									
									
									
									
								
							| @@ -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"); | ||||||
| 		} | 		} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott