diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 7e6ff8928..6b5bd17ab 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -163,6 +163,21 @@ static void touch_del(SDL_TouchID id, wl_fixed_t *x, wl_fixed_t *y, struct wl_su } } +static SDL_bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface) +{ + struct SDL_WaylandTouchPoint *tp = touch_points.head; + + while (tp) { + if (tp->surface == surface) { + return SDL_TRUE; + } + + tp = tp->next; + } + + return SDL_FALSE; +} + /* Returns SDL_TRUE if a key repeat event was due */ static SDL_bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, uint32_t elapsed) { @@ -453,7 +468,12 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer, } if (input->pointer_focus) { - 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; } } @@ -820,6 +840,8 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri const float x = dblx / window_data->sdlwindow->w; const float y = dbly / window_data->sdlwindow->h; + SDL_SetMouseFocus(window_data->sdlwindow); + SDL_SendTouch((SDL_TouchID)(intptr_t)touch, (SDL_FingerID)id, window_data->sdlwindow, SDL_TRUE, x, y, 1.0f); } @@ -829,6 +851,7 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial uint32_t timestamp, int id) { wl_fixed_t fx = 0, fy = 0; + struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data; struct wl_surface *surface = NULL; touch_del(id, &fx, &fy, &surface); @@ -844,6 +867,14 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial SDL_SendTouch((SDL_TouchID)(intptr_t)touch, (SDL_FingerID)id, window_data->sdlwindow, SDL_FALSE, x, y, 1.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); + } } } } @@ -1127,15 +1158,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 */ @@ -1143,12 +1177,20 @@ 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; #ifdef SDL_USE_IME if (!input->text_input) { 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)