From 0df1a0df3d0acbbab23be20587ecad426febc1ce Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 8 Oct 2023 18:48:35 +1100 Subject: [PATCH] WM: use time-stamps from events for double-click check Using the time from events is more accurate under Wayland which can create events while the main thread is busy. --- .../makesdna/DNA_windowmanager_types.h | 4 +- source/blender/windowmanager/intern/wm.cc | 2 +- .../windowmanager/intern/wm_event_system.cc | 74 +++++++++++++------ .../blender/windowmanager/intern/wm_window.cc | 33 +++++---- .../blender/windowmanager/wm_event_system.h | 6 +- 5 files changed, 76 insertions(+), 43 deletions(-) diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 8ad6b10eeb4..a3a7d0b9689 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -382,10 +382,10 @@ typedef struct wmWindow { void *cursor_keymap_status; /** - * The time when the key is pressed, see #PIL_check_seconds_timer. + * The time when the key is pressed in milliseconds (see #GHOST_GetEventTime). * Used to detect double-click events. */ - double eventstate_prev_press_time; + uint64_t eventstate_prev_press_time_ms; } wmWindow; diff --git a/source/blender/windowmanager/intern/wm.cc b/source/blender/windowmanager/intern/wm.cc index b2446924892..1a270dc5147 100644 --- a/source/blender/windowmanager/intern/wm.cc +++ b/source/blender/windowmanager/intern/wm.cc @@ -167,7 +167,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) win->ghostwin = nullptr; win->gpuctx = nullptr; win->eventstate = nullptr; - win->eventstate_prev_press_time = 0.0; + win->eventstate_prev_press_time_ms = 0; win->event_last_handled = nullptr; win->cursor_keymap_status = nullptr; #if defined(WIN32) || defined(__APPLE__) diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 1547350dc9a..f57cbf8077d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -64,8 +64,6 @@ #include "UI_interface.hh" -#include "PIL_time.h" - #include "WM_api.hh" #include "WM_message.hh" #include "WM_toolsystem.h" @@ -131,8 +129,9 @@ static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win); static void wm_operator_free_for_fileselect(wmOperator *file_operator); static void wm_event_state_update_and_click_set_ex(wmEvent *event, + uint64_t event_time_ms, wmEvent *event_state, - double *event_state_prev_press_time_p, + uint64_t *event_state_prev_press_time_ms_p, const bool is_keyboard, const bool check_double_click); @@ -181,8 +180,15 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) copy_v2_v2_int(event->prev_xy, win->eventstate->xy); } else if (ISKEYBOARD_OR_BUTTON(event->type)) { - wm_event_state_update_and_click_set_ex( - event, win->eventstate, &win->eventstate_prev_press_time, ISKEYBOARD(event->type), false); + /* Dummy time for simulated events. */ + const uint64_t event_time_ms = UINT64_MAX; + uint64_t eventstate_prev_press_time_ms = 0; + wm_event_state_update_and_click_set_ex(event, + event_time_ms, + win->eventstate, + &eventstate_prev_press_time_ms, + ISKEYBOARD(event->type), + false); } return event; } @@ -5241,7 +5247,9 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi return nullptr; } -static bool wm_event_is_double_click(const wmEvent *event, const double event_prev_press_time) +static bool wm_event_is_double_click(const wmEvent *event, + const uint64_t event_time_ms, + const uint64_t event_prev_press_time_ms) { if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) && (event->val == KM_PRESS)) @@ -5250,7 +5258,7 @@ static bool wm_event_is_double_click(const wmEvent *event, const double event_pr /* Pass. */ } else { - if ((PIL_check_seconds_timer() - event_prev_press_time) * 1000 < U.dbl_click_time) { + if ((event_time_ms - event_prev_press_time_ms) < uint64_t(U.dbl_click_time)) { return true; } } @@ -5268,14 +5276,16 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) event->prev_type = event_state->prev_type = event_state->type; } -static void wm_event_prev_click_set(wmEvent *event_state, double *r_prev_press_time) +static void wm_event_prev_click_set(uint64_t event_time_ms, + wmEvent *event_state, + uint64_t *r_event_state_prev_press_time_ms) { event_state->prev_press_type = event_state->type; event_state->prev_press_modifier = event_state->modifier; event_state->prev_press_keymodifier = event_state->keymodifier; event_state->prev_press_xy[0] = event_state->xy[0]; event_state->prev_press_xy[1] = event_state->xy[1]; - *r_prev_press_time = PIL_check_seconds_timer(); + *r_event_state_prev_press_time_ms = event_time_ms; } static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) @@ -5358,8 +5368,9 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d * Needed for event simulation where the time of click events is not so predictable. */ static void wm_event_state_update_and_click_set_ex(wmEvent *event, + const uint64_t event_time_ms, wmEvent *event_state, - double *event_state_prev_press_time_p, + uint64_t *event_state_prev_press_time_ms_p, const bool is_keyboard, const bool check_double_click) { @@ -5385,26 +5396,33 @@ static void wm_event_state_update_and_click_set_ex(wmEvent *event, * since the `event_state` and the `event` are not kept in sync. */ /* Double click test. */ - if (check_double_click && wm_event_is_double_click(event, *event_state_prev_press_time_p)) { + if (check_double_click && + wm_event_is_double_click(event, event_time_ms, *event_state_prev_press_time_ms_p)) + { CLOG_INFO(WM_LOG_HANDLERS, 1, "DBL_CLICK: detected"); event->val = KM_DBL_CLICK; } else if (event->val == KM_PRESS) { if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { - wm_event_prev_click_set(event_state, event_state_prev_press_time_p); + wm_event_prev_click_set(event_time_ms, event_state, event_state_prev_press_time_ms_p); } } } static void wm_event_state_update_and_click_set(wmEvent *event, + uint64_t event_time_ms, wmEvent *event_state, - double *event_state_prev_press_time_p, + uint64_t *event_state_prev_press_time_ms_p, const GHOST_TEventType type) { const bool is_keyboard = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventKeyUp); const bool check_double_click = true; - wm_event_state_update_and_click_set_ex( - event, event_state, event_state_prev_press_time_p, is_keyboard, check_double_click); + wm_event_state_update_and_click_set_ex(event, + event_time_ms, + event_state, + event_state_prev_press_time_ms_p, + is_keyboard, + check_double_click); } /* Returns true when the two events corresponds to a press of the same key with the same modifiers. @@ -5457,7 +5475,8 @@ static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent & void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, const int type, - const void *customdata) + const void *customdata, + const uint64_t event_time_ms) { if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) { return; @@ -5474,7 +5493,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, * - Data added to event and \a event_state stays and is handled immediately. */ wmEvent event, *event_state = win->eventstate; - double *event_state_prev_press_time_p = &win->eventstate_prev_press_time; + uint64_t *event_state_prev_press_time_ms_p = &win->eventstate_prev_press_time_ms; /* Initialize and copy state (only mouse x y and modifiers). */ event = *event_state; @@ -5636,8 +5655,11 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wm_tablet_data_from_ghost(&bd->tablet, &event.tablet); wm_eventemulation(&event, false); - wm_event_state_update_and_click_set( - &event, event_state, event_state_prev_press_time_p, (GHOST_TEventType)type); + wm_event_state_update_and_click_set(&event, + event_time_ms, + event_state, + event_state_prev_press_time_ms_p, + (GHOST_TEventType)type); /* Add to other window if event is there (not to both!). */ wmWindow *win_other = wm_event_cursor_other_windows(wm, win, &event); @@ -5808,8 +5830,11 @@ void wm_event_add_ghostevent(wmWindowManager *wm, } /* It's important `event.modifier` has been initialized first. */ - wm_event_state_update_and_click_set( - &event, event_state, event_state_prev_press_time_p, (GHOST_TEventType)type); + wm_event_state_update_and_click_set(&event, + event_time_ms, + event_state, + event_state_prev_press_time_ms_p, + (GHOST_TEventType)type); /* If test_break set, it catches this. Do not set with modifier presses. * Exclude modifiers because MS-Windows uses these to bring up the task manager. @@ -5877,8 +5902,11 @@ void wm_event_add_ghostevent(wmWindowManager *wm, event.custom = 0; event.customdata = nullptr; - wm_event_state_update_and_click_set( - &event, event_state, event_state_prev_press_time_p, (GHOST_TEventType)type); + wm_event_state_update_and_click_set(&event, + event_time_ms, + event_state, + event_state_prev_press_time_ms_p, + (GHOST_TEventType)type); wm_event_add(win, &event); diff --git a/source/blender/windowmanager/intern/wm_window.cc b/source/blender/windowmanager/intern/wm_window.cc index c56dc4c1efd..dcb0fc19bf1 100644 --- a/source/blender/windowmanager/intern/wm_window.cc +++ b/source/blender/windowmanager/intern/wm_window.cc @@ -572,7 +572,9 @@ void WM_window_set_dpi(const wmWindow *win) * inactive window must run #wm_window_update_eventstate_modifiers first to ensure no modifier * keys are held. See: #105277. */ -static void wm_window_update_eventstate_modifiers(wmWindowManager *wm, wmWindow *win) +static void wm_window_update_eventstate_modifiers(wmWindowManager *wm, + wmWindow *win, + const uint64_t event_time_ms) { const uint8_t keymodifier_sided[2] = { wm_ghost_modifier_query(MOD_SIDE_LEFT), @@ -591,7 +593,7 @@ static void wm_window_update_eventstate_modifiers(wmWindowManager *wm, wmWindow 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); + wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata, event_time_ms); /* Only ever send one release event * (currently releasing multiple isn't needed and only confuses logic). */ break; @@ -604,7 +606,7 @@ static void wm_window_update_eventstate_modifiers(wmWindowManager *wm, wmWindow for (int side = 0; side < 2; side++) { if (keymodifier_sided[side] & g_modifier_table[i].flag) { kdata.key = g_modifier_table[i].ghost_key_pair[side]; - wm_event_add_ghostevent(wm, win, GHOST_kEventKeyDown, &kdata); + wm_event_add_ghostevent(wm, win, GHOST_kEventKeyDown, &kdata, event_time_ms); } } } @@ -625,7 +627,9 @@ static void wm_window_update_eventstate_modifiers(wmWindowManager *wm, wmWindow * NOTE(@ideasman42): Events generated for non-active windows are rare, * this happens when using the mouse-wheel over an unfocused window, see: #103722. */ -static void wm_window_update_eventstate_modifiers_clear(wmWindowManager *wm, wmWindow *win) +static void wm_window_update_eventstate_modifiers_clear(wmWindowManager *wm, + wmWindow *win, + const uint64_t event_time_ms) { /* Release all held modifiers before de-activating the window. */ if (win->eventstate->modifier != 0) { @@ -652,7 +656,7 @@ static void wm_window_update_eventstate_modifiers_clear(wmWindowManager *wm, wmW 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); + wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata, event_time_ms); } } } @@ -1282,10 +1286,7 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt bContext *C = static_cast(C_void_ptr); wmWindowManager *wm = CTX_wm_manager(C); GHOST_TEventType type = GHOST_GetEventType(evt); -#if 0 - /* We may want to use time from ghost, currently `PIL_check_seconds_timer` is used instead. */ - uint64_t time = GHOST_GetEventTime(evt); -#endif + const uint64_t event_time_ms = GHOST_GetEventTime(evt); if (type == GHOST_kEventQuitRequest) { /* Find an active window to display quit dialog in. */ @@ -1331,15 +1332,15 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt switch (type) { case GHOST_kEventWindowDeactivate: { - wm_window_update_eventstate_modifiers_clear(wm, win); + wm_window_update_eventstate_modifiers_clear(wm, win, event_time_ms); - wm_event_add_ghostevent(wm, win, type, data); + wm_event_add_ghostevent(wm, win, type, data, event_time_ms); win->active = 0; break; } case GHOST_kEventWindowActivate: { /* Ensure the event state matches modifiers (window was inactive). */ - wm_window_update_eventstate_modifiers(wm, win); + wm_window_update_eventstate_modifiers(wm, win, event_time_ms); /* Entering window, update mouse position (without sending an event). */ wm_window_update_eventstate(win); @@ -1510,7 +1511,7 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt data); /* Ensure the event state matches modifiers (window was inactive). */ - wm_window_update_eventstate_modifiers(wm, win); + wm_window_update_eventstate_modifiers(wm, win, event_time_ms); /* Entering window, update mouse position (without sending an event). */ wm_window_update_eventstate(win); @@ -1593,15 +1594,15 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt if (win->active == 0) { /* Entering window, update cursor/tablet state & modifiers. * (ghost sends win-activate *after* the mouse-click in window!) */ - wm_window_update_eventstate_modifiers(wm, win); + wm_window_update_eventstate_modifiers(wm, win, event_time_ms); wm_window_update_eventstate(win); } - wm_event_add_ghostevent(wm, win, type, data); + wm_event_add_ghostevent(wm, win, type, data, event_time_ms); break; } default: { - wm_event_add_ghostevent(wm, win, type, data); + wm_event_add_ghostevent(wm, win, type, data, event_time_ms); break; } } diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index fd792bfde44..d4be459533b 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -134,7 +134,11 @@ void wm_event_do_handlers(bContext *C); /** * Windows store own event queues #wmWindow.event_queue (no #bContext here). */ -void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, const void *customdata); +void wm_event_add_ghostevent(wmWindowManager *wm, + wmWindow *win, + int type, + const void *customdata, + const uint64_t event_time_ms); #ifdef WITH_XR_OPENXR void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val); #endif