diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 43deab03d..941648aa1 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -593,6 +593,14 @@ static void registryHandleGlobal(void* data, &zwp_idle_inhibit_manager_v1_interface, 1); } + else if (strcmp(interface, "wl_data_device_manager") == 0) + { + _glfw.wl.dataDeviceManager = + wl_registry_bind(registry, name, + &wl_data_device_manager_interface, + 1); + } + } static void registryHandleGlobalRemove(void *data, @@ -761,6 +769,12 @@ void _glfwPlatformTerminate(void) zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); if (_glfw.wl.idleInhibitManager) zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); + if (_glfw.wl.dataSourceForClipboard) + wl_data_source_destroy(_glfw.wl.dataSourceForClipboard); + if (_glfw.wl.dataDevice) + wl_data_device_destroy(_glfw.wl.dataDevice); + if (_glfw.wl.dataDeviceManager) + wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); if (_glfw.wl.registry) wl_registry_destroy(_glfw.wl.registry); if (_glfw.wl.display) @@ -769,6 +783,7 @@ void _glfwPlatformTerminate(void) wl_display_disconnect(_glfw.wl.display); } closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0])); + free(_glfw.wl.clipboardString); _glfw.wl.clipboardString = NULL; } const char* _glfwPlatformGetVersionString(void) diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 4f0f5c117..f778e0370 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -193,6 +193,9 @@ typedef struct _GLFWlibraryWayland struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + struct wl_data_device_manager* dataDeviceManager; + struct wl_data_device* dataDevice; + struct wl_data_source* dataSourceForClipboard; int compositorVersion; int seatVersion; @@ -232,7 +235,7 @@ typedef struct _GLFWlibraryWayland } egl; EventLoopData eventLoopData; - + char *clipboardString; } _GLFWlibraryWayland; // Wayland-specific per-monitor data diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 8aaa94499..2b41e52ad 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -1435,11 +1435,95 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } +static void _glfwSendClipboardText(void *data, struct wl_data_source *data_source, const char *mime_type, int fd) +{ + if (_glfw.wl.clipboardString) { + size_t len = strlen(_glfw.wl.clipboardString), pos = 0; + double start = glfwGetTime(); + while (pos < len && glfwGetTime() - start < 2.0) { + ssize_t ret = write(fd, _glfw.wl.clipboardString + pos, len - pos); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) continue; + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Could not copy writing to destination fd failed with error: %s", strerror(errno)); + break; + } + if (ret > 0) { + start = glfwGetTime(); + pos += ret; + } + } + } + close(fd); +} + +static void _glfwDataSourceTargetHandler(void *data, struct wl_data_source *data_source, const char *mime_type) {} +static void _glfwDataSourceCanceledHandler(void *data, struct wl_data_source *data_source) {} + + +const struct wl_data_source_listener data_source_listener = { + .send = _glfwSendClipboardText, + .target = _glfwDataSourceTargetHandler, + .cancelled = _glfwDataSourceCanceledHandler +}; + +static void +copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) { + if (!_glfw.wl.dataDevice) return; + if (data == (void*)_glfw.wl.dataSourceForClipboard) { + wl_data_device_set_selection(_glfw.wl.dataDevice, data, serial); + } +} + +const struct wl_callback_listener copy_callback_listener = { + .done = copy_callback_done +}; + + void _glfwPlatformSetClipboardString(const char* string) { - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Clipboard setting not implemented yet"); + if (!_glfw.wl.dataDeviceManager) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Cannot copy to clipboard data device manager is not ready"); + return; + } + if (!_glfw.wl.dataDevice) + { + if (!_glfw.wl.seat) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Cannot copy to clipboard seat is not ready"); + return; + } + _glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); + if (!_glfw.wl.dataDevice) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard copy not enabled, failed to create data device"); + return; + } + } + + free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = _glfw_strdup(string); + if (_glfw.wl.dataSourceForClipboard) + wl_data_source_destroy(_glfw.wl.dataSourceForClipboard); + _glfw.wl.dataSourceForClipboard = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); + if (!_glfw.wl.dataSourceForClipboard) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Cannot copy failed to create data source"); + return; + } + wl_data_source_add_listener(_glfw.wl.dataSourceForClipboard, &data_source_listener, NULL); + wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "text/plain"); + wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "text/plain;charset=utf-8"); + wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "TEXT"); + wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "STRING"); + wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "UTF8_STRING"); + struct wl_callback *callback = wl_display_sync(_glfw.wl.display); + wl_callback_add_listener(callback, ©_callback_listener, _glfw.wl.dataSourceForClipboard); } const char* _glfwPlatformGetClipboardString(void)