diff --git a/src/x11_clipboard.c b/src/x11_clipboard.c index 6c05abc6..1e568222 100644 --- a/src/x11_clipboard.c +++ b/src/x11_clipboard.c @@ -35,6 +35,21 @@ #include +// Returns whether the event is a selection event +// +static Bool isSelectionMessage(Display* display, XEvent* event, XPointer pointer) +{ + if (event->type == SelectionRequest || + event->type == SelectionNotify || + event->type == SelectionClear) + { + return True; + } + + return False; +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// @@ -86,7 +101,7 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) count = _glfwGetWindowProperty(request->requestor, request->property, - XA_ATOM, + _glfw.x11.ATOM_PAIR, (unsigned char**) &targets); for (i = 0; i < count; i += 2) @@ -117,7 +132,7 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) XChangeProperty(_glfw.x11.display, request->requestor, request->property, - request->target, + _glfw.x11.ATOM_PAIR, 32, PropModeReplace, (unsigned char*) targets, @@ -128,6 +143,23 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) return request->property; } + if (request->target == _glfw.x11.SAVE_TARGETS) + { + // Conversion by clients to SAVE_TARGETS should be treated like + // a side-effect target without side effects + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + XInternAtom(_glfw.x11.display, "NULL", False), + 32, + PropModeReplace, + NULL, + 0); + + return request->property; + } + for (i = 0; i < formatCount; i++) { if (request->target == formats[i]) @@ -150,6 +182,79 @@ Atom _glfwWriteSelection(XSelectionRequestEvent* request) return None; } +// Save clipboard data to clipboard manager +// +void _glfwPushSelectionToManager(_GLFWwindow* window) +{ + XEvent request; + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != + window->x11.handle) + { + // This window does not own the clipboard selection + return; + } + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD_MANAGER) == + None) + { + // There is no running clipboard manager + return; + } + + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD_MANAGER, + _glfw.x11.SAVE_TARGETS, + None, + window->x11.handle, + CurrentTime); + + for (;;) + { + if (!XCheckIfEvent(_glfw.x11.display, &request, isSelectionMessage, NULL)) + continue; + + switch (request.type) + { + case SelectionRequest: + { + XEvent response; + memset(&response, 0, sizeof(response)); + + response.xselection.property = _glfwWriteSelection(&request.xselectionrequest); + response.xselection.type = SelectionNotify; + response.xselection.display = request.xselectionrequest.display; + response.xselection.requestor = request.xselectionrequest.requestor; + response.xselection.selection = request.xselectionrequest.selection; + response.xselection.target = request.xselectionrequest.target; + response.xselection.time = request.xselectionrequest.time; + + XSendEvent(_glfw.x11.display, + request.xselectionrequest.requestor, + False, 0, &response); + + break; + } + + case SelectionClear: + { + free(_glfw.x11.selection.string); + _glfw.x11.selection.string = NULL; + break; + } + + case SelectionNotify: + { + if (request.xselection.target == _glfw.x11.SAVE_TARGETS) + return; + + break; + } + } + } +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// diff --git a/src/x11_init.c b/src/x11_init.c index 601591a0..1adb08d7 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -537,6 +537,7 @@ static GLboolean initDisplay(void) XInternAtom(_glfw.x11.display, "UTF8_STRING", False); _glfw.x11.COMPOUND_STRING = XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); + _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); // Find or create selection property atom _glfw.x11.GLFW_SELECTION = @@ -547,6 +548,12 @@ static GLboolean initDisplay(void) _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); + // Find or create clipboard manager atoms + _glfw.x11.CLIPBOARD_MANAGER = + XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); + _glfw.x11.SAVE_TARGETS = + XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); + return GL_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 00f6afcf..d97f9b0c 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -119,8 +119,11 @@ typedef struct _GLFWlibraryX11 Atom TARGETS; Atom MULTIPLE; Atom CLIPBOARD; + Atom CLIPBOARD_MANAGER; + Atom SAVE_TARGETS; Atom UTF8_STRING; Atom COMPOUND_STRING; + Atom ATOM_PAIR; Atom GLFW_SELECTION; // True if window manager supports EWMH @@ -235,6 +238,7 @@ long _glfwKeySym2Unicode(KeySym keysym); // Clipboard handling Atom _glfwWriteSelection(XSelectionRequestEvent* request); +void _glfwPushSelectionToManager(_GLFWwindow* window); // Window support _GLFWwindow* _glfwFindWindowByHandle(Window handle); diff --git a/src/x11_window.c b/src/x11_window.c index 1b834fd2..c68becea 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -904,6 +904,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->x11.handle) { + _glfwPushSelectionToManager(window); + XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); XUnmapWindow(_glfw.x11.display, window->x11.handle); XDestroyWindow(_glfw.x11.display, window->x11.handle);