Răsfoiți Sursa

x11: Make a separate unmapped window to own clipboard selections.

Now the clipboard isn't lost if you destroy a specific SDL_Window, as it
works on other platforms. You will still lose the clipboard data on
SDL_Quit() or process termination, but that's X11 for you; run a
Clipboard Manager daemon.

Fixes Bugzilla #3222.
Fixes Bugzilla #3718.
Ryan C. Gordon 7 ani în urmă
părinte
comite
2ffd6d0208

+ 16 - 6
src/video/x11/SDL_x11clipboard.c

@@ -40,13 +40,23 @@
 static Window
 GetWindow(_THIS)
 {
-    SDL_Window *window;
-
-    window = _this->windows;
-    if (window) {
-        return ((SDL_WindowData *) window->driverdata)->xwindow;
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+
+    /* We create an unmapped window that exists just to manage the clipboard,
+       since X11 selection data is tied to a specific window and dies with it.
+       We create the window on demand, so apps that don't use the clipboard
+       don't have to keep an unnecessary resource around. */
+    if (data->clipboard_window == None) {
+        Display *dpy = data->display;
+        Window parent = RootWindow(dpy, DefaultScreen(dpy));
+        XSetWindowAttributes xattr;
+        data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
+                                                   CopyFromParent, InputOnly,
+                                                   CopyFromParent, 0, &xattr);
+        X11_XFlush(data->display);
     }
-    return None;
+
+    return data->clipboard_window;
 }
 
 /* We use our own cut-buffer for intermediate storage instead of  

+ 96 - 62
src/video/x11/SDL_x11events.c

@@ -38,6 +38,7 @@
 #include "SDL_hints.h"
 #include "SDL_timer.h"
 #include "SDL_syswm.h"
+#include "SDL_assert.h"
 
 #include <stdio.h>
 
@@ -537,6 +538,95 @@ X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest)
     }
 }
 
+static void
+X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+    Display *display = videodata->display;
+
+    SDL_assert(videodata->clipboard_window != None);
+    SDL_assert(xevent->xany.window == videodata->clipboard_window);
+
+    switch (xevent->type) {
+    /* Copy the selection from our own CUTBUFFER to the requested property */
+        case SelectionRequest: {
+            const XSelectionRequestEvent *req = &xevent->xselectionrequest;
+            XEvent sevent;
+            int seln_format;
+            unsigned long nbytes;
+            unsigned long overflow;
+            unsigned char *seln_data;
+
+#ifdef DEBUG_XEVENTS
+            printf("window CLIPBOARD: SelectionRequest (requestor = %ld, target = %ld)\n",
+                req->requestor, req->target);
+#endif
+
+            SDL_zero(sevent);
+            sevent.xany.type = SelectionNotify;
+            sevent.xselection.selection = req->selection;
+            sevent.xselection.target = None;
+            sevent.xselection.property = None;  /* tell them no by default */
+            sevent.xselection.requestor = req->requestor;
+            sevent.xselection.time = req->time;
+
+            /* !!! FIXME: We were probably storing this on the root window
+               because an SDL window might go away...? but we don't have to do
+               this now (or ever, really). */
+            if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
+                    X11_GetSDLCutBufferClipboardType(display), 0, INT_MAX/4, False, req->target,
+                    &sevent.xselection.target, &seln_format, &nbytes,
+                    &overflow, &seln_data) == Success) {
+                /* !!! FIXME: cache atoms */
+                Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
+                if (sevent.xselection.target == req->target) {
+                    X11_XChangeProperty(display, req->requestor, req->property,
+                        sevent.xselection.target, seln_format, PropModeReplace,
+                        seln_data, nbytes);
+                    sevent.xselection.property = req->property;
+                } else if (XA_TARGETS == req->target) {
+                    Atom SupportedFormats[] = { XA_TARGETS, sevent.xselection.target };
+                    X11_XChangeProperty(display, req->requestor, req->property,
+                        XA_ATOM, 32, PropModeReplace,
+                        (unsigned char*)SupportedFormats,
+                        SDL_arraysize(SupportedFormats));
+                    sevent.xselection.property = req->property;
+                    sevent.xselection.target = XA_TARGETS;
+                }
+                X11_XFree(seln_data);
+            }
+            X11_XSendEvent(display, req->requestor, False, 0, &sevent);
+            X11_XSync(display, False);
+        }
+        break;
+
+        case SelectionNotify: {
+#ifdef DEBUG_XEVENTS
+            printf("window CLIPBOARD: SelectionNotify (requestor = %ld, target = %ld)\n",
+                xevent.xselection.requestor, xevent.xselection.target);
+#endif
+            videodata->selection_waiting = SDL_FALSE;
+        }
+        break;
+
+        case SelectionClear: {
+            /* !!! FIXME: cache atoms */
+            Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
+
+#ifdef DEBUG_XEVENTS
+            printf("window CLIPBOARD: SelectionClear (requestor = %ld, target = %ld)\n",
+                xevent.xselection.requestor, xevent.xselection.target);
+#endif
+
+            if (xevent->xselectionclear.selection == XA_PRIMARY ||
+                (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD)) {
+                SDL_SendClipboardUpdate();
+            }
+        }
+        break;
+    }
+}
+
 
 static void
 X11_DispatchEvent(_THIS)
@@ -615,6 +705,12 @@ X11_DispatchEvent(_THIS)
            xevent.type, xevent.xany.display, xevent.xany.window);
 #endif
 
+    if ((videodata->clipboard_window != None) &&
+        (videodata->clipboard_window == xevent.xany.window)) {
+        X11_HandleClipboardEvent(_this, &xevent);
+        return;
+    }
+
     data = NULL;
     if (videodata && videodata->windowlist) {
         for (i = 0; i < videodata->numwindows; ++i) {
@@ -1223,55 +1319,6 @@ X11_DispatchEvent(_THIS)
         }
         break;
 
-    /* Copy the selection from our own CUTBUFFER to the requested property */
-    case SelectionRequest: {
-            XSelectionRequestEvent *req;
-            XEvent sevent;
-            int seln_format;
-            unsigned long nbytes;
-            unsigned long overflow;
-            unsigned char *seln_data;
-
-            req = &xevent.xselectionrequest;
-#ifdef DEBUG_XEVENTS
-            printf("window %p: SelectionRequest (requestor = %ld, target = %ld)\n", data,
-                req->requestor, req->target);
-#endif
-
-            SDL_zero(sevent);
-            sevent.xany.type = SelectionNotify;
-            sevent.xselection.selection = req->selection;
-            sevent.xselection.target = None;
-            sevent.xselection.property = None;
-            sevent.xselection.requestor = req->requestor;
-            sevent.xselection.time = req->time;
-
-            if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
-                    X11_GetSDLCutBufferClipboardType(display), 0, INT_MAX/4, False, req->target,
-                    &sevent.xselection.target, &seln_format, &nbytes,
-                    &overflow, &seln_data) == Success) {
-                Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
-                if (sevent.xselection.target == req->target) {
-                    X11_XChangeProperty(display, req->requestor, req->property,
-                        sevent.xselection.target, seln_format, PropModeReplace,
-                        seln_data, nbytes);
-                    sevent.xselection.property = req->property;
-                } else if (XA_TARGETS == req->target) {
-                    Atom SupportedFormats[] = { XA_TARGETS, sevent.xselection.target };
-                    X11_XChangeProperty(display, req->requestor, req->property,
-                        XA_ATOM, 32, PropModeReplace,
-                        (unsigned char*)SupportedFormats,
-                        SDL_arraysize(SupportedFormats));
-                    sevent.xselection.property = req->property;
-                    sevent.xselection.target = XA_TARGETS;
-                }
-                X11_XFree(seln_data);
-            }
-            X11_XSendEvent(display, req->requestor, False, 0, &sevent);
-            X11_XSync(display, False);
-        }
-        break;
-
     case SelectionNotify: {
             Atom target = xevent.xselection.target;
 #ifdef DEBUG_XEVENTS
@@ -1315,19 +1362,6 @@ X11_DispatchEvent(_THIS)
                 X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
 
                 X11_XSync(display, False);
-
-            } else {
-                videodata->selection_waiting = SDL_FALSE;
-            }
-        }
-        break;
-
-    case SelectionClear: {
-            Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
-
-            if (xevent.xselectionclear.selection == XA_PRIMARY ||
-                (XA_CLIPBOARD != None && xevent.xselectionclear.selection == XA_CLIPBOARD)) {
-                SDL_SendClipboardUpdate();
             }
         }
         break;

+ 4 - 0
src/video/x11/SDL_x11video.c

@@ -449,6 +449,10 @@ X11_VideoQuit(_THIS)
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
 
+    if (data->clipboard_window) {
+        X11_XDestroyWindow(data->display, data->clipboard_window);
+    }
+
     SDL_free(data->classname);
 #ifdef X_HAVE_UTF8_STRING
     if (data->im) {

+ 1 - 0
src/video/x11/SDL_x11video.h

@@ -82,6 +82,7 @@ typedef struct SDL_VideoData
     SDL_WindowData **windowlist;
     int windowlistlength;
     XID window_group;
+    Window clipboard_window;
 
     /* This is true for ICCCM2.0-compliant window managers */
     SDL_bool net_wm;