diff --git a/README.md b/README.md index bc28c698..93c5c2c8 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/src/x11_init.c b/src/x11_init.c index c4e84474..baf76fa4 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -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 diff --git a/src/x11_platform.h b/src/x11_platform.h index 48d15bc9..39eaec05 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -265,6 +265,7 @@ typedef struct _GLFWlibraryX11 // Selection (clipboard) atoms Atom TARGETS; Atom MULTIPLE; + Atom INCR; Atom CLIPBOARD; Atom PRIMARY; Atom CLIPBOARD_MANAGER; diff --git a/src/x11_window.c b/src/x11_window.c index aee89d40..592a8eb7 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -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)) + ¬ification)) { 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) ¬ification); + + 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) ¬ification)) + { + 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;