Wayland: Implement clipboard copy
diff --git a/src/wl_init.c b/src/wl_init.c
index 30d0eca..c6b209b 100644
--- a/src/wl_init.c
+++ b/src/wl_init.c
@@ -1263,6 +1263,8 @@
         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.dataDevice)
         wl_data_device_destroy(_glfw.wl.dataDevice);
     if (_glfw.wl.dataOffer)
@@ -1296,6 +1298,8 @@
 
     if (_glfw.wl.clipboardString)
         free(_glfw.wl.clipboardString);
+    if (_glfw.wl.clipboardSendString)
+        free(_glfw.wl.clipboardSendString);
 }
 
 const char* _glfwPlatformGetVersionString(void)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index 11c2f91..c17ebe8 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -236,6 +236,7 @@
     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;
@@ -258,6 +259,8 @@
     int                         keyboardLastScancode;
     char*                       clipboardString;
     size_t                      clipboardSize;
+    char*                       clipboardSendString;
+    size_t                      clipboardSendSize;
     int                         timerfd;
     short int                   keycodes[256];
     short int                   scancodes[GLFW_KEY_LAST + 1];
diff --git a/src/wl_window.c b/src/wl_window.c
index 817e2f0..98a6465 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -1569,11 +1569,126 @@
     }
 }
 
+static void dataSourceHandleTarget(void* data,
+                                   struct wl_data_source* dataSource,
+                                   const char* mimeType)
+{
+    if (_glfw.wl.dataSource != dataSource)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Unknown clipboard data source");
+        return;
+    }
+}
+
+static void dataSourceHandleSend(void* data,
+                                 struct wl_data_source* dataSource,
+                                 const char* mimeType,
+                                 int fd)
+{
+    const char* string = _glfw.wl.clipboardSendString;
+    size_t len = _glfw.wl.clipboardSendSize;
+    int ret;
+
+    if (_glfw.wl.dataSource != dataSource)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Unknown clipboard data source");
+        return;
+    }
+
+    if (!string)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Copy requested from an invalid string");
+        return;
+    }
+
+    if (strcmp(mimeType, "text/plain;charset=utf-8") != 0)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Wrong MIME type asked from clipboard");
+        close(fd);
+        return;
+    }
+
+    while (len > 0)
+    {
+        ret = write(fd, string, len);
+        if (ret == -1 && errno == EINTR)
+            continue;
+        if (ret == -1)
+        {
+            // TODO: also report errno maybe.
+            _glfwInputError(GLFW_PLATFORM_ERROR,
+                            "Wayland: Error while writing the clipboard");
+            close(fd);
+            return;
+        }
+        len -= ret;
+    }
+    close(fd);
+}
+
+static void dataSourceHandleCancelled(void* data,
+                                      struct wl_data_source* dataSource)
+{
+    wl_data_source_destroy(dataSource);
+
+    if (_glfw.wl.dataSource != dataSource)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Unknown clipboard data source");
+        return;
+    }
+
+    _glfw.wl.dataSource = NULL;
+}
+
+static const struct wl_data_source_listener dataSourceListener = {
+    dataSourceHandleTarget,
+    dataSourceHandleSend,
+    dataSourceHandleCancelled,
+};
+
 void _glfwPlatformSetClipboardString(const char* string)
 {
-    // TODO
-    _glfwInputError(GLFW_PLATFORM_ERROR,
-                    "Wayland: Clipboard setting not implemented yet");
+    if (_glfw.wl.dataSource)
+    {
+        wl_data_source_destroy(_glfw.wl.dataSource);
+        _glfw.wl.dataSource = NULL;
+    }
+
+    if (_glfw.wl.clipboardSendString)
+    {
+        free(_glfw.wl.clipboardSendString);
+        _glfw.wl.clipboardSendString = NULL;
+    }
+
+    _glfw.wl.clipboardSendString = strdup(string);
+    if (!_glfw.wl.clipboardSendString)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Impossible to allocate clipboard string");
+        return;
+    }
+    _glfw.wl.clipboardSendSize = strlen(string);
+    _glfw.wl.dataSource =
+        wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
+    if (!_glfw.wl.dataSource)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "Wayland: Impossible to create clipboard source");
+        free(_glfw.wl.clipboardSendString);
+        return;
+    }
+    wl_data_source_add_listener(_glfw.wl.dataSource,
+                                &dataSourceListener,
+                                NULL);
+    wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8");
+    wl_data_device_set_selection(_glfw.wl.dataDevice,
+                                 _glfw.wl.dataSource,
+                                 _glfw.wl.serial);
 }
 
 static GLFWbool growClipboardString(void)