X11: Add support for reading clipboard via INCR

This allows glfwGetClipboardString to retrieve clipboard contents larger
than (typically) 2^18 bytes.

Related to #275.
This commit is contained in:
Camilla Löwy 2017-08-07 23:23:37 +02:00
parent f30acd8f74
commit f7dc6df02c
4 changed files with 83 additions and 18 deletions

View File

@ -205,6 +205,7 @@ information on what to include when reporting a bug.
- [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747) - [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747)
- [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size - [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size
- [X11] Bugfix: Key name string encoding depended on current locale (#981,#983) - [X11] Bugfix: Key name string encoding depended on current locale (#981,#983)
- [X11] Bugfix: Incremental reading of selections was not supported (#275)
- [Linux] Moved to evdev for joystick input (#906,#1005) - [Linux] Moved to evdev for joystick input (#906,#1005)
- [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932)
- [Linux] Bugfix: The joystick device path could be truncated (#1025) - [Linux] Bugfix: The joystick device path could be truncated (#1025)

View File

@ -675,6 +675,7 @@ static GLFWbool initExtensions(void)
_glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
_glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
_glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False);
_glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False);
_glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
// Clipboard manager atoms // Clipboard manager atoms

View File

@ -265,6 +265,7 @@ typedef struct _GLFWlibraryX11
// Selection (clipboard) atoms // Selection (clipboard) atoms
Atom TARGETS; Atom TARGETS;
Atom MULTIPLE; Atom MULTIPLE;
Atom INCR;
Atom CLIPBOARD; Atom CLIPBOARD;
Atom PRIMARY; Atom PRIMARY;
Atom CLIPBOARD_MANAGER; Atom CLIPBOARD_MANAGER;

View File

@ -164,6 +164,17 @@ static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointe
event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
} }
// Returns whether it is a property event for the specified selection transfer
//
static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer)
{
XEvent* notification = (XEvent*) pointer;
return event->type == PropertyNotify &&
event->xproperty.state == PropertyNewValue &&
event->xproperty.window == notification->xselection.requestor &&
event->xproperty.atom == notification->xselection.property;
}
// Translates a GLFW standard cursor to a font cursor shape // Translates a GLFW standard cursor to a font cursor shape
// //
static int translateCursorShape(int shape) static int translateCursorShape(int shape)
@ -841,10 +852,10 @@ static const char* getSelectionString(Atom selection)
{ {
size_t i; size_t i;
char** selectionString = NULL; char** selectionString = NULL;
const Atom formats[] = { _glfw.x11.UTF8_STRING, const Atom targets[] = { _glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_STRING, _glfw.x11.COMPOUND_STRING,
XA_STRING }; XA_STRING };
const size_t formatCount = sizeof(formats) / sizeof(formats[0]); const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
if (selection == _glfw.x11.PRIMARY) if (selection == _glfw.x11.PRIMARY)
selectionString = &_glfw.x11.primarySelectionString; selectionString = &_glfw.x11.primarySelectionString;
@ -862,14 +873,17 @@ static const char* getSelectionString(Atom selection)
free(*selectionString); free(*selectionString);
*selectionString = NULL; *selectionString = NULL;
for (i = 0; i < formatCount; i++) for (i = 0; i < targetCount; i++)
{ {
char* data; char* data;
XEvent event; Atom actualType;
int actualFormat;
unsigned long itemCount, bytesAfter;
XEvent notification, dummy;
XConvertSelection(_glfw.x11.display, XConvertSelection(_glfw.x11.display,
selection, selection,
formats[i], targets[i],
_glfw.x11.GLFW_SELECTION, _glfw.x11.GLFW_SELECTION,
_glfw.x11.helperWindowHandle, _glfw.x11.helperWindowHandle,
CurrentTime); CurrentTime);
@ -877,28 +891,76 @@ static const char* getSelectionString(Atom selection)
while (!XCheckTypedWindowEvent(_glfw.x11.display, while (!XCheckTypedWindowEvent(_glfw.x11.display,
_glfw.x11.helperWindowHandle, _glfw.x11.helperWindowHandle,
SelectionNotify, SelectionNotify,
&event)) &notification))
{ {
waitForEvent(NULL); waitForEvent(NULL);
} }
if (event.xselection.property == None) if (notification.xselection.property == None)
continue; continue;
if (_glfwGetWindowPropertyX11(event.xselection.requestor, XCheckIfEvent(_glfw.x11.display,
event.xselection.property, &dummy,
event.xselection.target, isSelPropNewValueNotify,
(unsigned char**) &data)) (XPointer) &notification);
XGetWindowProperty(_glfw.x11.display,
notification.xselection.requestor,
notification.xselection.property,
0,
LONG_MAX,
True,
AnyPropertyType,
&actualType,
&actualFormat,
&itemCount,
&bytesAfter,
(unsigned char**) &data);
if (actualType == _glfw.x11.INCR)
{ {
*selectionString = strdup(data); size_t size = 1;
for (;;)
{
while (!XCheckIfEvent(_glfw.x11.display,
&dummy,
isSelPropNewValueNotify,
(XPointer) &notification))
{
waitForEvent(NULL);
}
XFree(data);
XGetWindowProperty(_glfw.x11.display,
notification.xselection.requestor,
notification.xselection.property,
0,
LONG_MAX,
True,
AnyPropertyType,
&actualType,
&actualFormat,
&itemCount,
&bytesAfter,
(unsigned char**) &data);
if (itemCount)
{
size += itemCount;
*selectionString = realloc(*selectionString, size);
(*selectionString)[size - itemCount - 1] = '\0';
strcat(*selectionString, data);
}
if (!itemCount)
break;
}
} }
else if (actualType == targets[i])
*selectionString = strdup(data);
if (data) XFree(data);
XFree(data);
XDeleteProperty(_glfw.x11.display,
event.xselection.requestor,
event.xselection.property);
if (*selectionString) if (*selectionString)
break; break;