From bdab00341b9e2a48aeb329a40c9a1b8d521ab4a1 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 29 Mar 2022 18:05:11 +0100 Subject: [PATCH] Cancel drag-select when the context menu pops up. I got a pterm into a stuck state this morning by an accidental mouse action. I'd intended to press Ctrl + right-click to pop up the context menu, but I accidentally pressed down the left button first, starting a selection drag, and then while the left button was still held down, pressed down the right button as well, triggering the menu. The effect was that the context menu appeared while term->selstate was set to DRAGGING, in which state terminal output is suppressed, and which is only unset by a mouse-button release event. But then that release event went to the popup menu, and the terminal window never got it. So the terminal stayed stuck forever - or rather, until I guessed the cause and did another selection drag to reset it. This happened to me on GTK, but once I knew how I'd done it, I found I could reproduce the same misbehaviour on Windows by the same method. Added a simplistic fix, on both platforms, that cancels a selection drag if the popup menu is summoned part way through it. --- putty.h | 1 + terminal/terminal.c | 17 +++++++++++++++++ unix/window.c | 2 ++ windows/window.c | 3 +++ 4 files changed, 23 insertions(+) diff --git a/putty.h b/putty.h index 606ff178..fc5c2941 100644 --- a/putty.h +++ b/putty.h @@ -2137,6 +2137,7 @@ void term_pwron(Terminal *, bool); void term_clrsb(Terminal *); void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action, int, int, bool, bool, bool); +void term_cancel_selection_drag(Terminal *); void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int, unsigned int); void term_lost_clipboard_ownership(Terminal *, int clipboard); diff --git a/terminal/terminal.c b/terminal/terminal.c index 923ed03e..ae5e4144 100644 --- a/terminal/terminal.c +++ b/terminal/terminal.c @@ -7299,6 +7299,23 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, term_schedule_update(term); } +void term_cancel_selection_drag(Terminal *term) +{ + /* + * In unusual circumstances, a mouse drag might be interrupted by + * something that steals the rest of the mouse gesture. An example + * is the GTK popup menu appearing. In that situation, we'll never + * receive the MA_RELEASE that finishes the DRAGGING state, which + * means terminal output could be suppressed indefinitely. Call + * this function from the front end in such situations to restore + * sensibleness. + */ + if (term->selstate == DRAGGING) + term->selstate = NO_SELECTION; + term_out(term, false); + term_schedule_update(term); +} + static int shift_bitmap(bool shift, bool ctrl, bool alt, bool *consumed_alt) { int bitmap = (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0); diff --git a/unix/window.c b/unix/window.c index a8e73789..d9d007b4 100644 --- a/unix/window.c +++ b/unix/window.c @@ -2162,6 +2162,8 @@ static gboolean button_internal(GtkFrontend *inst, GdkEventButton *event) } if (event->button == 3 && ctrl) { + /* Just in case this happened in mid-select */ + term_cancel_selection_drag(inst->term); #if GTK_CHECK_VERSION(3,22,0) gtk_menu_popup_at_pointer(GTK_MENU(inst->menu), (GdkEvent *)event); #else diff --git a/windows/window.c b/windows/window.c index c81755f2..cb9d5b1c 100644 --- a/windows/window.c +++ b/windows/window.c @@ -2641,6 +2641,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) { POINT cursorpos; + /* Just in case this happened in mid-select */ + term_cancel_selection_drag(term); + show_mouseptr(true); /* make sure pointer is visible */ GetCursorPos(&cursorpos); TrackPopupMenu(popup_menus[CTXMENU].menu,