From e553e1076c78fe99a7ea1faa5f2fd28a4f529d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Sep 2021 18:12:45 +0200 Subject: [PATCH] input: workaround GNOME issue with pointer button events Under certain circumstances, GNOME will send multiple pointer button press events, without any release or leave events in between. This trips up our button tracking. Workaround, by replacing the existing state for the pressed button with the new state. Previously, debug builds would assert (and thus crash), while release builds would have multiple states for the same button, causing (probably) issues like the title bar not being usable (as in, cannot be dragged, buttons not working etc). Hopefully closes #709 --- CHANGELOG.md | 6 +++ input.c | 102 ++++++++++++++++++++++++++++----------------------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf13d300..57984c49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,12 @@ ### Deprecated ### Removed ### Fixed + +* Added workaround for GNOME bug where multiple button press events + (for the same button) is sent to the CSDs without any release or + leave events in between (https://codeberg.org/dnkl/foot/issues/709). + + ### Security ### Contributors diff --git a/input.c b/input.c index ec76c42a..853bc495 100644 --- a/input.c +++ b/input.c @@ -1694,50 +1694,6 @@ fdm_csd_move(struct fdm *fdm, int fd, int events, void *data) struct wl_window *win = seat->mouse_focus->window; - /* - * Workaround GNOME bug - * - * Dragging the window, then stopping the drag (releasing the - * mouse button), *without* moving the mouse, and then clicking - * twice, waiting for the CSD timer, and finally clicking once - * more, results in the following sequence (keyboard and other - * irrelevant events filtered out, unless they’re needed to prove - * a point): - * - * dbg: input.c:1551: cancelling drag timer, moving window - * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=873, surface=0x6070000036d0 - * dbg: input.c:1432: seat0: pointer-leave: pointer=0x607000003660, serial=874, surface = 0x6070000396e0, old-moused = 0x622000006100 - * - * --> drag stopped here - * - * --> LMB clicked first time after the drag (generates the enter event on *release*, but no button events) - * dbg: input.c:1360: pointer-enter: pointer=0x607000003660, serial=876, surface = 0x6070000396e0, new-moused = 0x622000006100 - * - * --> LMB clicked, and held until the timer times out, second time after the drag - * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=877, button=110, state=1 - * dbg: input.c:1806: starting move timer - * dbg: input.c:1692: move timer timed out - * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=878, surface=0x6070000036d0 - * - * --> NOTE: ^^ no pointer leave event this time, only the keyboard leave - * - * --> LMB clicked one last time - * dbg: input.c:697: seat0: keyboard_enter: keyboard=0x607000003580, serial=879, surface=0x6070000036d0 - * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=880, button=110, state=1 - * err: input.c:1741: BUG in wl_pointer_button(): assertion failed: 'it->item.button != button' - * - * What are we seeing? - * - * - GNOME does *not* send a pointer *enter* event after the drag - * has stopped - * - The second drag does *not* generate a pointer *leave* event - * - The missing leave event means we’re still tracking LMB as - * being held down in our seat struct. - * - This leads to an assert (debug builds) when LMB is clicked - * again (seat’s button list already contains LMB). - */ - tll_free(seat->mouse.buttons); - win->csd.move_timeout_fd = -1; xdg_toplevel_move(win->xdg_toplevel, seat->wl_seat, win->csd.serial); return true; @@ -1774,6 +1730,62 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } else seat->mouse.count = 1; + /* + * Workaround GNOME bug + * + * Dragging the window, then stopping the drag (releasing the + * mouse button), *without* moving the mouse, and then + * clicking twice, waiting for the CSD timer, and finally + * clicking once more, results in the following sequence + * (keyboard and other irrelevant events filtered out, unless + * they’re needed to prove a point): + * + * dbg: input.c:1551: cancelling drag timer, moving window + * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=873, surface=0x6070000036d0 + * dbg: input.c:1432: seat0: pointer-leave: pointer=0x607000003660, serial=874, surface = 0x6070000396e0, old-moused = 0x622000006100 + * + * --> drag stopped here + * + * --> LMB clicked first time after the drag (generates the + * enter event on *release*, but no button events) + * dbg: input.c:1360: pointer-enter: pointer=0x607000003660, serial=876, surface = 0x6070000396e0, new-moused = 0x622000006100 + * + * --> LMB clicked, and held until the timer times out, second + * time after the drag + * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=877, button=110, state=1 + * dbg: input.c:1806: starting move timer + * dbg: input.c:1692: move timer timed out + * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=878, surface=0x6070000036d0 + * + * --> NOTE: ^^ no pointer leave event this time, only the + * keyboard leave + * + * --> LMB clicked one last time + * dbg: input.c:697: seat0: keyboard_enter: keyboard=0x607000003580, serial=879, surface=0x6070000036d0 + * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=880, button=110, state=1 + * err: input.c:1741: BUG in wl_pointer_button(): assertion failed: 'it->item.button != button' + * + * What are we seeing? + * + * - GNOME does *not* send a pointer *enter* event after the drag + * has stopped + * - The second drag does *not* generate a pointer *leave* event + * - The missing leave event means we’re still tracking LMB as + * being held down in our seat struct. + * - This leads to an assert (debug builds) when LMB is clicked + * again (seat’s button list already contains LMB). + * + * Note: I’ve also observed variants of the above + */ + tll_foreach(seat->mouse.buttons, it) { + if (it->item.button == button) { + LOG_WARN("multiple button press events for button %d " + "(compositor bug?)", button); + tll_remove(seat->mouse.buttons, it); + break; + } + } + #if defined(_DEBUG) tll_foreach(seat->mouse.buttons, it) xassert(it->item.button != button); @@ -1817,7 +1829,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, * 4. Release mouse button * 5. BAM! */ - LOG_WARN("stray button release event"); + LOG_WARN("stray button release event (compositor bug?)"); return; }