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.
parent
997c69b9ef
commit
2ffd6d0208
|
@ -40,13 +40,23 @@
|
|||
static Window
|
||||
GetWindow(_THIS)
|
||||
{
|
||||
SDL_Window *window;
|
||||
SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
|
||||
|
||||
window = _this->windows;
|
||||
if (window) {
|
||||
return ((SDL_WindowData *) window->driverdata)->xwindow;
|
||||
/* 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue