[Bf-blender-cvs] [8a43bfd8fd8] master: GHOST/Wayland: refactor copy/paste buffer storage

Campbell Barton noreply at git.blender.org
Thu Oct 20 13:16:57 CEST 2022


Commit: 8a43bfd8fd8d08438c5cc58befdf3e661aa17297
Author: Campbell Barton
Date:   Thu Oct 20 20:44:03 2022 +1100
Branches: master
https://developer.blender.org/rB8a43bfd8fd8d08438c5cc58befdf3e661aa17297

GHOST/Wayland: refactor copy/paste buffer storage

- Improve reporting when reading a file descriptor into a buffer fails,
  also check for failure to allocate memory.
- Store buffers with size in a simple struct.
- Use shared utility functions for simple buffer operations.

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

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

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

diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 6db8b9d33f8..42bfc6aa1e7 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -237,6 +237,43 @@ static const GWL_ModifierInfo g_modifier_info_table[MOD_INDEX_NUM] = {
 /** \name Private Types & Defines
  * \{ */
 
+struct GWL_SimpleBuffer {
+  /** Constant data, but may be freed. */
+  const char *data = nullptr;
+  size_t data_size = 0;
+};
+
+static void gwl_simple_buffer_free_data(GWL_SimpleBuffer *buffer)
+{
+  free(const_cast<char *>(buffer->data));
+  buffer->data = nullptr;
+  buffer->data_size = 0;
+}
+
+static void gwl_simple_buffer_set(GWL_SimpleBuffer *buffer, const char *data, size_t data_size)
+{
+  free(const_cast<char *>(buffer->data));
+  buffer->data = data;
+  buffer->data_size = data_size;
+}
+
+static void gwl_simple_buffer_set_from_string(GWL_SimpleBuffer *buffer, const char *str)
+{
+  free(const_cast<char *>(buffer->data));
+  buffer->data_size = strlen(str);
+  char *data = static_cast<char *>(malloc(buffer->data_size));
+  std::memcpy(data, str, buffer->data_size);
+  buffer->data = data;
+}
+
+static char *gwl_simple_buffer_as_string(const GWL_SimpleBuffer *buffer)
+{
+  char *buffer_str = static_cast<char *>(malloc(buffer->data_size + 1));
+  memcpy(buffer_str, buffer->data, buffer->data_size);
+  buffer_str[buffer->data_size] = '\0';
+  return buffer_str;
+}
+
 /**
  * From XKB internals, use for converting a scan-code from WAYLAND to a #xkb_keycode_t.
  * Ideally this wouldn't need a local define.
@@ -293,8 +330,7 @@ struct GWL_DataOffer {
 
 struct GWL_DataSource {
   struct wl_data_source *wl_data_source = nullptr;
-  char *buffer_out = nullptr;
-  size_t buffer_out_len = 0;
+  GWL_SimpleBuffer buffer_out;
 };
 
 /**
@@ -436,8 +472,7 @@ struct GWL_PrimarySelection_DataOffer {
 
 struct GWL_PrimarySelection_DataSource {
   struct zwp_primary_selection_source_v1 *wl_source = nullptr;
-  char *buffer_out = nullptr;
-  size_t buffer_out_len = 0;
+  GWL_SimpleBuffer buffer_out;
 };
 
 /** Primary selection support. */
@@ -466,7 +501,7 @@ static void gwl_primary_selection_discard_source(GWL_PrimarySelection *primary)
   if (data_source == nullptr) {
     return;
   }
-  free(data_source->buffer_out);
+  gwl_simple_buffer_free_data(&data_source->buffer_out);
   if (data_source->wl_source) {
     zwp_primary_selection_source_v1_destroy(data_source->wl_source);
   }
@@ -593,6 +628,9 @@ struct GWL_Display {
   struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
 
   struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager = nullptr;
+
+  GWL_SimpleBuffer clipboard;
+  GWL_SimpleBuffer clipboard_primary;
 };
 
 #undef LOG
@@ -671,7 +709,7 @@ static void display_destroy(GWL_Display *display)
     {
       std::lock_guard lock{seat->data_source_mutex};
       if (seat->data_source) {
-        free(seat->data_source->buffer_out);
+        gwl_simple_buffer_free_data(&seat->data_source->buffer_out);
         if (seat->data_source->wl_data_source) {
           wl_data_source_destroy(seat->data_source->wl_data_source);
         }
@@ -795,6 +833,9 @@ static void display_destroy(GWL_Display *display)
     wl_display_disconnect(display->wl_display);
   }
 
+  gwl_simple_buffer_free_data(&display->clipboard);
+  gwl_simple_buffer_free_data(&display->clipboard_primary);
+
   delete display;
 }
 
@@ -1294,9 +1335,74 @@ static void dnd_events(const GWL_Seat *const seat, const GHOST_TEventType event)
   }
 }
 
-static std::string read_pipe(GWL_DataOffer *data_offer,
+/**
+ * Read from `fd` into a buffer which is returned.
+ * \return the buffer or null on failure.
+ */
+static const char *read_file_as_buffer(const int fd, size_t *r_len)
+{
+  struct ByteChunk {
+    ByteChunk *next;
+    char data[4096 - sizeof(ByteChunk *)];
+  };
+  ByteChunk *chunk_first = nullptr, **chunk_link_p = &chunk_first;
+  bool ok = true;
+  size_t len = 0;
+  while (true) {
+    ByteChunk *chunk = static_cast<typeof(chunk)>(malloc(sizeof(*chunk)));
+    if (UNLIKELY(chunk == nullptr)) {
+      CLOG_WARN(LOG, "unable to allocate chunk for file buffer");
+      ok = false;
+      break;
+    }
+    chunk->next = nullptr;
+    const ssize_t len_chunk = read(fd, chunk->data, sizeof(chunk->data));
+    if (len_chunk <= 0) {
+      if (UNLIKELY(len_chunk < 0)) {
+        CLOG_WARN(LOG, "error reading from pipe: %s", std::strerror(errno));
+        ok = false;
+      }
+      free(chunk);
+      break;
+    }
+    if (chunk_first == nullptr) {
+      chunk_first = chunk;
+    }
+    *chunk_link_p = chunk;
+    chunk_link_p = &chunk->next;
+    len += len_chunk;
+  }
+
+  char *buf = nullptr;
+  if (ok) {
+    buf = static_cast<char *>(malloc(len));
+    if (UNLIKELY(buf == nullptr)) {
+      CLOG_WARN(LOG, "unable to allocate file buffer: %zu bytes", len);
+      ok = false;
+    }
+  }
+
+  *r_len = ok ? len : 0;
+  char *buf_stride = buf;
+  while (chunk_first) {
+    if (ok) {
+      const size_t len_chunk = std::min(len, sizeof(chunk_first->data));
+      memcpy(buf_stride, chunk_first->data, len_chunk);
+      buf_stride += len_chunk;
+      len -= len_chunk;
+    }
+    ByteChunk *chunk = chunk_first->next;
+    free(chunk_first);
+    chunk_first = chunk;
+  }
+
+  return buf;
+}
+
+static const char *read_pipe(GWL_DataOffer *data_offer,
                              const std::string mime_receive,
-                             std::mutex *mutex)
+                             std::mutex *mutex,
+                             size_t *r_len)
 {
   int pipefd[2];
   if (UNLIKELY(pipe(pipefd) != 0)) {
@@ -1312,20 +1418,15 @@ static std::string read_pipe(GWL_DataOffer *data_offer,
   }
   /* WARNING: `data_offer` 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);
-  }
+  const char *buf = read_file_as_buffer(pipefd[0], r_len);
   close(pipefd[0]);
-
-  return data;
+  return buf;
 }
 
-static std::string read_pipe_primary(GWL_PrimarySelection_DataOffer *data_offer,
+static const char *read_pipe_primary(GWL_PrimarySelection_DataOffer *data_offer,
                                      const std::string mime_receive,
-                                     std::mutex *mutex)
+                                     std::mutex *mutex,
+                                     size_t *r_len)
 {
   int pipefd[2];
   if (UNLIKELY(pipe(pipefd) != 0)) {
@@ -1339,17 +1440,10 @@ static std::string read_pipe_primary(GWL_PrimarySelection_DataOffer *data_offer,
   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);
-  }
+  /* WARNING: `data_offer` may be freed from now on. */
+  const char *buf = read_file_as_buffer(pipefd[0], r_len);
   close(pipefd[0]);
-
-  return data;
+  return buf;
 }
 
 /**
@@ -1375,9 +1469,9 @@ static void data_source_handle_send(void *data,
 
   CLOG_INFO(LOG, 2, "send");
 
-  const char *const buffer = seat->data_source->buffer_out;
-  if (write(fd, buffer, seat->data_source->buffer_out_len) < 0) {
-    GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
+  const char *const buffer = seat->data_source->buffer_out.data;
+  if (UNLIKELY(write(fd, buffer, seat->data_source->buffer_out.data_size) < 0)) {
+    CLOG_WARN(LOG, "error writing to clipboard: %s", std::strerror(errno));
   }
   close(fd);
 }
@@ -1598,7 +1692,9 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
                          const std::string mime_receive) {
     const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)};
 
-    const std::string data = read_pipe(data_offer, mime_receive, nullptr);
+    size_t data_buf_len = 0;
+    const char *data_buf = read_pipe(data_offer, mime_receive, nullptr, &data_buf_len);
+    std::string data = data_buf ? std::string(data_buf, data_buf_len) : "";
 
     CLOG_INFO(
         LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive.c_str(), data.c_str());
@@ -1713,12 +1809,15 @@ static void data_device_handle_selection(void *data,
         break;
       }
     }
-    const std::string data = read_pipe(
-        data_offer, mime_receive, &seat->data_offer_copy_paste_mutex);
+
+    size_t data_len = 0;
+    const char *data = read_pipe(
+        data_offer, mime_receive, &seat->data_offer_copy_paste_mutex, &data_len);
 
     {
       std::lock_guard lock{system_clipboard_mutex};
-      system->clipboard_set(data);
+      GWL_SimpleBuffer *buf = system->clipboard_data(false);
+      gwl_simple_buffer_set(buf, data, data_len);
     }
   };
 
@@ -3065,12 +3164,14 @@ static void primary_selection_device_handle_selection(
         break;
       }
     }
-    const std::string data = read_pipe_primary(
-        data_offer, mime_receive, &primary->data_offer_mutex);
+    size_t data_len = 0;
+    const char *data = read_pipe_primary(
+        data_offer, mime_receive, &primary->data_offer_mutex, &data_len);
 
     {
       std::lock_guard lock{system_clipboard_mutex};
-      system->clipboard_primary_set(data);
+      GWL_SimpleBuffer *buf = system->clipboard_data(true);
+      gwl_simple_buffer_set(buf, data, data_len);
     }
   };
 
@@ -3106,9 +3207,9 @@ static void primary_selection_source_send(void *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);
+  const char *const buffer = data_source->buffer_out.data;
+  if (UNLIKELY(write(fd, buffer, data_source->buffer_out.data_size) < 0)) {
+    CLOG_WARN(LOG, "error writing to primary clipboard: %s", std::strerror(errno));
   }
   close(fd);
 }
@@ -3837,10 +3938,11 @@ GHOST_TSuccess G

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list