diff --git a/.gitignore b/.gitignore index 61ba29b9..c2450f75 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,10 @@ src/glfw_config.h src/glfw3.pc src/glfw3Config.cmake src/glfw3ConfigVersion.cmake +src/wayland-pointer-constraints-unstable-v1-client-protocol.h +src/wayland-pointer-constraints-unstable-v1-protocol.c +src/wayland-relative-pointer-unstable-v1-client-protocol.h +src/wayland-relative-pointer-unstable-v1-protocol.c # Compiled binaries src/libglfw.so diff --git a/CMake/modules/FindWaylandProtocols.cmake b/CMake/modules/FindWaylandProtocols.cmake new file mode 100644 index 00000000..8eb83f27 --- /dev/null +++ b/CMake/modules/FindWaylandProtocols.cmake @@ -0,0 +1,26 @@ +find_package(PkgConfig) + +pkg_check_modules(WaylandProtocols QUIET wayland-protocols>=${WaylandProtocols_FIND_VERSION}) + +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols + OUTPUT_VARIABLE WaylandProtocols_PKGDATADIR + RESULT_VARIABLE _pkgconfig_failed) +if (_pkgconfig_failed) + message(FATAL_ERROR "Missing wayland-protocols pkgdatadir") +endif() + +string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}") + +find_package_handle_standard_args(WaylandProtocols + FOUND_VAR + WaylandProtocols_FOUND + REQUIRED_VARS + WaylandProtocols_PKGDATADIR + VERSION_VAR + WaylandProtocols_VERSION + HANDLE_COMPONENTS +) + +set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND}) +set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR}) +set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION}) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46229dfd..ddaa518b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,8 @@ if (_GLFW_WAYLAND) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) find_package(Wayland REQUIRED) + find_package(WaylandScanner REQUIRED) + find_package(WaylandProtocols 1.1 REQUIRED) list(APPEND glfw_PKG_DEPS "wayland-egl") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d44f873c..dab94ef1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,15 @@ elseif (_GLFW_WAYLAND) posix_time.h posix_tls.h xkb_unicode.h) set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c) + + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml + BASENAME relative-pointer-unstable-v1) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml + BASENAME pointer-constraints-unstable-v1) elseif (_GLFW_MIR) set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h posix_time.h posix_tls.h xkb_unicode.h) diff --git a/src/wl_init.c b/src/wl_init.c index 90dc507b..29509ae9 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -84,12 +84,7 @@ static void pointerHandleMotion(void* data, return; if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - /* TODO */ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: GLFW_CURSOR_DISABLED not supported"); return; - } else { window->wl.cursorPosX = wl_fixed_to_double(sx); @@ -413,6 +408,20 @@ static void registryHandleGlobal(void* data, wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); } } + else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) + { + _glfw.wl.relativePointerManager = + wl_registry_bind(registry, name, + &zwp_relative_pointer_manager_v1_interface, + 1); + } + else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) + { + _glfw.wl.pointerConstraints = + wl_registry_bind(registry, name, + &zwp_pointer_constraints_v1_interface, + 1); + } } static void registryHandleGlobalRemove(void *data, diff --git a/src/wl_platform.h b/src/wl_platform.h index 56ce1631..100b12bc 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -56,6 +56,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #error "The Wayland backend depends on EGL platform support" #endif +#include "wayland-relative-pointer-unstable-v1-client-protocol.h" +#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" + #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) @@ -95,6 +98,10 @@ typedef struct _GLFWwindowWayland int monitorsCount; int monitorsSize; + struct { + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + } pointerLock; } _GLFWwindowWayland; @@ -110,6 +117,8 @@ typedef struct _GLFWlibraryWayland struct wl_seat* seat; struct wl_pointer* pointer; struct wl_keyboard* keyboard; + struct zwp_relative_pointer_manager_v1* relativePointerManager; + struct zwp_pointer_constraints_v1* pointerConstraints; int wl_compositor_version; diff --git a/src/wl_window.c b/src/wl_window.c index 706f52a3..521dc3c2 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -529,11 +529,17 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) *ypos = window->wl.cursorPosY; } +static GLFWbool isPointerLocked(_GLFWwindow* window); + void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { - // A Wayland client can not set the cursor position - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Cursor position setting not supported"); + if (isPointerLocked(window)) + { + zwp_locked_pointer_v1_set_cursor_position_hint( + window->wl.pointerLock.lockedPointer, + wl_fixed_from_double(x), wl_fixed_from_double(y)); + wl_surface_commit(window->wl.surface); + } } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) @@ -631,6 +637,103 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) wl_buffer_destroy(cursor->wl.buffer); } +static void handleRelativeMotion(void* data, + struct zwp_relative_pointer_v1* pointer, + uint32_t timeHi, + uint32_t timeLo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dxUnaccel, + wl_fixed_t dyUnaccel) +{ + _GLFWwindow* window = data; + + if (window->cursorMode != GLFW_CURSOR_DISABLED) + return; + + _glfwInputCursorMotion(window, + wl_fixed_to_double(dxUnaccel), + wl_fixed_to_double(dyUnaccel)); +} + +static const struct zwp_relative_pointer_v1_listener relativePointerListener = { + handleRelativeMotion +}; + +static void handleLocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static void unlockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer = + window->wl.pointerLock.relativePointer; + struct zwp_locked_pointer_v1* lockedPointer = + window->wl.pointerLock.lockedPointer; + + zwp_relative_pointer_v1_destroy(relativePointer); + zwp_locked_pointer_v1_destroy(lockedPointer); + + window->wl.pointerLock.relativePointer = NULL; + window->wl.pointerLock.lockedPointer = NULL; +} + +static void lockPointer(_GLFWwindow* window); + +static void handleUnlocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { + handleLocked, + handleUnlocked +}; + +static void lockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + + if (!_glfw.wl.relativePointerManager) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: no relative pointer manager"); + return; + } + + relativePointer = + zwp_relative_pointer_manager_v1_get_relative_pointer( + _glfw.wl.relativePointerManager, + _glfw.wl.pointer); + zwp_relative_pointer_v1_add_listener(relativePointer, + &relativePointerListener, + window); + + lockedPointer = + zwp_pointer_constraints_v1_lock_pointer( + _glfw.wl.pointerConstraints, + window->wl.surface, + _glfw.wl.pointer, + NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(lockedPointer, + &lockedPointerListener, + window); + + window->wl.pointerLock.relativePointer = relativePointer; + window->wl.pointerLock.lockedPointer = lockedPointer; + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + NULL, 0, 0); +} + +static GLFWbool isPointerLocked(_GLFWwindow* window) +{ + return window->wl.pointerLock.lockedPointer != NULL; +} + void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { struct wl_buffer* buffer; @@ -648,6 +751,10 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (window != _glfw.wl.pointerFocus) return; + // Unlock possible pointer lock if no longer disabled. + if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) + unlockPointer(window); + if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (cursor) @@ -691,9 +798,15 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) wl_surface_commit(surface); } } - else /* Cursor is hidden set cursor surface to NULL */ + else if (window->cursorMode == GLFW_CURSOR_DISABLED) { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, NULL, 0, 0); + if (!isPointerLocked(window)) + lockPointer(window); + } + else if (window->cursorMode == GLFW_CURSOR_HIDDEN) + { + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + NULL, 0, 0); } }