FLTK logo

[master] ef72df0 - Improve X11 selection data transfer (INCR) protocol (#451)

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.commit  ]
 
Previous Message ]Next Message ]

[master] ef72df0 - Improve X11 selection data transfer (INCR) protocol (#451) "Albrecht Schlosser" Jul 02, 2022  
 
commit ef72df0dc7c3c48373c52085a855ac6ce6df4868
Author:     Albrecht Schlosser <albrechts.fltk@online.de>
AuthorDate: Sat Jul 2 15:44:46 2022 +0200
Commit:     Albrecht Schlosser <albrechts.fltk@online.de>
CommitDate: Sat Jul 2 15:44:46 2022 +0200

    Improve X11 selection data transfer (INCR) protocol (#451)
    
    This improves reading large selections and fixes one more bug.
    
    - Fix reading selection data size of clients that don't send the size.
      These clients don't respect the ICCCM. Using a default size instead.
    
    - Improve memory allocation by pre-allocating at least 4 MB and
      extending by 4 MB to reduce the number of realloc() operations.
      This may waste up to 4 MB buffer space but this is only an issue
      if clients don't send an appropriate size (see above).
    
    - Limit the initial allocation to 200 MB in case clients send higher
      values. This is very unlikely and might be a bug in these clients.
      Default extension as above would apply anyway.
    
    - Add more comments and optional debug statements (commented out).
    
    Note: reading selections (clipboard) from other clients using the
      INCR protocol is implemented but the opposite (providing large
      selections (clipboard) by using the INCR protocol is not.

 src/Fl_x.cxx | 57 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 46 insertions(+), 11 deletions(-)

diff --git src/Fl_x.cxx src/Fl_x.cxx
index c07a69e..38ab036 100644
--- src/Fl_x.cxx
+++ src/Fl_x.cxx
@@ -1025,13 +1025,25 @@ static bool getNextEvent(XEvent *event_return) {
 
 static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lower_bound) {
   // fprintf(stderr,"Incremental transfer starting due to INCR property\n");
+  // fprintf(stderr, "[getIncrData:%d] lower_bound [in ] =%10ld\n", __LINE__, lower_bound);
+  const size_t alloc_min =   4 * 1024 * 1024; // min. initial allocation
+  const size_t alloc_max = 200 * 1024 * 1024; // max. initial allocation
+  const size_t alloc_inc =   4 * 1024 * 1024; // (min.) increase if necessary
   size_t total = 0;
+  size_t data_size = lower_bound + 1;
+  if (data_size < alloc_min) {
+    data_size = alloc_min;
+  } else if (data_size > alloc_max) {
+    data_size = alloc_max;
+  }
+  // fprintf(stderr, "[getIncrData:%d] initial alloc.    =%10ld\n", __LINE__, data_size);
+
   XEvent event;
   XDeleteProperty(fl_display, selevent.requestor, selevent.property);
-  data = (uchar*)realloc(data, lower_bound);
+  data = (uchar*)realloc(data, data_size);
   if (!data) {
-    // fprintf(stderr, "[getIncrData:%d] realloc() FAILED, size = %ld\n", __LINE__, lower_bound);
-    Fl::fatal("Clipboard data transfer failed, size %ld too large.", lower_bound);
+    // fprintf(stderr, "[getIncrData:%d] realloc() FAILED, size = %ld\n", __LINE__, data_size);
+    Fl::fatal("Clipboard data transfer failed, size %ld is too large.", data_size);
   }
   for (;;) {
     if (!getNextEvent(&event)) {
@@ -1047,7 +1059,7 @@ static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lo
       unsigned long bytes_after;
       unsigned char* prop = 0;
       long offset = 0;
-      size_t num_bytes;
+      size_t num_bytes = 0;
       // size_t slice_size = 0;
       do {
         XGetWindowProperty(fl_display, selevent.requestor, selevent.property, offset, 70000, True,
@@ -1055,11 +1067,16 @@ static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lo
         num_bytes = nitems * (actual_format / 8);
         offset += num_bytes/4;
         // slice_size += num_bytes;
-        if (total + num_bytes > lower_bound) {
-          data = (uchar*)realloc(data, total + num_bytes);
+        if (total + num_bytes + bytes_after + 1 > data_size) {
+          data_size += alloc_inc;
+          if (total + num_bytes + bytes_after + 1 > data_size)
+            data_size = total + num_bytes + bytes_after + 1;
+          // printf(" -- realloc(%9ld), total=%10ld, num_bytes=%7ld, bytes_after=%7ld (%7ld), required=%10ld\n",
+          //        data_size, total, num_bytes, bytes_after, num_bytes + bytes_after, total + num_bytes + bytes_after + 1);
+          data = (uchar*)realloc(data, data_size);
           if (!data) {
-            // fprintf(stderr, "[getIncrData():%d] realloc() FAILED, size = %ld\n", __LINE__, total + num_bytes);
-            Fl::fatal("Clipboard data transfer failed, size %ld too large.", total + num_bytes);
+            // fprintf(stderr, "[getIncrData():%d] realloc() FAILED, size = %ld\n", __LINE__, data_size);
+            Fl::fatal("Clipboard data transfer failed, size %ld is too large.", data_size);
           }
         }
         memcpy(data + total, prop, num_bytes);
@@ -1089,6 +1106,7 @@ static long getIncrData(uchar* &data, const XSelectionEvent& selevent, size_t lo
     }
   }
   XDeleteProperty(fl_display, selevent.requestor, selevent.property);
+  // fprintf(stderr, "[getIncrData:%d] total data  [out] =%10ld\n", __LINE__, (long)total);
   return (long)total;
 }
 
@@ -1316,9 +1334,26 @@ int fl_handle(const XEvent& thisevent)
         return true;
       }
       if (actual == fl_INCR) {
-        // an X11 "integer" (32 bit), the "lower bound" of the clipboard size (see ICCCM)
-        size_t lower_bound = (*(unsigned long *)portion) & 0xFFFFFFFF;
-        // fprintf(stderr, "[fl_handle:%d] INCR: lower_bound = %ld\n", __LINE__, lower_bound);
+        // From ICCCM: "The contents of the INCR property will be an integer, which
+        // represents a lower bound on the number of bytes of data in the selection."
+        //
+        // However, some X clients don't set the integer ("lower bound") in the INCR
+        // property, hence 'count' below is zero and we must not access '*portion'.
+        // Debug:
+#if (0)
+        fprintf(stderr,
+                "[fl_handle(SelectionNotify/INCR):%d] actual=%ld (INCR), format=%d, count=%ld, remaining=%ld",
+                __LINE__, actual, format, count, remaining);
+        if (portion && count > 0) {
+          fprintf(stderr,
+                ", portion=%p (%ld)", portion, *(long*)portion);
+        }
+        fprintf(stderr, "\n");
+#endif
+        size_t lower_bound = 0;
+        if (portion && count > 0) {
+          lower_bound = *(unsigned long *)portion;
+        }
         bytesread = getIncrData(sn_buffer, xevent.xselection, lower_bound);
         XFree(portion);
         break;
Direct Link to Message ]
 
     
Previous Message ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.