Dispatch any clicks waiting for click_interval on key events

Fixes #7601
This commit is contained in:
Kovid Goyal 2024-07-07 20:17:22 +05:30
parent 87bd4334b7
commit f8beaa0a4b
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 66 additions and 49 deletions

View file

@ -86,6 +86,8 @@ Detailed list of changes
- macOS 15: Handle Fn modifier when detecting global shortcuts (:iss:`7582`)
- Dispatch any clicks waiting for :opt:`click_interval` on key events (:iss:`7601`)
0.35.2 [2024-06-22]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -180,6 +180,7 @@ on_key_input(GLFWkeyevent *ev) {
}
}
if (!w) { debug("no active window, ignoring\n"); return; }
send_pending_click_to_window(w, -1);
if (OPT(mouse_hide_wait) < 0 && !is_no_action_key(key, native_key)) hide_mouse(global_state.callback_os_window);
Screen *screen = w->render_data.screen;
id_type active_window_id = w->id;

View file

@ -491,38 +491,30 @@ move_cursor_to_mouse_if_at_shell_prompt(Window *w) {
}
typedef struct PendingClick {
id_type window_id;
int button, count, modifiers;
bool grabbed;
monotonic_t at;
MousePosition mouse_pos;
unsigned long press_num;
double radius_for_multiclick;
} PendingClick;
static void
free_pending_click(id_type timer_id UNUSED, void *pc) { free(pc); }
void
send_pending_click_to_window(Window *w, void *data) {
PendingClick *pc = (PendingClick*)data;
const ClickQueue *q = &w->click_queues[pc->button];
send_pending_click_to_window(Window *w, int i) {
if (i < 0) {
while (w->pending_clicks.num) send_pending_click_to_window(w, w->pending_clicks.num - 1);
return;
}
PendingClick pc = w->pending_clicks.clicks[i];
remove_i_from_array(w->pending_clicks.clicks, (unsigned)i, w->pending_clicks.num);
const ClickQueue *q = &w->click_queues[pc.button];
// only send click if no presses have happened since the release that triggered
// 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 <= 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
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;
w->mouse_pos = pc->mouse_pos;
w->mouse_pos = pc.mouse_pos;
id_type wid = w->id;
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 = window_for_id(wid);
if (w) w->mouse_pos = current_pos;
}
@ -534,20 +526,20 @@ dispatch_possible_click(Window *w, int button, int modifiers) {
Screen *screen = w->render_data.screen;
int count = multi_click_count(w, button);
if (release_is_click(w, button)) {
PendingClick *pc = calloc(1, sizeof(PendingClick));
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->mouse_pos = w->mouse_pos;
pc->at = monotonic();
pc->button = button;
pc->count = count == 2 ? -3 : -2;
pc->modifiers = modifiers;
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);
}
ensure_space_for(&(w->pending_clicks), clicks, PendingClick, w->pending_clicks.num + 1, capacity, 4, true);
PendingClick *pc = w->pending_clicks.clicks + w->pending_clicks.num++;
zero_at_ptr(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->mouse_pos = w->mouse_pos;
pc->at = monotonic();
pc->button = button;
pc->count = count == 2 ? -3 : -2;
pc->modifiers = modifiers;
pc->grabbed = screen->modes.mouse_tracking_mode != 0;
pc->radius_for_multiclick = radius_for_multiclick();
add_main_loop_timer(OPT(click_interval), false, dispatch_pending_clicks, NULL, NULL);
}
}

View file

@ -337,6 +337,7 @@ update_os_window_title(OSWindow *os_window) {
static void
destroy_window(Window *w) {
free(w->pending_clicks.clicks); w->pending_clicks.clicks = NULL; w->pending_clicks.num = 0; w->pending_clicks.capacity = 0;
Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);
Py_CLEAR(w->title_bar_data.last_drawn_title_object_id);
free(w->title_bar_data.buf); w->title_bar_data.buf = NULL;
@ -611,21 +612,27 @@ make_window_context_current(id_type window_id) {
}
void
send_pending_click_to_window_id(id_type timer_id UNUSED, void *data) {
id_type window_id = *((id_type*)data);
for (size_t o = 0; o < global_state.num_os_windows; o++) {
OSWindow *osw = global_state.os_windows + o;
for (size_t t = 0; t < osw->num_tabs; t++) {
Tab *qtab = osw->tabs + t;
for (size_t w = 0; w < qtab->num_windows; w++) {
Window *window = qtab->windows + w;
if (window->id == window_id) {
send_pending_click_to_window(window, data);
return;
dispatch_pending_clicks(id_type timer_id UNUSED, void *data UNUSED) {
bool dispatched = false;
do { // dispatching a click can cause windows/tabs/etc to close so do it one at a time.
const monotonic_t now = monotonic();
dispatched = false;
for (size_t o = 0; o < global_state.num_os_windows && !dispatched; o++) {
OSWindow *osw = global_state.os_windows + o;
for (size_t t = 0; t < osw->num_tabs && !dispatched; t++) {
Tab *qtab = osw->tabs + t;
for (size_t w = 0; w < qtab->num_windows && !dispatched; w++) {
Window *window = qtab->windows + w;
for (size_t i = 0; i < window->pending_clicks.num && !dispatched; i++) {
if (now - window->pending_clicks.clicks[i].at >= OPT(click_interval)) {
dispatched = true;
send_pending_click_to_window(window, i);
}
}
}
}
}
}
} while (dispatched);
}
bool

View file

@ -157,6 +157,17 @@ typedef struct MousePosition {
bool in_left_half_of_cell;
} MousePosition;
typedef struct PendingClick {
id_type window_id;
int button, count, modifiers;
bool grabbed;
monotonic_t at;
MousePosition mouse_pos;
unsigned long press_num;
double radius_for_multiclick;
} PendingClick;
typedef struct WindowBarData {
unsigned width, height;
uint8_t *buf;
@ -181,6 +192,10 @@ typedef struct {
monotonic_t last_drag_scroll_at;
uint32_t last_special_key_pressed;
WindowBarData title_bar_data, url_target_bar_data;
struct {
PendingClick *clicks;
size_t num, capacity;
} pending_clicks;
} Window;
typedef struct {
@ -396,8 +411,8 @@ bool mouse_select_cmd_output(Window *w);
bool move_cursor_to_mouse_if_at_shell_prompt(Window *w);
void mouse_selection(Window *w, int code, int button);
const char* format_mods(unsigned mods);
void send_pending_click_to_window_id(id_type, void*);
void send_pending_click_to_window(Window*, void*);
void dispatch_pending_clicks(id_type, void*);
void send_pending_click_to_window(Window*, int);
void get_platform_dependent_config_values(void *glfw_window);
bool draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height);
uint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height);