From 00ac2ddca23ee7391da775563edb5685ccbf6a33 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 22 Jan 2023 20:48:17 +1100 Subject: [PATCH] Fix T103722: Stuck modifiers for wheel events over unfocused windows Regression in [0] caused mouse wheel events over windows without focus to use the modifier state at the point the window was de-activated. Now un-focused windows have all events release, when focused again modifier press events are set again. [0]: 8bc76bf4b957c51ddc5a13c6305f05c64b218a27 --- .../blender/windowmanager/intern/wm_window.c | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 2e5e370ae06..7e245236fe0 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -89,6 +89,20 @@ */ #define USE_WIN_ACTIVATE +/** + * When the window is de-activated, release all held modifiers. + * + * Needed so events generated over unfocused (non-active) windows don't have modifiers held. + * Since modifier press/release events aren't send to unfocused windows it's best to assume + * modifiers are not pressed. This means when modifiers *are* held, events will incorrectly + * reported as not being held. Since this is standard behavior for Linux/MS-Window, + * opt to use this. + * + * NOTE(@campbellbarton): Events generated for non-active windows are rare, + * this happens when using the mouse-wheel over an unfocused window, see: T103722. + */ +#define USE_WIN_DEACTIVATE + /* the global to talk to ghost */ static GHOST_SystemHandle g_system = NULL; #if !(defined(WIN32) || defined(__APPLE__)) @@ -1130,6 +1144,41 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt switch (type) { case GHOST_kEventWindowDeactivate: +#ifdef USE_WIN_DEACTIVATE + /* Release all held modifiers before de-activating the window. */ + if (win->eventstate->modifier != 0) { + const uint8_t keymodifier_eventstate = win->eventstate->modifier; + const uint8_t keymodifier_l = wm_ghost_modifier_query(MOD_SIDE_LEFT); + const uint8_t keymodifier_r = wm_ghost_modifier_query(MOD_SIDE_RIGHT); + /* NOTE(@campbellbarton): when non-zero, there are modifiers held in + * `win->eventstate` which are not considered held by the GHOST internal state. + * While this should not happen, it's important all modifier held in event-state + * receive release events. Without this, so any events generated while the window + * is *not* active will have modifiers held. */ + const uint8_t keymodifier_unhandled = keymodifier_eventstate & + ~(keymodifier_l | keymodifier_r); + const uint8_t keymodifier_sided[2] = { + keymodifier_l | keymodifier_unhandled, + keymodifier_r, + }; + GHOST_TEventKeyData kdata = { + .key = GHOST_kKeyUnknown, + .utf8_buf = {'\0'}, + .is_repeat = false, + }; + for (int i = 0; i < ARRAY_SIZE(g_modifier_table); i++) { + if (keymodifier_eventstate & g_modifier_table[i].flag) { + for (int side = 0; side < 2; side++) { + if ((keymodifier_sided[side] & g_modifier_table[i].flag) == 0) { + kdata.key = g_modifier_table[i].ghost_key_pair[side]; + wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); + } + } + } + } + } +#endif /* USE_WIN_DEACTIVATE */ + wm_event_add_ghostevent(wm, win, type, data); win->active = 0; /* XXX */ break;