Explorar o código

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
Camilla Löwy %!s(int64=3) %!d(string=hai) anos
pai
achega
8d87be1268
Modificáronse 4 ficheiros con 93 adicións e 25 borrados
  1. 2 0
      README.md
  2. 64 8
      src/wl_init.c
  3. 12 2
      src/wl_platform.h
  4. 15 15
      src/wl_window.c

+ 2 - 0
README.md

@@ -315,6 +315,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
  - [POSIX] Removed use of deprecated function `gettimeofday`
  - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled
  - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072)

+ 64 - 8
src/wl_init.c

@@ -704,6 +704,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 = {
@@ -714,11 +724,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 =
+        _glfw_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,
@@ -729,6 +747,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,
@@ -753,6 +784,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 = {
@@ -1409,6 +1460,11 @@ void _glfwTerminateWayland(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);
+
+    _glfw_free(_glfw.wl.offers);
+
     if (_glfw.wl.cursorSurface)
         wl_surface_destroy(_glfw.wl.cursorSurface);
     if (_glfw.wl.subcompositor)
@@ -1423,12 +1479,12 @@ void _glfwTerminateWayland(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)

+ 12 - 2
src/wl_platform.h

@@ -219,6 +219,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
@@ -281,8 +287,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;
@@ -290,6 +294,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;
 

+ 15 - 15
src/wl_window.c

@@ -1578,7 +1578,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");
@@ -1595,7 +1595,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");
@@ -1643,14 +1643,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 = {
@@ -1661,10 +1661,10 @@ static const struct wl_data_source_listener dataSourceListener = {
 
 void _glfwSetClipboardStringWayland(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);
@@ -1678,9 +1678,9 @@ void _glfwSetClipboardStringWayland(const char* string)
     _glfw_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");
@@ -1688,12 +1688,12 @@ void _glfwSetClipboardStringWayland(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);
 }
 
@@ -1719,14 +1719,14 @@ const char* _glfwGetClipboardStringWayland(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);
@@ -1738,7 +1738,7 @@ const char* _glfwGetClipboardStringWayland(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]);