From ae67c7d2da96ce2b2f8ef2318e6fbcc94d3b89ed Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Tue, 9 Nov 2021 01:30:00 -0500 Subject: [PATCH] Implemented SDL_SetWindowMouseRect() on Wayland --- src/video/wayland/SDL_waylandevents.c | 48 ++++++++++++++++--------- src/video/wayland/SDL_waylandevents_c.h | 6 ++-- src/video/wayland/SDL_waylandvideo.c | 1 + src/video/wayland/SDL_waylandwindow.c | 26 ++++++++++++-- src/video/wayland/SDL_waylandwindow.h | 2 ++ 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index f52b261fc..5f20b468b 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1670,11 +1670,12 @@ lock_pointer_to_window(SDL_Window *window, w->locked_pointer = locked_pointer; } -static void pointer_confine_destroy(struct SDL_WaylandInput *input) +static void pointer_confine_destroy(SDL_Window *window) { - if (input->confined_pointer) { - zwp_confined_pointer_v1_destroy(input->confined_pointer); - input->confined_pointer = NULL; + SDL_WindowData *w = window->driverdata; + if (w->confined_pointer) { + zwp_confined_pointer_v1_destroy(w->confined_pointer); + w->confined_pointer = NULL; } } @@ -1696,7 +1697,8 @@ int Wayland_input_lock_pointer(struct SDL_WaylandInput *input) /* If we have a pointer confine active, we must destroy it here because * creating a locked pointer otherwise would be a protocol error. */ - pointer_confine_destroy(input); + for (window = vd->windows; window; window = window->next) + pointer_confine_destroy(window); if (!input->relative_pointer) { relative_pointer = @@ -1736,8 +1738,8 @@ int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input) d->relative_mouse_mode = 0; - if (input->confined_pointer_window) - Wayland_input_confine_pointer(input->confined_pointer_window, input); + for (window = vd->windows; window; window = window->next) + Wayland_input_confine_pointer(input, window); return 0; } @@ -1759,11 +1761,12 @@ static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = confined_pointer_unconfined, }; -int Wayland_input_confine_pointer(SDL_Window *window, struct SDL_WaylandInput *input) +int Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window) { SDL_WindowData *w = window->driverdata; SDL_VideoData *d = input->display; struct zwp_confined_pointer_v1 *confined_pointer; + struct wl_region *confine_rect; if (!d->pointer_constraints) return -1; @@ -1773,34 +1776,45 @@ int Wayland_input_confine_pointer(SDL_Window *window, struct SDL_WaylandInput *i /* A confine may already be active, in which case we should destroy it and * create a new one. */ - if (input->confined_pointer) - Wayland_input_unconfine_pointer(input); - - input->confined_pointer_window = window; + pointer_confine_destroy(window); /* We cannot create a confine if the pointer is already locked. Defer until * the pointer is unlocked. */ if (d->relative_mouse_mode) return 0; + if (SDL_RectEmpty(&window->mouse_rect)) { + confine_rect = NULL; + } else { + confine_rect = wl_compositor_create_region(d->compositor); + wl_region_add(confine_rect, + window->mouse_rect.x, + window->mouse_rect.y, + window->mouse_rect.w, + window->mouse_rect.h); + } + confined_pointer = zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints, w->surface, input->pointer, - NULL, + confine_rect, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); zwp_confined_pointer_v1_add_listener(confined_pointer, &confined_pointer_listener, window); - input->confined_pointer = confined_pointer; + if (confine_rect != NULL) { + wl_region_destroy(confine_rect); + } + + w->confined_pointer = confined_pointer; return 0; } -int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input) +int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window) { - pointer_confine_destroy(input); - input->confined_pointer_window = NULL; + pointer_confine_destroy(window); return 0; } diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index 699d2b377..ee905ce4d 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -50,8 +50,6 @@ struct SDL_WaylandInput { SDL_WaylandDataDevice *data_device; SDL_WaylandTextInput *text_input; struct zwp_relative_pointer_v1 *relative_pointer; - struct zwp_confined_pointer_v1 *confined_pointer; - SDL_Window *confined_pointer_window; SDL_WindowData *pointer_focus; SDL_WindowData *keyboard_focus; uint32_t pointer_enter_serial; @@ -98,8 +96,8 @@ extern void Wayland_display_destroy_pointer_constraints(SDL_VideoData *d); extern int Wayland_input_lock_pointer(struct SDL_WaylandInput *input); extern int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input); -extern int Wayland_input_confine_pointer(SDL_Window *window, struct SDL_WaylandInput *input); -extern int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input); +extern int Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window); +extern int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window); extern void Wayland_display_add_relative_pointer_manager(SDL_VideoData *d, uint32_t id); extern void Wayland_display_destroy_relative_pointer_manager(SDL_VideoData *d); diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 71f6fa886..777a1fa01 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -247,6 +247,7 @@ Wayland_CreateDevice(int devindex) device->SetWindowFullscreen = Wayland_SetWindowFullscreen; device->MaximizeWindow = Wayland_MaximizeWindow; device->MinimizeWindow = Wayland_MinimizeWindow; + device->SetWindowMouseRect = Wayland_SetWindowMouseRect; device->SetWindowMouseGrab = Wayland_SetWindowMouseGrab; device->SetWindowKeyboardGrab = Wayland_SetWindowKeyboardGrab; device->RestoreWindow = Wayland_RestoreWindow; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 02443ec4e..f4494deb2 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -1153,15 +1153,35 @@ Wayland_MinimizeWindow(_THIS, SDL_Window * window) WAYLAND_wl_display_flush(viddata->display); } +void +Wayland_SetWindowMouseRect(_THIS, SDL_Window *window) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + /* This may look suspiciously like SetWindowGrab, despite SetMouseRect not + * implicitly doing a grab. And you're right! Wayland doesn't let us mess + * around with mouse focus whatsoever, so it just happens to be that the + * work that we can do in these two functions ends up being the same. + * + * Just know that this call lets you confine with a rect, SetWindowGrab + * lets you confine without a rect. + */ + if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { + Wayland_input_unconfine_pointer(data->input, window); + } else { + Wayland_input_confine_pointer(data->input, window); + } +} + void Wayland_SetWindowMouseGrab(_THIS, SDL_Window *window, SDL_bool grabbed) { SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; if (grabbed) { - Wayland_input_confine_pointer(window, data->input); - } else { - Wayland_input_unconfine_pointer(data->input); + Wayland_input_confine_pointer(data->input, window); + } else if (SDL_RectEmpty(&window->mouse_rect)) { + Wayland_input_unconfine_pointer(data->input, window); } } diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 1aaa64521..f6656997b 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -65,6 +65,7 @@ typedef struct { struct SDL_WaylandInput *keyboard_device; EGLSurface egl_surface; struct zwp_locked_pointer_v1 *locked_pointer; + struct zwp_confined_pointer_v1 *confined_pointer; struct zxdg_toplevel_decoration_v1 *server_decoration; struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor; struct zwp_idle_inhibitor_v1 *idle_inhibitor; @@ -93,6 +94,7 @@ extern void Wayland_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_bool fullscreen); extern void Wayland_MaximizeWindow(_THIS, SDL_Window * window); extern void Wayland_MinimizeWindow(_THIS, SDL_Window * window); +extern void Wayland_SetWindowMouseRect(_THIS, SDL_Window * window); extern void Wayland_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed); extern void Wayland_SetWindowKeyboardGrab(_THIS, SDL_Window *window, SDL_bool grabbed); extern void Wayland_RestoreWindow(_THIS, SDL_Window * window);