From dcae6f1431dcf8deab1545cf3a251dd1a668ab21 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Wed, 20 May 2020 21:04:09 -0400 Subject: [PATCH] Allow keyboard and pointer grabs to hook clear_focus() This is necessary for some grabs, which currently have no way of knowing when the pointer/keyboard focus has left a surface. For example, without this, a drag-and-drop grab can erroneously drop into a window that the cursor is no longer over. This is the plumbing needed to properly fix swaywm/sway#5220. The existing fix, swaywm/sway#5222, relies on every grab's `enter()` hook allowing a `NULL` surface. This is not guaranteed by the API and, in fact, is not the case for the xdg-shell popup grab and results in a crash when the cursor leaves a surface and does not immediately enter another one while a popup is open (#2161). This fix also adds an assertion to wlr_seat_pointer_notify_enter() that ensures it's never called with a `NULL` surface. This will make Sway crash much more until it fixes its usage of the API, so we should land this at the same time as a fix in Sway (which I haven't posted yet). --- include/wlr/types/wlr_seat.h | 18 ++++++++++++++++++ types/data_device/wlr_drag.c | 11 +++++++++++ types/seat/wlr_seat_keyboard.c | 14 +++++++++++++- types/seat/wlr_seat_pointer.c | 13 +++++++++++++ types/xdg_shell/wlr_xdg_popup.c | 10 ++++++++++ types/xdg_shell_v6/wlr_xdg_popup_v6.c | 10 ++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index f6221939..7b47da08 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -78,6 +78,7 @@ struct wlr_seat_pointer_grab; struct wlr_pointer_grab_interface { void (*enter)(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy); + void (*clear_focus)(struct wlr_seat_pointer_grab *grab); void (*motion)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, double sx, double sy); uint32_t (*button)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, @@ -95,6 +96,7 @@ struct wlr_keyboard_grab_interface { void (*enter)(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes, struct wlr_keyboard_modifiers *modifiers); + void (*clear_focus)(struct wlr_seat_keyboard_grab *grab); void (*key)(struct wlr_seat_keyboard_grab *grab, uint32_t time_msec, uint32_t key, uint32_t state); void (*modifiers)(struct wlr_seat_keyboard_grab *grab, @@ -354,6 +356,8 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, /** * Clear the focused surface for the pointer and leave all entered surfaces. + * This function does not respect pointer grabs: you probably want + * `wlr_seat_pointer_notify_clear_focus()` instead. */ void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat); @@ -399,6 +403,12 @@ void wlr_seat_pointer_send_frame(struct wlr_seat *wlr_seat); void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy); +/** + * Notify the seat of a pointer leave event to the currently-focused surface. + * Defers to any grab of the pointer. + */ +void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat); + /** * Notify the seat of motion over the given surface. Pass surface-local * coordinates where the pointer motion occurred. Defers to any grab of the @@ -485,6 +495,8 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat, /** * Clear the focused surface for the keyboard and leave all entered surfaces. + * This function does not respect keyboard grabs: you probably want + * `wlr_seat_keyboard_notify_clear_focus()` instead. */ void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat); @@ -511,6 +523,12 @@ void wlr_seat_keyboard_notify_enter(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes, struct wlr_keyboard_modifiers *modifiers); +/** + * Notify the seat of a keyboard leave event to the currently-focused surface. + * Defers to any keyboard grabs. + */ +void wlr_seat_keyboard_notify_clear_focus(struct wlr_seat *wlr_seat); + /** * Start a grab of the keyboard of this seat. The grabber is responsible for * handling all keyboard events until the grab ends. diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index cd8695ae..86b7d19f 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -159,6 +159,11 @@ static void drag_handle_pointer_enter(struct wlr_seat_pointer_grab *grab, drag_set_focus(drag, surface, sx, sy); } +static void drag_handle_pointer_clear_focus(struct wlr_seat_pointer_grab *grab) { + struct wlr_drag *drag = grab->data; + drag_set_focus(drag, NULL, 0, 0); +} + static void drag_handle_pointer_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { struct wlr_drag *drag = grab->data; @@ -238,6 +243,7 @@ static void drag_handle_pointer_cancel(struct wlr_seat_pointer_grab *grab) { static const struct wlr_pointer_grab_interface data_device_pointer_drag_interface = { .enter = drag_handle_pointer_enter, + .clear_focus = drag_handle_pointer_clear_focus, .motion = drag_handle_pointer_motion, .button = drag_handle_pointer_button, .axis = drag_handle_pointer_axis, @@ -303,6 +309,10 @@ static void drag_handle_keyboard_enter(struct wlr_seat_keyboard_grab *grab, // nothing has keyboard focus during drags } +static void drag_handle_keyboard_clear_focus(struct wlr_seat_keyboard_grab *grab) { + // nothing has keyboard focus during drags +} + static void drag_handle_keyboard_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { // no keyboard input during drags @@ -323,6 +333,7 @@ static void drag_handle_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { static const struct wlr_keyboard_grab_interface data_device_keyboard_drag_interface = { .enter = drag_handle_keyboard_enter, + .clear_focus = drag_handle_keyboard_clear_focus, .key = drag_handle_keyboard_key, .modifiers = drag_handle_keyboard_modifiers, .cancel = drag_handle_keyboard_cancel, diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index 33de2aa5..b366e0af 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -21,6 +21,10 @@ static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab, wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers); } +static void default_keyboard_clear_focus(struct wlr_seat_keyboard_grab *grab) { + wlr_seat_keyboard_clear_focus(grab->seat); +} + static void default_keyboard_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { wlr_seat_keyboard_send_key(grab->seat, time, key, state); @@ -37,6 +41,7 @@ static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { const struct wlr_keyboard_grab_interface default_keyboard_grab_impl = { .enter = default_keyboard_enter, + .clear_focus = default_keyboard_clear_focus, .key = default_keyboard_key, .modifiers = default_keyboard_modifiers, .cancel = default_keyboard_cancel, @@ -311,15 +316,22 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat, void wlr_seat_keyboard_notify_enter(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes, struct wlr_keyboard_modifiers *modifiers) { + // NULL surfaces are prohibited in the grab-compatible API. Use + // wlr_seat_keyboard_notify_clear_focus() instead. + assert(surface); struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->enter(grab, surface, keycodes, num_keycodes, modifiers); } void wlr_seat_keyboard_clear_focus(struct wlr_seat *seat) { - // TODO respect grabs here? wlr_seat_keyboard_enter(seat, NULL, NULL, 0, NULL); } +void wlr_seat_keyboard_notify_clear_focus(struct wlr_seat *seat) { + struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; + grab->interface->clear_focus(grab); +} + bool wlr_seat_keyboard_has_grab(struct wlr_seat *seat) { return seat->keyboard_state.grab->interface != &default_keyboard_grab_impl; } diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 22848d81..9ad5f88c 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -15,6 +15,10 @@ static void default_pointer_enter(struct wlr_seat_pointer_grab *grab, wlr_seat_pointer_enter(grab->seat, surface, sx, sy); } +static void default_pointer_clear_focus(struct wlr_seat_pointer_grab *grab) { + wlr_seat_pointer_clear_focus(grab->seat); +} + static void default_pointer_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); @@ -42,6 +46,7 @@ static void default_pointer_cancel(struct wlr_seat_pointer_grab *grab) { const struct wlr_pointer_grab_interface default_pointer_grab_impl = { .enter = default_pointer_enter, + .clear_focus = default_pointer_clear_focus, .motion = default_pointer_motion, .button = default_pointer_button, .axis = default_pointer_axis, @@ -331,10 +336,18 @@ void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy) { + // NULL surfaces are prohibited in the grab-compatible API. Use + // wlr_seat_pointer_notify_clear_focus() instead. + assert(surface); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->enter(grab, surface, sx, sy); } +void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat) { + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + grab->interface->clear_focus(grab); +} + void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index 3239ea19..057e69b8 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -25,6 +25,10 @@ static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, } } +static void xdg_pointer_grab_clear_focus(struct wlr_seat_pointer_grab *grab) { + wlr_seat_pointer_clear_focus(grab->seat); +} + static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); @@ -59,6 +63,7 @@ static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { .enter = xdg_pointer_grab_enter, + .clear_focus = xdg_pointer_grab_clear_focus, .motion = xdg_pointer_grab_motion, .button = xdg_pointer_grab_button, .cancel = xdg_pointer_grab_cancel, @@ -72,6 +77,10 @@ static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, // keyboard focus should remain on the popup } +static void xdg_keyboard_grab_clear_focus(struct wlr_seat_keyboard_grab *grab) { + // keyboard focus should remain on the popup +} + static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { wlr_seat_keyboard_send_key(grab->seat, time, key, state); @@ -88,6 +97,7 @@ static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) { static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = { .enter = xdg_keyboard_grab_enter, + .clear_focus = xdg_keyboard_grab_clear_focus, .key = xdg_keyboard_grab_key, .modifiers = xdg_keyboard_grab_modifiers, .cancel = xdg_keyboard_grab_cancel, diff --git a/types/xdg_shell_v6/wlr_xdg_popup_v6.c b/types/xdg_shell_v6/wlr_xdg_popup_v6.c index 54c583bd..bed7e6d9 100644 --- a/types/xdg_shell_v6/wlr_xdg_popup_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_popup_v6.c @@ -35,6 +35,10 @@ static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, } } +static void xdg_pointer_grab_clear_focus(struct wlr_seat_pointer_grab *grab) { + wlr_seat_pointer_clear_focus(grab->seat); +} + static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); @@ -69,6 +73,7 @@ static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { .enter = xdg_pointer_grab_enter, + .clear_focus = xdg_pointer_grab_clear_focus, .motion = xdg_pointer_grab_motion, .button = xdg_pointer_grab_button, .cancel = xdg_pointer_grab_cancel, @@ -82,6 +87,10 @@ static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, // keyboard focus should remain on the popup } +static void xdg_keyboard_grab_clear_focus(struct wlr_seat_keyboard_grab *grab) { + // keyboard focus should remain on the popup +} + static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { wlr_seat_keyboard_send_key(grab->seat, time, key, state); @@ -98,6 +107,7 @@ static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) { static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = { .enter = xdg_keyboard_grab_enter, + .clear_focus = xdg_keyboard_grab_clear_focus, .key = xdg_keyboard_grab_key, .modifiers = xdg_keyboard_grab_modifiers, .cancel = xdg_keyboard_grab_cancel,