From a31c648127bcbd856661d5f31bcca7a61aaa7f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 23 Mar 2022 19:58:33 +0100 Subject: [PATCH] Wayland: Improve handling of pending data offers The code assumed that all data offers were selections that supported plaintext UTF-8. The initial data offer events are now handled almost tolerably. Only selection data offers are used for clipboard string and only if they provide plaintext UTF-8. Drag and drop data offers are now rejected as soon as they enter a surface. Related to #2040 (cherry picked from commit 8d87be1268869af69d59400a1b0403e9ecbc5c0c) --- README.md | 2 ++ src/wl_init.c | 72 +++++++++++++++++++++++++++++++++++++++++------ src/wl_platform.h | 14 +++++++-- src/wl_window.c | 30 ++++++++++---------- 4 files changed, 93 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 582500c7..d97f71d6 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,8 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: Data source creation error would cause double free at termination - [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat - [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang + - [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string + - [Wayland] Bugfix: MIME type matching was not performed for clipboard string ## Contact diff --git a/src/wl_init.c b/src/wl_init.c index b7b1188d..fb874ba6 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -700,6 +700,16 @@ static void dataOfferHandleOffer(void* userData, struct wl_data_offer* offer, const char* mimeType) { + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (strcmp(mimeType, "text/plain;charset=utf-8") == 0) + _glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE; + + break; + } + } } static const struct wl_data_offer_listener dataOfferListener = { @@ -710,11 +720,19 @@ static void dataDeviceHandleDataOffer(void* userData, struct wl_data_device* device, struct wl_data_offer* offer) { - if (_glfw.wl.dataOffer) - wl_data_offer_destroy(_glfw.wl.dataOffer); + _GLFWofferWayland* offers = + realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1); + if (!offers) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return; + } - _glfw.wl.dataOffer = offer; - wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); + _glfw.wl.offers = offers; + _glfw.wl.offerCount++; + + _glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer }; + wl_data_offer_add_listener(offer, &dataOfferListener, NULL); } static void dataDeviceHandleEnter(void* userData, @@ -725,6 +743,19 @@ static void dataDeviceHandleEnter(void* userData, wl_fixed_t y, struct wl_data_offer* offer) { + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + + // We don't yet handle drag and drop + wl_data_offer_accept(offer, serial, NULL); + wl_data_offer_destroy(offer); + break; + } + } } static void dataDeviceHandleLeave(void* userData, @@ -749,6 +780,26 @@ static void dataDeviceHandleSelection(void* userData, struct wl_data_device* device, struct wl_data_offer* offer) { + if (_glfw.wl.selectionOffer) + { + wl_data_offer_destroy(_glfw.wl.selectionOffer); + _glfw.wl.selectionOffer = NULL; + } + + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + { + if (_glfw.wl.offers[i].offer == offer) + { + if (_glfw.wl.offers[i].text_plain_utf8) + _glfw.wl.selectionOffer = offer; + else + wl_data_offer_destroy(offer); + + _glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1]; + _glfw.wl.offerCount--; + break; + } + } } static const struct wl_data_device_listener dataDeviceListener = { @@ -1240,6 +1291,11 @@ void _glfwPlatformTerminate(void) _glfw.wl.cursor.handle = NULL; } + for (unsigned int i = 0; i < _glfw.wl.offerCount; i++) + wl_data_offer_destroy(_glfw.wl.offers[i].offer); + + free(_glfw.wl.offers); + if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); if (_glfw.wl.subcompositor) @@ -1256,12 +1312,12 @@ void _glfwPlatformTerminate(void) zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); if (_glfw.wl.wmBase) xdg_wm_base_destroy(_glfw.wl.wmBase); - if (_glfw.wl.dataSource) - wl_data_source_destroy(_glfw.wl.dataSource); + if (_glfw.wl.selectionOffer) + wl_data_offer_destroy(_glfw.wl.selectionOffer); + if (_glfw.wl.selectionSource) + wl_data_source_destroy(_glfw.wl.selectionSource); if (_glfw.wl.dataDevice) wl_data_device_destroy(_glfw.wl.dataDevice); - if (_glfw.wl.dataOffer) - wl_data_offer_destroy(_glfw.wl.dataOffer); if (_glfw.wl.dataDeviceManager) wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); if (_glfw.wl.pointer) diff --git a/src/wl_platform.h b/src/wl_platform.h index 2385402e..31aa0fc7 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -167,6 +167,12 @@ typedef struct _GLFWdecorationWayland struct wp_viewport* viewport; } _GLFWdecorationWayland; +typedef struct _GLFWofferWayland +{ + struct wl_data_offer* offer; + GLFWbool text_plain_utf8; +} _GLFWofferWayland; + // Wayland-specific per-window data // typedef struct _GLFWwindowWayland @@ -231,8 +237,6 @@ typedef struct _GLFWlibraryWayland struct wl_keyboard* keyboard; struct wl_data_device_manager* dataDeviceManager; struct wl_data_device* dataDevice; - struct wl_data_offer* dataOffer; - struct wl_data_source* dataSource; struct xdg_wm_base* wmBase; struct zxdg_decoration_manager_v1* decorationManager; struct wp_viewporter* viewporter; @@ -240,6 +244,12 @@ typedef struct _GLFWlibraryWayland struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; + _GLFWofferWayland* offers; + unsigned int offerCount; + + struct wl_data_offer* selectionOffer; + struct wl_data_source* selectionSource; + int compositorVersion; int seatVersion; diff --git a/src/wl_window.c b/src/wl_window.c index 997baa18..98f2891c 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1712,7 +1712,7 @@ static void dataSourceHandleTarget(void* userData, struct wl_data_source* source, const char* mimeType) { - if (_glfw.wl.dataSource != source) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1729,7 +1729,7 @@ static void dataSourceHandleSend(void* userData, size_t len = strlen(string); int ret; - if (_glfw.wl.dataSource != source) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); @@ -1777,14 +1777,14 @@ static void dataSourceHandleCancelled(void* userData, { wl_data_source_destroy(source); - if (_glfw.wl.dataSource != source) + if (_glfw.wl.selectionSource != source) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); return; } - _glfw.wl.dataSource = NULL; + _glfw.wl.selectionSource = NULL; } static const struct wl_data_source_listener dataSourceListener = { @@ -1795,10 +1795,10 @@ static const struct wl_data_source_listener dataSourceListener = { void _glfwPlatformSetClipboardString(const char* string) { - if (_glfw.wl.dataSource) + if (_glfw.wl.selectionSource) { - wl_data_source_destroy(_glfw.wl.dataSource); - _glfw.wl.dataSource = NULL; + wl_data_source_destroy(_glfw.wl.selectionSource); + _glfw.wl.selectionSource = NULL; } char* copy = _glfw_strdup(string); @@ -1812,9 +1812,9 @@ void _glfwPlatformSetClipboardString(const char* string) free(_glfw.wl.clipboardSendString); _glfw.wl.clipboardSendString = copy; - _glfw.wl.dataSource = + _glfw.wl.selectionSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); - if (!_glfw.wl.dataSource) + if (!_glfw.wl.selectionSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create clipboard data source"); @@ -1822,12 +1822,12 @@ void _glfwPlatformSetClipboardString(const char* string) _glfw.wl.clipboardSendString = NULL; return; } - wl_data_source_add_listener(_glfw.wl.dataSource, + wl_data_source_add_listener(_glfw.wl.selectionSource, &dataSourceListener, NULL); - wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); + wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8"); wl_data_device_set_selection(_glfw.wl.dataDevice, - _glfw.wl.dataSource, + _glfw.wl.selectionSource, _glfw.wl.serial); } @@ -1853,14 +1853,14 @@ const char* _glfwPlatformGetClipboardString(void) int ret; size_t len = 0; - if (!_glfw.wl.dataOffer) + if (!_glfw.wl.selectionOffer) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "Wayland: No clipboard data available"); return NULL; } - if (_glfw.wl.dataSource) + if (_glfw.wl.selectionSource) return _glfw.wl.clipboardSendString; ret = pipe2(fds, O_CLOEXEC); @@ -1872,7 +1872,7 @@ const char* _glfwPlatformGetClipboardString(void) return NULL; } - wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); + wl_data_offer_receive(_glfw.wl.selectionOffer, "text/plain;charset=utf-8", fds[1]); flushDisplay(); close(fds[1]);