From 3cfbd3df0f0e07282f3a45ccb58fa1df1ce08606 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Dec 2019 13:56:58 +0000 Subject: [PATCH] Support xterm any-event mouse tracking From https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Any-event-tracking: Any-event mode is the same as button-event mode, except that all motion events are reported, even if no mouse button is down. It is enabled by specifying 1003 to DECSET. Normally the front ends only report mouse events when buttons are pressed, so we introduce a MA_MOVE event with MBT_NOTHING set to indicate such a mouse movement. --- putty.h | 2 +- terminal/terminal.c | 34 +++++++++++++++++++++++++++++++++- terminal/terminal.h | 2 ++ unix/window.c | 10 +++++++--- windows/window.c | 5 +++++ 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/putty.h b/putty.h index 9eb6c0b5..c7088644 100644 --- a/putty.h +++ b/putty.h @@ -367,7 +367,7 @@ typedef enum { } Mouse_Button; typedef enum { - MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE + MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE, MA_MOVE } Mouse_Action; /* Keyboard modifiers -- keys the user is actually holding down */ diff --git a/terminal/terminal.c b/terminal/terminal.c index cf658710..fd57a5af 100644 --- a/terminal/terminal.c +++ b/terminal/terminal.c @@ -1374,6 +1374,8 @@ static void power_on(Terminal *term, bool clear) term->xterm_mouse = 0; term->xterm_extended_mouse = false; term->urxvt_extended_mouse = false; + term->raw_mouse_reported_x = 0; + term->raw_mouse_reported_y = 0; win_set_raw_mouse_mode(term->win, false); term->win_pointer_shape_pending = true; term->win_pointer_shape_raw = false; @@ -3067,6 +3069,10 @@ static void toggle_mode(Terminal *term, int mode, int query, bool state) term->xterm_mouse = state ? 2 : 0; term_update_raw_mouse_mode(term); break; + case 1003: /* xterm mouse any-event tracking */ + term->xterm_mouse = state ? 3 : 0; + term_update_raw_mouse_mode(term); + break; case 1006: /* xterm extended mouse */ term->xterm_extended_mouse = state; break; @@ -7053,6 +7059,13 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, !(term->mouse_override && shift)); int default_seltype; + // Don't do anything if mouse movement events weren't requested; + // Note: return early to avoid doing all of this code on every mouse move + // event only to throw it away. + if (a == MA_MOVE && (!raw_mouse || term->xterm_mouse < 3)) { + return; + } + if (y < 0) { y = 0; if (a == MA_DRAG && !raw_mouse) @@ -7139,6 +7152,11 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, encstate = 0x41; wheel = true; break; + case MBT_NOTHING: + assert( a == MA_MOVE ); + encstate = 0x03; // release; no buttons pressed + wheel = false; + break; default: return; } @@ -7153,7 +7171,21 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, case MA_DRAG: if (term->xterm_mouse == 1) return; - encstate += 0x20; + encstate += 0x20; // motion indicator + break; + case MA_MOVE: // mouse move without buttons + assert( braw == MBT_NOTHING && bcooked == MBT_NOTHING ); + if (term->xterm_mouse < 3) + return; + + if (selpoint.x == term->raw_mouse_reported_x && + selpoint.y == term->raw_mouse_reported_y) + return; + + term->raw_mouse_reported_x = x; + term->raw_mouse_reported_y = y; + + encstate += 0x20; // motion indicator break; case MA_RELEASE: /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */ diff --git a/terminal/terminal.h b/terminal/terminal.h index c9d93342..66d57d8b 100644 --- a/terminal/terminal.h +++ b/terminal/terminal.h @@ -155,6 +155,8 @@ struct terminal_tag { bool xterm_extended_mouse; bool urxvt_extended_mouse; int mouse_is_down; /* used while tracking mouse buttons */ + int raw_mouse_reported_x; + int raw_mouse_reported_y; bool bracketed_paste, bracketed_paste_active; diff --git a/unix/window.c b/unix/window.c index 64efb968..5460bb52 100644 --- a/unix/window.c +++ b/unix/window.c @@ -502,6 +502,8 @@ static Mouse_Button translate_button(Mouse_Button button) return MBT_PASTE; if (button == MBT_RIGHT) return MBT_EXTEND; + if (button == MBT_NOTHING) + return MBT_NOTHING; return 0; /* shouldn't happen */ } @@ -2305,7 +2307,9 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; bool shift, ctrl, alt; - int x, y, button; + Mouse_Action action = MA_DRAG; + Mouse_Button button = MBT_NOTHING; + int x, y; /* Remember the timestamp. */ inst->input_event_time = event->time; @@ -2325,12 +2329,12 @@ gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) else if (event->state & GDK_BUTTON3_MASK) button = MBT_RIGHT; else - return false; /* don't even know what button! */ + action = MA_MOVE; x = (event->x - inst->window_border) / inst->font_width; y = (event->y - inst->window_border) / inst->font_height; - term_mouse(inst->term, button, translate_button(button), MA_DRAG, + term_mouse(inst->term, button, translate_button(button), action, x, y, shift, ctrl, alt); return true; diff --git a/windows/window.c b/windows/window.c index 3f052c79..7f2271bb 100644 --- a/windows/window.c +++ b/windows/window.c @@ -2729,6 +2729,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); + } else { + term_mouse(wgs->term, MBT_NOTHING, MBT_NOTHING, MA_MOVE, + TO_CHR_X(X_POS(lParam)), + TO_CHR_Y(Y_POS(lParam)), false, + false, false); } return 0; }