[Bf-blender-cvs] [b0eff51fb73] master: GHOST/Wayland: primary clipboard support

Campbell Barton noreply at git.blender.org
Thu Oct 20 05:49:39 CEST 2022


Commit: b0eff51fb73cfd9def696e3ede5ff23817f35784
Author: Campbell Barton
Date:   Thu Oct 20 14:02:32 2022 +1100
Branches: master
https://developer.blender.org/rBb0eff51fb73cfd9def696e3ede5ff23817f35784

GHOST/Wayland: primary clipboard support

Match X11's primary clipboard support
(typically used for MMB to paste the previous selection).

===================================================================

M	intern/ghost/CMakeLists.txt
M	intern/ghost/intern/GHOST_SystemWayland.cpp
M	intern/ghost/intern/GHOST_SystemWayland.h

===================================================================

diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 72621648608..c05f2a327b1 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -376,6 +376,10 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
     generate_protocol_bindings(
       "${WAYLAND_PROTOCOLS_DIR}/unstable/tablet/tablet-unstable-v2.xml"
     )
+    # Primary-selection.
+    generate_protocol_bindings(
+      "${WAYLAND_PROTOCOLS_DIR}/unstable/primary-selection/primary-selection-unstable-v1.xml"
+    )
 
     add_definitions(-DWITH_GHOST_WAYLAND)
 
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index b8b9fda8f74..6db8b9d33f8 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -51,6 +51,7 @@
 
 /* Generated by `wayland-scanner`. */
 #include <pointer-constraints-unstable-v1-client-protocol.h>
+#include <primary-selection-unstable-v1-client-protocol.h>
 #include <relative-pointer-unstable-v1-client-protocol.h>
 #include <tablet-unstable-v2-client-protocol.h>
 #include <xdg-output-unstable-v1-client-protocol.h>
@@ -426,6 +427,53 @@ static void gwl_xdg_decor_system_destroy(GWL_XDG_Decor_System *decor)
   delete decor;
 }
 
+struct GWL_PrimarySelection_DataOffer {
+  struct zwp_primary_selection_offer_v1 *id = nullptr;
+  std::atomic<bool> in_use = false;
+
+  std::unordered_set<std::string> types;
+};
+
+struct GWL_PrimarySelection_DataSource {
+  struct zwp_primary_selection_source_v1 *wl_source = nullptr;
+  char *buffer_out = nullptr;
+  size_t buffer_out_len = 0;
+};
+
+/** Primary selection support. */
+struct GWL_PrimarySelection {
+
+  GWL_PrimarySelection_DataSource *data_source = nullptr;
+  std::mutex data_source_mutex;
+
+  GWL_PrimarySelection_DataOffer *data_offer = nullptr;
+  std::mutex data_offer_mutex;
+};
+
+static void gwl_primary_selection_discard_offer(GWL_PrimarySelection *primary)
+{
+  if (primary->data_offer == nullptr) {
+    return;
+  }
+  zwp_primary_selection_offer_v1_destroy(primary->data_offer->id);
+  delete primary->data_offer;
+  primary->data_offer = nullptr;
+}
+
+static void gwl_primary_selection_discard_source(GWL_PrimarySelection *primary)
+{
+  GWL_PrimarySelection_DataSource *data_source = primary->data_source;
+  if (data_source == nullptr) {
+    return;
+  }
+  free(data_source->buffer_out);
+  if (data_source->wl_source) {
+    zwp_primary_selection_source_v1_destroy(data_source->wl_source);
+  }
+  delete primary->data_source;
+  primary->data_source = nullptr;
+}
+
 struct GWL_Seat {
   GHOST_SystemWayland *system = nullptr;
 
@@ -515,6 +563,9 @@ struct GWL_Seat {
   struct GWL_DataSource *data_source = nullptr;
   std::mutex data_source_mutex;
 
+  struct zwp_primary_selection_device_v1 *primary_selection_device = nullptr;
+  struct GWL_PrimarySelection primary_selection;
+
   /** Last device that was active. */
   uint32_t data_source_serial = 0;
 };
@@ -540,6 +591,8 @@ struct GWL_Display {
   struct zwp_tablet_manager_v2 *tablet_manager = nullptr;
   struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
   struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
+
+  struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager = nullptr;
 };
 
 #undef LOG
@@ -642,6 +695,22 @@ static void display_destroy(GWL_Display *display)
       }
     }
 
+    {
+      GWL_PrimarySelection *primary = &seat->primary_selection;
+      std::lock_guard lock{primary->data_offer_mutex};
+      gwl_primary_selection_discard_offer(primary);
+    }
+
+    {
+      GWL_PrimarySelection *primary = &seat->primary_selection;
+      std::lock_guard lock{primary->data_source_mutex};
+      gwl_primary_selection_discard_source(primary);
+    }
+
+    if (seat->primary_selection_device) {
+      zwp_primary_selection_device_v1_destroy(seat->primary_selection_device);
+    }
+
     if (seat->data_device) {
       wl_data_device_release(seat->data_device);
     }
@@ -696,6 +765,10 @@ static void display_destroy(GWL_Display *display)
     zwp_pointer_constraints_v1_destroy(display->pointer_constraints);
   }
 
+  if (display->primary_selection_device_manager) {
+    zwp_primary_selection_device_manager_v1_destroy(display->primary_selection_device_manager);
+  }
+
   if (display->wl_compositor) {
     wl_compositor_destroy(display->wl_compositor);
   }
@@ -1250,6 +1323,35 @@ static std::string read_pipe(GWL_DataOffer *data_offer,
   return data;
 }
 
+static std::string read_pipe_primary(GWL_PrimarySelection_DataOffer *data_offer,
+                                     const std::string mime_receive,
+                                     std::mutex *mutex)
+{
+  int pipefd[2];
+  if (UNLIKELY(pipe(pipefd) != 0)) {
+    return {};
+  }
+  zwp_primary_selection_offer_v1_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
+  close(pipefd[1]);
+
+  data_offer->in_use.store(false);
+
+  if (mutex) {
+    mutex->unlock();
+  }
+  /* WARNING: `data_offer_base` may be freed from now on. */
+
+  std::string data;
+  ssize_t len;
+  char buffer[4096];
+  while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
+    data.insert(data.end(), buffer, buffer + len);
+  }
+  close(pipefd[0]);
+
+  return data;
+}
+
 /**
  * A target accepts an offered mime type.
  *
@@ -2880,6 +2982,158 @@ static const struct wl_keyboard_listener keyboard_listener = {
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Listener (Primary Selection Offer), #zwp_primary_selection_offer_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_PRIMARY_SELECTION_OFFER = {"ghost.wl.handle.primary_selection_offer"};
+#define LOG (&LOG_WL_PRIMARY_SELECTION_OFFER)
+
+static void primary_selection_offer_offer(void *data,
+                                          struct zwp_primary_selection_offer_v1 *id,
+                                          const char *type)
+{
+  GWL_PrimarySelection_DataOffer *data_offer = static_cast<GWL_PrimarySelection_DataOffer *>(data);
+  if (data_offer->id != id) {
+    CLOG_INFO(LOG, 2, "offer: %p: offer for unknown selection %p of %s (skipped)", data, id, type);
+    return;
+  }
+
+  data_offer->types.insert(std::string(type));
+}
+
+static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = {
+    primary_selection_offer_offer,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Primary Selection Device), #zwp_primary_selection_device_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_PRIMARY_SELECTION_DEVICE = {"ghost.wl.handle.primary_selection_device"};
+#define LOG (&LOG_WL_PRIMARY_SELECTION_DEVICE)
+
+static void primary_selection_device_handle_data_offer(
+    void * /*data*/,
+    struct zwp_primary_selection_device_v1 * /*zwp_primary_selection_device_v1*/,
+    struct zwp_primary_selection_offer_v1 *id)
+{
+  CLOG_INFO(LOG, 2, "data_offer");
+
+  GWL_PrimarySelection_DataOffer *data_offer = new GWL_PrimarySelection_DataOffer;
+  data_offer->id = id;
+  zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, data_offer);
+}
+
+static void primary_selection_device_handle_selection(
+    void *data,
+    struct zwp_primary_selection_device_v1 * /*zwp_primary_selection_device_v1*/,
+    struct zwp_primary_selection_offer_v1 *id)
+{
+  GWL_PrimarySelection *primary = static_cast<GWL_PrimarySelection *>(data);
+
+  std::lock_guard lock{primary->data_offer_mutex};
+
+  /* Delete old data offer. */
+  if (primary->data_offer != nullptr) {
+    gwl_primary_selection_discard_offer(primary);
+  }
+
+  if (id == nullptr) {
+    CLOG_INFO(LOG, 2, "selection: (skipped)");
+    return;
+  }
+  CLOG_INFO(LOG, 2, "selection");
+  /* Get new data offer. */
+  GWL_PrimarySelection_DataOffer *data_offer = static_cast<GWL_PrimarySelection_DataOffer *>(
+      zwp_primary_selection_offer_v1_get_user_data(id));
+  primary->data_offer = data_offer;
+
+  auto read_selection_fn = [](GWL_PrimarySelection *primary) {
+    GHOST_SystemWayland *system = static_cast<GHOST_SystemWayland *>(GHOST_ISystem::getSystem());
+    primary->data_offer_mutex.lock();
+
+    GWL_PrimarySelection_DataOffer *data_offer = primary->data_offer;
+    std::string mime_receive;
+    for (const std::string type : {mime_text_utf8, mime_text_plain}) {
+      if (data_offer->types.count(type)) {
+        mime_receive = type;
+        break;
+      }
+    }
+    const std::string data = read_pipe_primary(
+        data_offer, mime_receive, &primary->data_offer_mutex);
+
+    {
+      std::lock_guard lock{system_clipboard_mutex};
+      system->clipboard_primary_set(data);
+    }
+  };
+
+  std::thread read_thread(read_selection_fn, primary);
+  read_thread.detach();
+}
+
+static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
+    primary_selection_device_handle_data_offer,
+    primary_selection_device_handle_selection,
+};
+
+#undef LOG
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Listener (Primary Selection Source), #zwp_primary_selection_source_v1_listener
+ * \{ */
+
+static CLG_LogRef LOG_WL_PRIMARY_SELECTION_SOURCE = {"ghost.wl.handle.primary_selection_source"};
+#define LOG (&LOG_WL_PRIMARY_SELECTION_SOURCE)
+
+static void primary_selection_source_send(void *data,
+                                          struct zwp_primary_selection_source_v1 * /*source*/,
+                                          const char * /*mime_type*/,
+                                          int32_t fd)
+{
+  CLOG_INFO(LOG, 2, "send");
+
+  GWL_PrimarySelection *primary = static_cast<GWL_PrimarySelection *>(data);
+
+  std::lock_guard lock{primary->data_source_mutex};
+  GWL_PrimarySelection_DataSource *data_source = primary->data_source;
+
+  const char *const buffer = data_source->buffer_out;
+  if (write(fd, buffer, data_source->buffer_out_len) < 0) {
+    GHOST_PRINT("error writing to primary clipboard: " << std::strerror(errno) << std::endl);
+  }
+  close(fd);
+}
+
+static void primary_selection_source_cancelled(void *data,
+          

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list