Fix a fast click, move mouse, click sequence causing the first click event to be discarded

Fix #4603
This commit is contained in:
Kovid Goyal 2022-02-11 14:46:37 +05:30
parent 9fd4f8e5c2
commit 275ede6f0a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 33 additions and 9 deletions

View File

@ -129,6 +129,8 @@ Detailed list of changes
- macOS: Fix regression in previous release that caused Apple's global shortcuts to not work if they had never been configured on a particular machine (:iss:`4657`) - macOS: Fix regression in previous release that caused Apple's global shortcuts to not work if they had never been configured on a particular machine (:iss:`4657`)
- Fix a fast click, move mouse, click sequence causing the first click event to be discarded (:iss:`4603`)
0.24.2 [2022-02-03] 0.24.2 [2022-02-03]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -378,17 +378,22 @@ clear_click_queue(Window *w, int button) {
#define N(n) (q->clicks[q->length - n]) #define N(n) (q->clicks[q->length - n])
static bool static bool
release_is_click(Window *w, int button) { release_is_click(const Window *w, int button) {
ClickQueue *q = &w->click_queues[button]; const ClickQueue *q = &w->click_queues[button];
double click_allowed_radius = 1.2 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->cell_height : 20); double click_allowed_radius = 1.2 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->cell_height : 20);
monotonic_t now = monotonic(); monotonic_t now = monotonic();
return (q->length > 0 && distance(N(1).x, N(1).y, MAX(0, w->mouse_pos.global_x), MAX(0, w->mouse_pos.global_y)) <= click_allowed_radius && now - N(1).at < OPT(click_interval)); return (q->length > 0 && distance(N(1).x, N(1).y, MAX(0, w->mouse_pos.global_x), MAX(0, w->mouse_pos.global_y)) <= click_allowed_radius && now - N(1).at < OPT(click_interval));
} }
static double
radius_for_multiclick(void) {
return 1.2 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->cell_height : 20);
}
static unsigned static unsigned
multi_click_count(Window *w, int button) { multi_click_count(const Window *w, int button) {
ClickQueue *q = &w->click_queues[button]; const ClickQueue *q = &w->click_queues[button];
double multi_click_allowed_radius = 1.2 * (global_state.callback_os_window ? global_state.callback_os_window->fonts_data->cell_height : 20); double multi_click_allowed_radius = radius_for_multiclick();
if (q->length > 2) { if (q->length > 2) {
// possible triple-click // possible triple-click
if ( if (
@ -414,7 +419,8 @@ add_press(Window *w, int button, int modifiers) {
ClickQueue *q = &w->click_queues[button]; ClickQueue *q = &w->click_queues[button];
if (q->length == CLICK_QUEUE_SZ) { memmove(q->clicks, q->clicks + 1, sizeof(Click) * (CLICK_QUEUE_SZ - 1)); q->length--; } if (q->length == CLICK_QUEUE_SZ) { memmove(q->clicks, q->clicks + 1, sizeof(Click) * (CLICK_QUEUE_SZ - 1)); q->length--; }
monotonic_t now = monotonic(); monotonic_t now = monotonic();
N(0).at = now; N(0).button = button; N(0).modifiers = modifiers; N(0).x = MAX(0, w->mouse_pos.global_x); N(0).y = MAX(0, w->mouse_pos.global_y); static unsigned long num = 0;
N(0).at = now; N(0).button = button; N(0).modifiers = modifiers; N(0).x = MAX(0, w->mouse_pos.global_x); N(0).y = MAX(0, w->mouse_pos.global_y); N(0).num = ++num;
q->length++; q->length++;
Screen *screen = w->render_data.screen; Screen *screen = w->render_data.screen;
int count = multi_click_count(w, button); int count = multi_click_count(w, button);
@ -459,6 +465,8 @@ typedef struct PendingClick {
bool grabbed; bool grabbed;
monotonic_t at; monotonic_t at;
MousePosition mouse_pos; MousePosition mouse_pos;
unsigned long press_num;
double radius_for_multiclick;
} PendingClick; } PendingClick;
static void static void
@ -467,14 +475,24 @@ free_pending_click(id_type timer_id UNUSED, void *pc) { free(pc); }
void void
send_pending_click_to_window(Window *w, void *data) { send_pending_click_to_window(Window *w, void *data) {
PendingClick *pc = (PendingClick*)data; PendingClick *pc = (PendingClick*)data;
ClickQueue *q = &w->click_queues[pc->button]; const ClickQueue *q = &w->click_queues[pc->button];
// only send click if no presses have happened since the release that triggered the click // only send click if no presses have happened since the release that triggered
if (q->length && q->clicks[q->length - 1].at <= pc->at) { // the click or if the subsequent press is too far or too late for a double click
if (!q->length) return;
#define press(n) q->clicks[q->length - n]
if (
press(1).at <= pc->at || // latest press is before click release
(q->length > 1 && press(2).num == pc->press_num && ( // penultimate press is the press that belongs to this click
press(1).at - press(2).at > OPT(click_interval) || // too long between the presses for it to be a double click
distance(press(1).x, press(1).y, press(2).x, press(2).y) > pc->radius_for_multiclick // presses are too far apart
))
) {
MousePosition current_pos = w->mouse_pos; MousePosition current_pos = w->mouse_pos;
w->mouse_pos = pc->mouse_pos; w->mouse_pos = pc->mouse_pos;
dispatch_mouse_event(w, pc->button, pc->count, pc->modifiers, pc->grabbed); dispatch_mouse_event(w, pc->button, pc->count, pc->modifiers, pc->grabbed);
w->mouse_pos = current_pos; w->mouse_pos = current_pos;
} }
#undef press
} }
static void static void
@ -484,6 +502,8 @@ dispatch_possible_click(Window *w, int button, int modifiers) {
if (release_is_click(w, button)) { if (release_is_click(w, button)) {
PendingClick *pc = calloc(sizeof(PendingClick), 1); PendingClick *pc = calloc(sizeof(PendingClick), 1);
if (pc) { if (pc) {
const ClickQueue *q = &w->click_queues[button];
pc->press_num = q->length ? q->clicks[q->length - 1].num : 0;
pc->window_id = w->id; pc->window_id = w->id;
pc->mouse_pos = w->mouse_pos; pc->mouse_pos = w->mouse_pos;
pc->at = monotonic(); pc->at = monotonic();
@ -491,6 +511,7 @@ dispatch_possible_click(Window *w, int button, int modifiers) {
pc->count = count == 2 ? -3 : -2; pc->count = count == 2 ? -3 : -2;
pc->modifiers = modifiers; pc->modifiers = modifiers;
pc->grabbed = screen->modes.mouse_tracking_mode != 0; pc->grabbed = screen->modes.mouse_tracking_mode != 0;
pc->radius_for_multiclick = radius_for_multiclick();
add_main_loop_timer(OPT(click_interval), false, send_pending_click_to_window_id, pc, free_pending_click); add_main_loop_timer(OPT(click_interval), false, send_pending_click_to_window_id, pc, free_pending_click);
} }
} }

View File

@ -105,6 +105,7 @@ typedef struct {
monotonic_t at; monotonic_t at;
int button, modifiers; int button, modifiers;
double x, y; double x, y;
unsigned long num;
} Click; } Click;
#define CLICK_QUEUE_SZ 3 #define CLICK_QUEUE_SZ 3