diff --git a/include/GL/glfw3.h b/include/GL/glfw3.h index e3687b5c..21ed049a 100644 --- a/include/GL/glfw3.h +++ b/include/GL/glfw3.h @@ -465,6 +465,7 @@ extern "C" { #define GLFW_VERSION_UNAVAILABLE 0x00070007 #define GLFW_PLATFORM_ERROR 0x00070008 #define GLFW_WINDOW_NOT_ACTIVE 0x00070009 +#define GLFW_CLIPBOARD_FORMAT_UNAVAILABLE 0x00070010 /* Gamma ramps */ #define GLFW_GAMMA_RAMP_SIZE 256 diff --git a/src/clipboard.c b/src/clipboard.c index 5e38904f..79cf680c 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -48,6 +48,10 @@ GLFWAPI void glfwSetClipboardData(void *data, size_t size, int format) _glfwSetError(GLFW_NOT_INITIALIZED, NULL); return; } + + if (format == GLFW_CLIPBOARD_FORMAT_NONE) + return; + _glfwPlatformSetClipboardData(data, size, format); } @@ -61,8 +65,11 @@ GLFWAPI size_t glfwGetClipboardData(void *data, size_t size, int format) if (!_glfwInitialized) { _glfwSetError(GLFW_NOT_INITIALIZED, NULL); - return 0; + return 0; } + if (format == GLFW_CLIPBOARD_FORMAT_NONE) + return 0; + return _glfwPlatformGetClipboardData(data, size, format); } diff --git a/src/x11_clipboard.c b/src/x11_clipboard.c index 164bad49..467a0d52 100644 --- a/src/x11_clipboard.c +++ b/src/x11_clipboard.c @@ -29,28 +29,131 @@ #include "internal.h" +#include #include #include - +#include ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// +//======================================================================== +// Get the corresponding X11 format for a given GLFW format. +//======================================================================== + +static Atom *getInternalFormat(int fmt) +{ + // Get the necessary atoms + + switch (fmt) + { + case GLFW_CLIPBOARD_FORMAT_STRING: + return _glfwLibrary.X11.selection.stringatoms; + default: + return 0; + } +} + //======================================================================== // Set the clipboard contents //======================================================================== void _glfwPlatformSetClipboardData(void *data, size_t size, int format) { + } //======================================================================== // Return the current clipboard contents +// TODO: Incremental support? Overkill perhaps. //======================================================================== size_t _glfwPlatformGetClipboardData(void *data, size_t size, int format) { - return 0; + size_t len, rembytes, dummy; + unsigned char *d; + int fmt; + Window window; + Atom *xfmt, type; + + // Try different formats that relate to the GLFW format with preference + // for better formats first + for (xfmt = getInternalFormat(format); *xfmt; xfmt++) + { + // Specify the format we would like. + _glfwLibrary.X11.selection.request = *xfmt; + + // Convert the selection into a format we would like. + window = _glfwLibrary.activeWindow->X11.handle; + XConvertSelection(_glfwLibrary.X11.display, XA_PRIMARY, + *xfmt, None, window, + CurrentTime); + XFlush(_glfwLibrary.X11.display); + + // Process pending events until we get a SelectionNotify. + while (!_glfwLibrary.X11.selection.converted) + _glfwPlatformWaitEvents(); + + // If there is no owner to the selection/wrong request, bail out. + if (_glfwLibrary.X11.selection.converted == 2) + { + _glfwLibrary.X11.selection.converted = 0; + _glfwSetError(GLFW_CLIPBOARD_FORMAT_UNAVAILABLE, + "X11/GLX: Unavailable clipboard format"); + return 0; + } + else // Right format, stop checking + { + _glfwLibrary.X11.selection.converted = 0; + break; + } + } + + // Reset for the next selection + _glfwLibrary.X11.selection.converted = 0; + + // Check the length of data to receive + XGetWindowProperty(_glfwLibrary.X11.display, + window, + *xfmt, + 0, 0, + 0, + AnyPropertyType, + &type, + &fmt, + &len, &rembytes, + &d); + + // The number of bytes remaining (which is all of them) + if (rembytes > 0) + { + int result = XGetWindowProperty(_glfwLibrary.X11.display, window, + *xfmt, 0, rembytes, 0, + AnyPropertyType, &type, &fmt, + &len, &dummy, &d); + if (result == Success) + { + size_t s = size - 1 > rembytes ? rembytes : size - 1; + // Copy the data out. + memcpy(data, d, s); + // Null-terminate strings. + if (format == GLFW_CLIPBOARD_FORMAT_STRING) + { + ((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; } diff --git a/src/x11_init.c b/src/x11_init.c index 9af7783e..a824528b 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -446,6 +446,14 @@ static GLboolean initDisplay(void) // the keyboard mapping. updateKeyCodeLUT(); + // Find or create selection atoms + _glfwLibrary.X11.selection.stringatoms[0] = + XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False); + _glfwLibrary.X11.selection.stringatoms[1] = + XInternAtom(_glfwLibrary.X11.display, "COMPOUND_STRING", False); + _glfwLibrary.X11.selection.stringatoms[2] = XA_STRING; + _glfwLibrary.X11.selection.stringatoms[3] = 0; + return GL_TRUE; } diff --git a/src/x11_platform.h b/src/x11_platform.h index b3cf642e..90b6e75c 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -89,6 +89,8 @@ #define _GLFW_PLATFORM_LIBRARY_STATE _GLFWlibraryX11 X11 #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX GLX +// Number of string atoms that will be checked +#define _GLFW_STRING_ATOMS_COUNT 4 //======================================================================== // GLFW platform specific types @@ -225,6 +227,13 @@ typedef struct _GLFWlibraryX11 uint64_t t0; } timer; + // Selection data + struct { + Atom stringatoms[_GLFW_STRING_ATOMS_COUNT]; + Atom request; + int converted; + } selection; + #if defined(_GLFW_DLOPEN_LIBGL) void* libGL; // dlopen handle for libGL.so #endif diff --git a/src/x11_window.c b/src/x11_window.c index a05b121c..ff54a11b 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1381,6 +1381,19 @@ static void processSingleEvent(void) break; } + case SelectionNotify: + { + // Selection notification triggered by the XConvertSelection + + // 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; + + break; + } + // Was the window destroyed? case DestroyNotify: return; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ef07acc..23a1ebd0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories(${GLFW_SOURCE_DIR}/include ${GLFW_SOURCE_DIR}/support ${OPENGL_INCLUDE_DIR}) +add_executable(clipboard clipboard.c) add_executable(defaults defaults.c) add_executable(events events.c) add_executable(fsaa fsaa.c getopt.c) @@ -32,8 +33,8 @@ else() endif(APPLE) set(WINDOWS_BINARIES accuracy sharing tearing windows) -set(CONSOLE_BINARIES defaults events fsaa fsfocus gamma iconify joysticks - listmodes peter reopen version) +set(CONSOLE_BINARIES clipboard defaults events fsaa fsfocus gamma iconify + joysticks listmodes peter reopen version) if(MSVC) # Tell MSVC to use main instead of WinMain for Windows subsystem executables