From: Alexandros Frantzis alexandros.frantzis@collabora.com
Use the zwp_pointer_constraints_v1 protocol to implement cursor clipping. Note that Wayland only allows us to constrain the cursor within the extents of the currently focused surface. --- dlls/winewayland.drv/Makefile.in | 1 + dlls/winewayland.drv/wayland.c | 10 +++ dlls/winewayland.drv/wayland_pointer.c | 108 +++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 4 + dlls/winewayland.drv/waylanddrv_main.c | 1 + 5 files changed, 124 insertions(+)
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 8be78bd2080..ec1eff8d97c 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -6,6 +6,7 @@ UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(XKBCOMMON_LIBS) $(XKBREGISTRY_LIBS SOURCES = \ display.c \ dllmain.c \ + pointer-constraints-unstable-v1.xml \ version.rc \ viewporter.xml \ vulkan.c \ diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 31cd27f7a76..066e9e7c963 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -154,6 +154,11 @@ static void registry_handle_global(void *data, struct wl_registry *registry, process_wayland.wl_subcompositor = wl_registry_bind(registry, id, &wl_subcompositor_interface, 1); } + else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) + { + process_wayland.zwp_pointer_constraints_v1 = + wl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, 1); + } }
static void registry_handle_global_remove(void *data, struct wl_registry *registry, @@ -259,6 +264,11 @@ BOOL wayland_process_init(void) ERR("Wayland compositor doesn't support wl_subcompositor\n"); return FALSE; } + if (!process_wayland.zwp_pointer_constraints_v1) + { + ERR("Wayland compositor doesn't support zwp_pointer_constraints_v1\n"); + return FALSE; + }
wayland_init_display_devices(FALSE);
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index 33fd14fa0c5..79a1c6a8e60 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -245,6 +245,11 @@ void wayland_pointer_deinit(void) struct wayland_pointer *pointer = &process_wayland.pointer;
pthread_mutex_lock(&pointer->mutex); + if (pointer->zwp_confined_pointer_v1) + { + zwp_confined_pointer_v1_destroy(pointer->zwp_confined_pointer_v1); + pointer->zwp_confined_pointer_v1 = NULL; + } wl_pointer_release(pointer->wl_pointer); pointer->wl_pointer = NULL; pointer->focused_hwnd = NULL; @@ -587,3 +592,106 @@ void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor)
wayland_set_cursor(hwnd, hcursor, TRUE); } + +/*********************************************************************** + * WAYLAND_ClipCursor + */ +BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset) +{ + struct wayland_pointer *pointer = &process_wayland.pointer; + HWND hwnd = wayland_pointer_get_focused_hwnd(); + struct wl_surface *wl_surface = NULL; + int top, left, bottom, right; + RECT window_clip; + + TRACE("clip=%s reset=%d\n", wine_dbgstr_rect(clip), reset); + + if (NtUserGetWindowThread(hwnd, NULL) != GetCurrentThreadId()) + { + pthread_mutex_lock(&pointer->mutex); + if (pointer->zwp_confined_pointer_v1) + { + zwp_confined_pointer_v1_destroy(pointer->zwp_confined_pointer_v1); + pointer->zwp_confined_pointer_v1 = NULL; + TRACE("Unconfining due to foreground thread mismatch\n"); + } + pthread_mutex_unlock(&pointer->mutex); + return TRUE; + } + + if (!reset && clip) + { + RECT vscreen_rect = NtUserGetVirtualScreenRect(); + struct wayland_surface *surface = NULL; + + /* FIXME: surface->window.rect is in window DPI, whereas + * clip and vscreen_rect are in thread DPI. */ + + if (!EqualRect(clip, &vscreen_rect) && + (surface = wayland_surface_lock_hwnd(pointer->focused_hwnd)) && + (intersect_rect(&window_clip, clip, &surface->window.rect) || + IsRectEmpty(clip))) + { + OffsetRect(&window_clip, + -surface->window.rect.left, + -surface->window.rect.top); + + wayland_surface_coords_from_window(surface, + window_clip.left, + window_clip.top, + &left, &top); + wayland_surface_coords_from_window(surface, + window_clip.right, + window_clip.bottom, + &right, &bottom); + wl_surface = surface->wl_surface; + } + if (surface) pthread_mutex_unlock(&surface->mutex); + } + + pthread_mutex_lock(&pointer->mutex); + + /* Since we have previously established that we are running in the context + * of the HWND thread, we know that the HWND, wayland_surface and wl_surface + * will not be invalidated, so we can access these without extra locking. */ + if (wl_surface) + { + struct wl_region *region; + + region = wl_compositor_create_region(process_wayland.wl_compositor); + wl_region_add(region, left, top, right - left, bottom - top); + + if (!pointer->zwp_confined_pointer_v1) + { + pointer->zwp_confined_pointer_v1 = + zwp_pointer_constraints_v1_confine_pointer( + process_wayland.zwp_pointer_constraints_v1, + wl_surface, + pointer->wl_pointer, + region, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + } + else + { + zwp_confined_pointer_v1_set_region(pointer->zwp_confined_pointer_v1, + region); + } + + TRACE("Confining to %s %d,%d+%d,%d\n", + wine_dbgstr_rect(&window_clip), + left, top, right - left, bottom - top); + + wl_region_destroy(region); + wl_display_flush(process_wayland.wl_display); + } + else if (pointer->zwp_confined_pointer_v1) + { + zwp_confined_pointer_v1_destroy(pointer->zwp_confined_pointer_v1); + pointer->zwp_confined_pointer_v1 = NULL; + TRACE("Unconfining\n"); + } + + pthread_mutex_unlock(&pointer->mutex); + + return TRUE; +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 08abc247e16..f3c21f2871f 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -29,6 +29,7 @@ #include <wayland-client.h> #include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbregistry.h> +#include "pointer-constraints-unstable-v1-client-protocol.h" #include "viewporter-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -89,6 +90,7 @@ struct wayland_cursor struct wayland_pointer { struct wl_pointer *wl_pointer; + struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1; HWND focused_hwnd; uint32_t enter_serial; uint32_t button_serial; @@ -115,6 +117,7 @@ struct wayland struct wl_shm *wl_shm; struct wp_viewporter *wp_viewporter; struct wl_subcompositor *wl_subcompositor; + struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1; struct wayland_seat seat; struct wayland_keyboard keyboard; struct wayland_pointer pointer; @@ -305,6 +308,7 @@ RGNDATA *get_region_data(HRGN region); * USER driver functions */
+BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset); LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); void WAYLAND_DestroyWindow(HWND hwnd); void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor); diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index 435a6d2b36c..b60d282aacb 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -31,6 +31,7 @@
static const struct user_driver_funcs waylanddrv_funcs = { + .pClipCursor = WAYLAND_ClipCursor, .pDesktopWindowProc = WAYLAND_DesktopWindowProc, .pDestroyWindow = WAYLAND_DestroyWindow, .pKbdLayerDescriptor = WAYLAND_KbdLayerDescriptor,