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
This commit is contained in:
Daniel Eklöf 2021-09-06 18:12:45 +02:00
parent 4853bcd139
commit e553e1076c
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 63 additions and 45 deletions

View file

@ -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

102
input.c
View file

@ -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 theyre 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 were still tracking LMB as
* being held down in our seat struct.
* - This leads to an assert (debug builds) when LMB is clicked
* again (seats 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
* theyre 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 were still tracking LMB as
* being held down in our seat struct.
* - This leads to an assert (debug builds) when LMB is clicked
* again (seats button list already contains LMB).
*
* Note: Ive 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;
}