From e209ac7a42fb082aadc2149fe6697b74c26df598 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 31 Jan 2013 00:26:37 +0100 Subject: [PATCH] Fixed X11 clipboard regressions, event waiting. --- src/x11_clipboard.c | 119 +++++++++++++++++++++++--------------------- src/x11_platform.h | 11 +--- src/x11_window.c | 51 ++++++++----------- 3 files changed, 84 insertions(+), 97 deletions(-) diff --git a/src/x11_clipboard.c b/src/x11_clipboard.c index 949105fa..b83f6695 100644 --- a/src/x11_clipboard.c +++ b/src/x11_clipboard.c @@ -39,43 +39,6 @@ ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -//======================================================================== -// Save the contents of the specified property -//======================================================================== - -GLboolean _glfwReadSelection(XSelectionEvent* request) -{ - Atom actualType; - int actualFormat; - unsigned long itemCount, bytesAfter; - char* data; - - if (request->property == None) - return GL_FALSE; - - XGetWindowProperty(_glfw.x11.display, - request->requestor, - request->property, - 0, LONG_MAX, - False, - request->target, - &actualType, - &actualFormat, - &itemCount, - &bytesAfter, - (unsigned char**) &data); - - if (actualType == None) - return GL_FALSE; - - free(_glfw.x11.selection.string); - _glfw.x11.selection.string = strdup(data); - - XFree(data); - return GL_TRUE; -} - - //======================================================================== // Set the specified property to the contents of the requested selection //======================================================================== @@ -83,10 +46,12 @@ GLboolean _glfwReadSelection(XSelectionEvent* request) Atom _glfwWriteSelection(XSelectionRequestEvent* request) { int i; - Atom property = request->property; - if (property == None) - property = _glfw.x11.selection.property; + if (request->property == None) + { + // The requestor is a legacy client (ICCCM section 2.2) + return None; + } if (request->target == _glfw.x11.TARGETS) { @@ -94,14 +59,14 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) XChangeProperty(_glfw.x11.display, request->requestor, - property, + request->property, XA_ATOM, 32, PropModeReplace, (unsigned char*) _glfw.x11.selection.formats, _GLFW_CLIPBOARD_FORMAT_COUNT); - return property; + return request->property; } for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++) @@ -112,14 +77,14 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) XChangeProperty(_glfw.x11.display, request->requestor, - property, + request->property, request->target, 8, PropModeReplace, (unsigned char*) _glfw.x11.selection.string, strlen(_glfw.x11.selection.string)); - return property; + return request->property; } } @@ -133,47 +98,87 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) { - // Store the new string in preparation for a selection request event free(_glfw.x11.selection.string); _glfw.x11.selection.string = strdup(string); - // Set the specified window as owner of the selection XSetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD, window->x11.handle, CurrentTime); + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != + window->x11.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to become owner of the clipboard selection"); + } } const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) { int i; - _glfw.x11.selection.status = _GLFW_CONVERSION_INACTIVE; + if (_glfwFindWindowByHandle(XGetSelectionOwner(_glfw.x11.display, + _glfw.x11.CLIPBOARD))) + { + // Instead of doing a large number of X round-trips just to put this + // string into a window property and then read it back, just return it + return _glfw.x11.selection.string; + } + + free(_glfw.x11.selection.string); + _glfw.x11.selection.string = NULL; for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++) { - // Request conversion to the selected format - _glfw.x11.selection.target = _glfw.x11.selection.formats[i]; + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + char* data; + XEvent event; XConvertSelection(_glfw.x11.display, _glfw.x11.CLIPBOARD, - _glfw.x11.selection.target, + _glfw.x11.selection.formats[i], _glfw.x11.selection.property, window->x11.handle, CurrentTime); - // Process the resulting SelectionNotify event - XSync(_glfw.x11.display, False); - while (_glfw.x11.selection.status == _GLFW_CONVERSION_INACTIVE) - _glfwPlatformWaitEvents(); + // XCheckTypedEvent is used instead of XIfEvent in order not to lock + // other threads out from the display during the entire wait period + while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event)) + ; - if (_glfw.x11.selection.status == _GLFW_CONVERSION_SUCCEEDED) + if (event.xselection.property == None) + continue; + + XGetWindowProperty(_glfw.x11.display, + event.xselection.requestor, + event.xselection.property, + 0, LONG_MAX, + False, + event.xselection.target, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + XDeleteProperty(_glfw.x11.display, + event.xselection.requestor, + event.xselection.property); + + if (actualType == event.xselection.target) + _glfw.x11.selection.string = strdup(data); + + XFree(data); + + if (_glfw.x11.selection.string) break; } - if (_glfw.x11.selection.status == _GLFW_CONVERSION_FAILED) + if (_glfw.x11.selection.string == NULL) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "X11: Failed to convert selection to string"); - return NULL; } return _glfw.x11.selection.string; diff --git a/src/x11_platform.h b/src/x11_platform.h index 61bff91f..f6daea12 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -63,17 +63,12 @@ #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 -// Clipboard format atom indices +// Clipboard format atom indices (in order of preference) #define _GLFW_CLIPBOARD_FORMAT_UTF8 0 #define _GLFW_CLIPBOARD_FORMAT_COMPOUND 1 #define _GLFW_CLIPBOARD_FORMAT_STRING 2 #define _GLFW_CLIPBOARD_FORMAT_COUNT 3 -// Clipboard conversion status tokens -#define _GLFW_CONVERSION_INACTIVE 0 -#define _GLFW_CONVERSION_SUCCEEDED 1 -#define _GLFW_CONVERSION_FAILED 2 - //======================================================================== // GLFW platform specific types @@ -184,9 +179,7 @@ typedef struct _GLFWlibraryX11 struct { Atom formats[_GLFW_CLIPBOARD_FORMAT_COUNT]; char* string; - Atom target; Atom property; - int status; } selection; struct { @@ -251,13 +244,13 @@ void _glfwTerminateJoysticks(void); long _glfwKeySym2Unicode(KeySym keysym); // Clipboard handling -GLboolean _glfwReadSelection(XSelectionEvent* request); Atom _glfwWriteSelection(XSelectionRequestEvent* request); // Event processing void _glfwProcessPendingEvents(void); // Window support +_GLFWwindow* _glfwFindWindowByHandle(Window handle); unsigned long _glfwGetWindowProperty(Window window, Atom property, Atom type, diff --git a/src/x11_window.c b/src/x11_window.c index 59d6902b..2b4dcf8d 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -451,7 +451,7 @@ static void leaveFullscreenMode(_GLFWwindow* window) // Return the GLFW window corresponding to the specified X11 window //======================================================================== -static _GLFWwindow* findWindow(Window handle) +_GLFWwindow* _glfwFindWindowByHandle(Window handle) { _GLFWwindow* window; @@ -475,7 +475,7 @@ static void processEvent(XEvent *event) if (event->type != GenericEvent) { - window = findWindow(event->xany.window); + window = _glfwFindWindowByHandle(event->xany.window); if (window == NULL) { // This is either an event for a destroyed GLFW window or an event @@ -710,20 +710,6 @@ static void processEvent(XEvent *event) break; } - case SelectionNotify: - { - // The clipboard selection conversion status is available - - XSelectionEvent* request = &event->xselection; - - if (_glfwReadSelection(request)) - _glfw.x11.selection.status = _GLFW_CONVERSION_SUCCEEDED; - else - _glfw.x11.selection.status = _GLFW_CONVERSION_FAILED; - - break; - } - case SelectionRequest: { // The contents of the clipboard selection was requested @@ -993,11 +979,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) void _glfwPlatformPollEvents(void) { - XEvent event; - - while (XCheckMaskEvent(_glfw.x11.display, ~0, &event) || - XCheckTypedEvent(_glfw.x11.display, ClientMessage, &event)) + int count = XPending(_glfw.x11.display); + while (count--) { + XEvent event; + XNextEvent(_glfw.x11.display, &event); processEvent(&event); } @@ -1026,21 +1012,24 @@ void _glfwPlatformPollEvents(void) void _glfwPlatformWaitEvents(void) { - int fd; - fd_set fds; + if (!XPending(_glfw.x11.display)) + { + int fd; + fd_set fds; - fd = ConnectionNumber(_glfw.x11.display); + fd = ConnectionNumber(_glfw.x11.display); - FD_ZERO(&fds); - FD_SET(fd, &fds); + FD_ZERO(&fds); + FD_SET(fd, &fds); - XFlush(_glfw.x11.display); + // select(1) is used instead of an X function like XNextEvent, as the + // wait inside those are guarded by the mutex protecting the display + // struct, locking out other threads from using X (including GLX) + if (select(fd + 1, &fds, NULL, NULL, NULL) < 0) + return; + } - // select(1) is used instead of an X function like XNextEvent, as the - // wait inside those are guarded by the mutex protecting the display - // struct, locking out other threads from using X (including GLX) - if (select(fd + 1, &fds, NULL, NULL, NULL) > 0) - _glfwPlatformPollEvents(); + _glfwPlatformPollEvents(); } void _glfwPlatformSetCursorPos(_GLFWwindow* window, int x, int y)