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: Gamma ramp setting via RandR did not validate ramp size
- [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] Bugfix: Event processing did not detect joystick disconnection (#932)
- [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.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", 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);
// Clipboard manager atoms

View File

@ -265,6 +265,7 @@ typedef struct _GLFWlibraryX11
// Selection (clipboard) atoms
Atom TARGETS;
Atom MULTIPLE;
Atom INCR;
Atom CLIPBOARD;
Atom PRIMARY;
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;
}
// 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
//
static int translateCursorShape(int shape)
@ -841,10 +852,10 @@ static const char* getSelectionString(Atom selection)
{
size_t i;
char** selectionString = NULL;
const Atom formats[] = { _glfw.x11.UTF8_STRING,
const Atom targets[] = { _glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_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)
selectionString = &_glfw.x11.primarySelectionString;
@ -862,14 +873,17 @@ static const char* getSelectionString(Atom selection)
free(*selectionString);
*selectionString = NULL;
for (i = 0; i < formatCount; i++)
for (i = 0; i < targetCount; i++)
{
char* data;
XEvent event;
Atom actualType;
int actualFormat;
unsigned long itemCount, bytesAfter;
XEvent notification, dummy;
XConvertSelection(_glfw.x11.display,
selection,
formats[i],
targets[i],
_glfw.x11.GLFW_SELECTION,
_glfw.x11.helperWindowHandle,
CurrentTime);
@ -877,28 +891,76 @@ static const char* getSelectionString(Atom selection)
while (!XCheckTypedWindowEvent(_glfw.x11.display,
_glfw.x11.helperWindowHandle,
SelectionNotify,
&event))
&notification))
{
waitForEvent(NULL);
}
if (event.xselection.property == None)
if (notification.xselection.property == None)
continue;
if (_glfwGetWindowPropertyX11(event.xselection.requestor,
event.xselection.property,
event.xselection.target,
(unsigned char**) &data))
XCheckIfEvent(_glfw.x11.display,
&dummy,
isSelPropNewValueNotify,
(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);
XDeleteProperty(_glfw.x11.display,
event.xselection.requestor,
event.xselection.property);
XFree(data);
if (*selectionString)
break;