Skip to content

Commit

Permalink
x11: Make a separate unmapped window to own clipboard selections.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
icculus committed Jul 31, 2017
1 parent 997c69b commit 2ffd6d0
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 68 deletions.
22 changes: 16 additions & 6 deletions src/video/x11/SDL_x11clipboard.c
Expand Up @@ -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
Expand Down
158 changes: 96 additions & 62 deletions src/video/x11/SDL_x11events.c
Expand Up @@ -38,6 +38,7 @@
#include "SDL_hints.h"
#include "SDL_timer.h"
#include "SDL_syswm.h"
#include "SDL_assert.h"

#include <stdio.h>

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/video/x11/SDL_x11video.c
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/video/x11/SDL_x11video.h
Expand Up @@ -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;
Expand Down

0 comments on commit 2ffd6d0

Please sign in to comment.