diff --git a/AUTHORS b/AUTHORS index 382a139ba..1307245f8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -61,6 +61,7 @@ Antonio Palama, DOS port Egmont Koblinger Support of 256 colors + Support of extended mouse clicks beyond 223 Erwin van Eijk diff --git a/lib/tty/key.c b/lib/tty/key.c index 57c1caf91..ea043ad3e 100644 --- a/lib/tty/key.c +++ b/lib/tty/key.c @@ -517,6 +517,8 @@ static int *seq_append = NULL; static int *pending_keys = NULL; +static int mouse_btn, mouse_x, mouse_y; + #ifdef __QNXNTO__ ph_dv_f ph_attach; ph_ov_f ph_input_group; @@ -707,18 +709,14 @@ getch_with_delay (void) static void xmouse_get_event (Gpm_Event * ev) { - int btn; static struct timeval tv1 = { 0, 0 }; /* Force first click as single */ static struct timeval tv2; static int clicks = 0; static int last_btn = 0; + int btn = mouse_btn; /* Decode Xterm mouse information to a GPM style event */ - /* Variable btn has following meaning: */ - /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */ - btn = tty_lowlevel_getch () - 32; - /* There seems to be no way of knowing which button was released */ /* So we assume all the buttons were released */ @@ -796,10 +794,8 @@ xmouse_get_event (Gpm_Event * ev) } last_btn = ev->buttons; } - /* Coordinates are 33-based */ - /* Transform them to 1-based */ - ev->x = tty_lowlevel_getch () - 32; - ev->y = tty_lowlevel_getch () - 32; + ev->x = mouse_x; + ev->y = mouse_y; } /* --------------------------------------------------------------------------------------------- */ @@ -933,6 +929,134 @@ push_char (int c) return ret; } +/* --------------------------------------------------------------------------------------------- */ +/* Parse extended mouse coordinates. + Returns -1 if pending_keys cannot be a prefix of extended mouse coordinates. + Returns 0 if pending_keys is a valid (but still incomplete) prefix for extended mouse + coordinates, e.g. "^[[32;4". + Returns 1 and fills the mouse_btn, mouse_x, mouse_y values if pending_keys is a complete extended + mouse sequence, e.g. "^[[32;42;5M" + */ + +/* Technical info (Egmont Koblinger ): + + The ancient way of reporting mouse coordinates only supports coordinates up to 231, + so if your terminal is wider (or taller, but that's unlikely), you cannot use your mouse + in the rightmost columns. + + * The old way of reporting mouse coordinates is the following: + + Output DECSET 1000 to enable mouse + + Expect escape sequences in the format \e[M whereas , + and are single bytes. (Action is 0 for left click, 1 for middle click, + 2 for right click, 3 for release, or something like this.) + + Disadvantages of this format: + + x and y can only go up to 231. + + Coordinates above 95 are not ascii-compatible, so any character set converting + layer (e.g. luit) messes them up. + + The stream is not valid UTF-8, even if everything else is. + + * The first new extension, introduced by xterm-262, is the following: + + Output DECSET 1000 to enable mouse, followed by DECSET 1005 to activate extended mode. + + Expect escape sequences in the format \e[M<><> whereas <> + and <> each can be up to two bytes long: coordinate+32 is encoded in UTF-8. + + Disadvantates of this format: + + There's still a limit of 2015 rows/columns (okay, it's not a real life problem). + + Doesn't solve the luit issue. + + It is "horribly broken" (quoting urxvt's changelog) in terms of compatibility + with the previous standard. There is no way for an application to tell whether + the underlying terminal supports this new mode (whether DECSET 1005 did actually change + the behavior or not), but depending on this a completely different user action might + generate the same input. Example: + + If the terminal doesn't support this extension, then clicking at (162, 129) + generates \e[M<32><194><161>. + + If the terminal supports this extension, then clicking at (129, 1) [bit of math: + 129+32 = 161, U+0161 in UTF-8 is 194 161] generates \e[M<32><194><161><33>. + + so there's no way to tell whether the terminal ignored the 1005 escape sequence, + the user clicked on (162, 129) and then typed an exclamation mark; or whether + the terminal recognized the escape, and the user clicked on (129, 1). + + Due to this horrible brokenness, there's no way to implement support it without + explicitly asking the user (via a setting) if the terminal can speak this extension. + + * The second new extension, introduced by rxvt-unicode-9.10, is the following: + + Output DECSET 1000 to enable mouse, followed by DECSET 1015 to activate this extended mode. + + Expect escape sequences in the format \e[{action+32};{x};{y}M where this time I used + the braces to denote spelling out the numbers in decimal, rather than using raw bytes. + + The only thing I don't understand is why they kept the offset of 32 at action, but other + than that, this format is totally okay, and solves all the weaknesses of the previous ones. + + Currently, at least the following terminal emulators have support for these: + * xterm supports the xterm extension + * rxvt-unicode >= 9.10 supports both extensions + * iterm2 supports both extensions. +*/ + +static int +parse_extended_mouse_coordinates (void) +{ + int c, btn = 0, x = 0, y = 0; + const int *p = pending_keys; + + c = *p++; + if (c == 0) + return 0; + if (c != ESC_CHAR) + return -1; + + c = *p++; + if (c == 0) + return 0; + if (c != '[') + return -1; + + while (TRUE) + { + c = *p++; + if (c == 0) + return 0; + if (c == ';') + break; + if (c < '0' || c > '9') + return -1; + btn = 10 * btn + c - '0'; + } + if (btn < 32) + return -1; + btn -= 32; + + while (TRUE) + { + c = *p++; + if (c == 0) + return 0; + if (c == ';') + break; + if (c < '0' || c > '9') + return -1; + x = 10 * x + c - '0'; + } + if (x < 1) + return -1; + + while (TRUE) + { + c = *p++; + if (c == 0) + return 0; + if (c == 'M') + break; + if (c < '0' || c > '9') + return -1; + y = 10 * y + c - '0'; + } + if (y < 1) + return -1; + + mouse_btn = btn; + mouse_x = x; + mouse_y = y; + return 1; +} + /* --------------------------------------------------------------------------------------------- */ /* Apply corrections for the keycode generated in get_key_code() */ @@ -1692,22 +1816,35 @@ get_key_code (int no_delay) pend_send: if (pending_keys != NULL) { - int d = *pending_keys++; - check_pend: - if (*pending_keys == 0) + int m; + + m = parse_extended_mouse_coordinates (); + if (m == 1) { - pending_keys = NULL; - seq_append = NULL; + pending_keys = seq_append = NULL; + this = NULL; + return MCKEY_EXTENDED_MOUSE; } - if ((d == ESC_CHAR) && (pending_keys != NULL)) + if (m == -1) { - d = ALT (*pending_keys++); - goto check_pend; + int d = *pending_keys++; + check_pend: + if (*pending_keys == 0) + { + pending_keys = NULL; + seq_append = NULL; + } + if ((d == ESC_CHAR) && (pending_keys != NULL)) + { + d = ALT (*pending_keys++); + goto check_pend; + } + if ((d > 127 && d < 256) && use_8th_bit_as_meta) + d = ALT (d & 0x7f); + this = NULL; + return correct_key_code (d); } - if ((d > 127 && d < 256) && use_8th_bit_as_meta) - d = ALT (d & 0x7f); - this = NULL; - return correct_key_code (d); + /* else if (m == 0), just let it continue */ } nodelay_try_again: @@ -2014,9 +2151,21 @@ tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) #ifdef KEY_MOUSE || c == KEY_MOUSE #endif /* KEY_MOUSE */ + || c == MCKEY_EXTENDED_MOUSE )) { /* Mouse event */ + /* In case of extended coordinates, mouse_btn, mouse_x and mouse_y are already filled in. */ + if (c != MCKEY_EXTENDED_MOUSE) + { + /* Variable btn has following meaning: */ + /* 0 = btn1 dn, 1 = btn2 dn, 2 = btn3 dn, 3 = btn up */ + mouse_btn = tty_lowlevel_getch () - 32; + /* Coordinates are 33-based */ + /* Transform them to 1-based */ + mouse_x = tty_lowlevel_getch () - 32; + mouse_y = tty_lowlevel_getch () - 32; + } xmouse_get_event (event); return (event->type != 0) ? EV_MOUSE : EV_NONE; } diff --git a/lib/tty/key.h b/lib/tty/key.h index 0eba859e2..8365395f0 100644 --- a/lib/tty/key.h +++ b/lib/tty/key.h @@ -33,6 +33,9 @@ /* Return code for the mouse sequence */ #define MCKEY_MOUSE -2 +/* Return code for the extended mouse sequence */ +#define MCKEY_EXTENDED_MOUSE -3 + /*** enums ***************************************************************************************/ /*** structures declarations (and typedefs of structures)*****************************************/ diff --git a/lib/tty/mouse.c b/lib/tty/mouse.c index 299eca9e7..58eaca26b 100644 --- a/lib/tty/mouse.c +++ b/lib/tty/mouse.c @@ -138,6 +138,9 @@ enable_mouse (void) /* enable mouse tracking */ printf (ESC_STR "[?1000h"); + /* enable urxvt extended mouse coordinate reporting */ + printf (ESC_STR "[?1015h"); + fflush (stdout); mouse_enabled = TRUE; break; @@ -149,6 +152,9 @@ enable_mouse (void) /* enable mouse tracking */ printf (ESC_STR "[?1002h"); + /* enable urxvt extended mouse coordinate reporting */ + printf (ESC_STR "[?1015h"); + fflush (stdout); mouse_enabled = TRUE; break; @@ -176,6 +182,9 @@ disable_mouse (void) break; #endif case MOUSE_XTERM_NORMAL_TRACKING: + /* disable urxvt extended mouse coordinate reporting */ + printf (ESC_STR "[?1015l"); + /* disable mouse tracking */ printf (ESC_STR "[?1000l"); @@ -185,6 +194,9 @@ disable_mouse (void) fflush (stdout); break; case MOUSE_XTERM_BUTTON_EVENT_TRACKING: + /* disable urxvt extended mouse coordinate reporting */ + printf (ESC_STR "[?1015l"); + /* disable mouse tracking */ printf (ESC_STR "[?1002l");