diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index ebdf546d55..096e357b38 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -148,6 +148,19 @@ static void touch_del(SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_ } } +static SDL_bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface) +{ + struct SDL_WaylandTouchPoint *tp; + + wl_list_for_each (tp, &touch_points, link) { + if (tp->surface == surface) { + return SDL_TRUE; + } + } + + return SDL_FALSE; +} + static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp) { static Uint64 last; @@ -581,7 +594,13 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer, SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, 0, SDL_RELEASED, SDL_BUTTON_X2); } - SDL_SetMouseFocus(NULL); + + /* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event. + * Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode. + */ + if (!Wayland_SurfaceHasActiveTouches(surface)) { + SDL_SetMouseFocus(NULL); + } input->pointer_focus = NULL; } } @@ -984,6 +1003,8 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri y = wl_fixed_to_double(fy) / (window_data->wl_window_height - 1); } + SDL_SetMouseFocus(window_data->sdlwindow); + SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(intptr_t)touch, (SDL_FingerID)id, window_data->sdlwindow, SDL_TRUE, x, y, 1.0f); } @@ -1007,6 +1028,14 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(intptr_t)touch, (SDL_FingerID)id, window_data->sdlwindow, SDL_FALSE, x, y, 0.0f); + + /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window curently + * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost. + */ + if (!input->pointer_focus && input->keyboard_focus != window_data && + SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(surface)) { + SDL_SetMouseFocus(NULL); + } } } } @@ -1447,15 +1476,18 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { struct SDL_WaylandInput *input = data; - SDL_WindowData *window; + SDL_WindowData *wind; + SDL_Window *window = NULL; if (!surface || !SDL_WAYLAND_own_surface(surface)) { return; } - window = wl_surface_get_user_data(surface); - if (window) { - window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; + wind = wl_surface_get_user_data(surface); + if (wind) { + wind->keyboard_device = NULL; + window = wind->sdlwindow; + window->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; } /* Stop key repeat before clearing keyboard focus */ @@ -1463,6 +1495,7 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, /* This will release any keys still pressed */ SDL_SetKeyboardFocus(NULL); + input->keyboard_focus = NULL; /* Clear the pressed modifiers. */ input->pressed_modifiers = SDL_KMOD_NONE; @@ -1472,6 +1505,13 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, SDL_IME_SetFocus(SDL_FALSE); } #endif + + /* If the surface had a pointer leave event while still having active touch events, it retained mouse focus. + * Clear it now if all touch events are raised. + */ + if (!input->pointer_focus && SDL_GetMouseFocus() == window && !Wayland_SurfaceHasActiveTouches(surface)) { + SDL_SetMouseFocus(NULL); + } } static SDL_bool keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint32_t key, Uint8 state, SDL_bool *handled_by_ime) @@ -1651,7 +1691,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *seat, } if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) { - WAYLAND_wl_list_init(&touch_points); input->touch = wl_seat_get_touch(seat); SDL_AddTouch((SDL_TouchID)(intptr_t)input->touch, SDL_TOUCH_DEVICE_DIRECT, "wayland_touch"); wl_touch_set_user_data(input->touch, input); @@ -2955,6 +2994,8 @@ void Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version) return; } + WAYLAND_wl_list_init(&touch_points); + input->display = d; input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, SDL_min(SDL_WL_SEAT_VERSION, version)); input->sx_w = wl_fixed_from_int(0);