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.
This commit is contained in:
parent
8adc4cf7fa
commit
0df1a0df3d
|
@ -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;
|
||||
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<bContext *>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue