diff --git a/src/x11_clipboard.c b/src/x11_clipboard.c index cd0b5976..1b9e1fa3 100644 --- a/src/x11_clipboard.c +++ b/src/x11_clipboard.c @@ -27,8 +27,6 @@ // //======================================================================== -// TODO: Incremental support? Overkill perhaps. - #include "internal.h" #include @@ -42,46 +40,90 @@ ////////////////////////////////////////////////////////////////////////// //======================================================================== -// X11 selection request event +// Save the contents of the specified property //======================================================================== -Atom _glfwSelectionRequest(XSelectionRequestEvent* request) +GLboolean _glfwReadSelection(XSelectionEvent* request) { - Atom* formats = _glfwLibrary.X11.selection.formats; - char* target = _glfwLibrary.X11.selection.string; + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + char* data; - if (request->target == XA_STRING) + if (request->property == None) + return GL_FALSE; + + XGetWindowProperty(_glfwLibrary.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(_glfwLibrary.X11.selection.string); + _glfwLibrary.X11.selection.string = strdup(data); + + XFree(data); + return GL_TRUE; +} + + +//======================================================================== +// Set the specified property to the contents of the requested selection +//======================================================================== + +Atom _glfwWriteSelection(XSelectionRequestEvent* request) +{ + int i; + Atom property = request->property; + + if (property == None) + property = _glfwLibrary.X11.selection.property; + + if (request->target == _glfwLibrary.X11.selection.targets) { - // TODO: ISO Latin-1 specific characters don't get converted - // (yet). For cleanliness, would we need something like iconv? + // The list of supported targets was requested + XChangeProperty(_glfwLibrary.X11.display, request->requestor, - request->target, - request->target, - 8, + property, + XA_ATOM, + 32, PropModeReplace, - (unsigned char*) target, - 8); - } - else if (request->target == formats[_GLFW_CLIPBOARD_FORMAT_COMPOUND] || - request->target == formats[_GLFW_CLIPBOARD_FORMAT_UTF8]) - { - XChangeProperty(_glfwLibrary.X11.display, - request->requestor, - request->target, - request->target, - 8, - PropModeReplace, - (unsigned char*) target, - _glfwLibrary.X11.selection.stringLength); - } - else - { - // TODO: Should we set an error? Probably not. - return None; + (unsigned char*) _glfwLibrary.X11.selection.formats, + _GLFW_CLIPBOARD_FORMAT_COUNT); + + return property; } - return request->target; + for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++) + { + if (request->target == _glfwLibrary.X11.selection.formats[i]) + { + // The requested format is one we support + + XChangeProperty(_glfwLibrary.X11.display, + request->requestor, + property, + request->target, + 8, + PropModeReplace, + (unsigned char*) _glfwLibrary.X11.selection.string, + strlen(_glfwLibrary.X11.selection.string)); + + return property; + } + } + + return None; } @@ -95,21 +137,14 @@ Atom _glfwSelectionRequest(XSelectionRequestEvent* request) void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) { - size_t size = strlen(string) + 1; - - // Store the new string in preparation for a request event + // Store the new string in preparation for a selection request event free(_glfwLibrary.X11.selection.string); - _glfwLibrary.X11.selection.string = malloc(size); - _glfwLibrary.X11.selection.stringLength = size; - memcpy(_glfwLibrary.X11.selection.string, string, size); + _glfwLibrary.X11.selection.string = strdup(string); - // Set the selection owner to our active window - XSetSelectionOwner(_glfwLibrary.X11.display, XA_PRIMARY, - window->X11.handle, CurrentTime); + // Set the specified window as owner of the selection XSetSelectionOwner(_glfwLibrary.X11.display, _glfwLibrary.X11.selection.atom, window->X11.handle, CurrentTime); - XFlush(_glfwLibrary.X11.display); } @@ -117,103 +152,50 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) // Return the current clipboard contents //======================================================================== -size_t _glfwPlatformGetClipboardString(_GLFWwindow* window, char* data, size_t size) +size_t _glfwPlatformGetClipboardString(_GLFWwindow* window, char* string, size_t size) { - size_t len, rembytes, dummy; - unsigned char* d; - int i, fmt; - Atom type; + int i; + size_t sourceSize, targetSize; + + _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_INACTIVE; for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++) { - // Specify the format we would like. - _glfwLibrary.X11.selection.request = + // Request conversion to the selected format + _glfwLibrary.X11.selection.target = _glfwLibrary.X11.selection.formats[i]; - // Convert the selection into a format we would like. XConvertSelection(_glfwLibrary.X11.display, _glfwLibrary.X11.selection.atom, - _glfwLibrary.X11.selection.request, - None, window->X11.handle, CurrentTime); + _glfwLibrary.X11.selection.target, + _glfwLibrary.X11.selection.property, + window->X11.handle, CurrentTime); // Process the resulting SelectionNotify event XSync(_glfwLibrary.X11.display, False); - _glfwProcessPendingEvents(); + while (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_INACTIVE) + _glfwPlatformWaitEvents(); - // Successful? - if (_glfwLibrary.X11.selection.converted == 1) + if (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_SUCCEEDED) break; } - // Successful? - if (_glfwLibrary.X11.selection.converted == 1) - _glfwLibrary.X11.selection.converted = 0; - - // Unsuccessful conversion, bail with no clipboard data - if (_glfwLibrary.X11.selection.converted) + if (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_FAILED) { _glfwSetError(GLFW_FORMAT_UNAVAILABLE, "X11/GLX: Failed to convert selection to string"); return 0; } - // Reset for the next selection - _glfwLibrary.X11.selection.converted = 0; + sourceSize = strlen(_glfwLibrary.X11.selection.string) + 1; - // Check the length of data to receive (rembytes) - XGetWindowProperty(_glfwLibrary.X11.display, - window->X11.handle, - _glfwLibrary.X11.selection.request, - 0, 0, - 0, - AnyPropertyType, - &type, - &fmt, - &len, &rembytes, - &d); + targetSize = sourceSize; + if (targetSize > size) + targetSize = size; - // The number of bytes remaining (which is all of them) - if (rembytes > 0) - { - int result = XGetWindowProperty(_glfwLibrary.X11.display, - window->X11.handle, - _glfwLibrary.X11.selection.request, - 0, rembytes, - 0, - AnyPropertyType, - &type, - &fmt, - &len, &dummy, - &d); - if (result == Success) - { - size_t s; + memcpy(string, _glfwLibrary.X11.selection.string, targetSize); + string[targetSize - 1] = '\0'; - if (rembytes < size - 1) - s = rembytes; - else - s = size - 1; - - // Copy the data out. - memcpy(data, d, s); - - // Null-terminate strings. - ((char*) data)[s] = '\0'; - - // Free the data allocated using X11. - XFree(d); - - // Return the actual number of bytes. - return rembytes; - } - else - { - // Free the data allocated using X11. - XFree(d); - return 0; - } - } - - return 0; + return sourceSize; } diff --git a/src/x11_init.c b/src/x11_init.c index 535ede30..3af08cf4 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -597,11 +597,15 @@ static GLboolean initDisplay(void) // the keyboard mapping. updateKeyCodeLUT(); + // Find or create selection property atom + _glfwLibrary.X11.selection.property = + XInternAtom(_glfwLibrary.X11.display, "GLFW_SELECTION", False); + // Find or create clipboard atom _glfwLibrary.X11.selection.atom = XInternAtom(_glfwLibrary.X11.display, "CLIPBOARD", False); - // Find or create selection atoms + // Find or create selection target atoms _glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_UTF8] = XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False); _glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_COMPOUND] = @@ -609,6 +613,10 @@ static GLboolean initDisplay(void) _glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_STRING] = XA_STRING; + _glfwLibrary.X11.selection.targets = XInternAtom(_glfwLibrary.X11.display, + "TARGETS", + False); + return GL_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 8de54ead..2de3367e 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -91,6 +91,12 @@ #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 //======================================================================== @@ -235,10 +241,11 @@ typedef struct _GLFWlibraryX11 struct { Atom atom; Atom formats[_GLFW_CLIPBOARD_FORMAT_COUNT]; - size_t stringLength; char* string; - Atom request; - int converted; + Atom target; + Atom targets; + Atom property; + int status; } selection; #if defined(_GLFW_DLOPEN_LIBGL) @@ -281,7 +288,8 @@ void _glfwTerminateJoysticks(void); long _glfwKeySym2Unicode(KeySym keysym); // Clipboard handling -Atom _glfwSelectionRequest(XSelectionRequestEvent *request); +GLboolean _glfwReadSelection(XSelectionEvent* request); +Atom _glfwWriteSelection(XSelectionRequestEvent* request); // Event processing void _glfwProcessPendingEvents(void); diff --git a/src/x11_window.c b/src/x11_window.c index 854480c5..a84e209a 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1241,28 +1241,39 @@ static void processSingleEvent(void) break; } + case SelectionClear: + { + // The ownership of the selection was lost + + free(_glfwLibrary.X11.selection.string); + _glfwLibrary.X11.selection.string = NULL; + break; + } + case SelectionNotify: { - // Selection notification triggered by the XConvertSelection + // The selection conversion status is available - // Check if the notification property matches the request - if (event.xselection.property != _glfwLibrary.X11.selection.request) - _glfwLibrary.X11.selection.converted = 2; - else // It was successful - _glfwLibrary.X11.selection.converted = 1; + XSelectionEvent* request = &event.xselection; + + if (_glfwReadSelection(request)) + _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED; + else + _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_FAILED; break; } case SelectionRequest: { - // Selection request triggered by someone wanting data from the - // X11 clipboard - XSelectionRequestEvent *request = &event.xselectionrequest; + // The contents of the selection was requested + + XSelectionRequestEvent* request = &event.xselectionrequest; - // Construct the response XEvent response; - response.xselection.property = _glfwSelectionRequest(request); + memset(&response, 0, sizeof(response)); + + response.xselection.property = _glfwWriteSelection(request); response.xselection.type = SelectionNotify; response.xselection.display = request->display; response.xselection.requestor = request->requestor; @@ -1270,9 +1281,9 @@ static void processSingleEvent(void) response.xselection.target = request->target; response.xselection.time = request->time; - // Send off the event - XSendEvent(_glfwLibrary.X11.display, request->requestor, 0, 0, &response); - + XSendEvent(_glfwLibrary.X11.display, + request->requestor, + False, 0, &response); break; }