diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 62bd6716..6a20f897 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -38,8 +38,6 @@ extern "C" { * Doxygen documentation *************************************************************************/ -/*! @defgroup clipboard Clipboard support - */ /*! @defgroup context Context handling */ /*! @defgroup error Error handling @@ -2418,7 +2416,7 @@ GLFWAPI const char* glfwGetJoystickName(int joy); * * @sa glfwGetClipboardString * - * @ingroup clipboard + * @ingroup input */ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); @@ -2442,7 +2440,7 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * * @sa glfwSetClipboardString * - * @ingroup clipboard + * @ingroup input */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6496854e..4a390b17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,33 +8,29 @@ add_definitions(-D_GLFW_USE_CONFIG_H) set(common_HEADERS "${GLFW_BINARY_DIR}/src/glfw_config.h" internal.h "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h") -set(common_SOURCES clipboard.c context.c init.c input.c joystick.c - monitor.c time.c window.c) +set(common_SOURCES context.c init.c input.c joystick.c monitor.c time.c + window.c) if (_GLFW_COCOA) set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h iokit_joystick.h posix_tls.h) - set(glfw_SOURCES ${common_SOURCES} cocoa_clipboard.m cocoa_init.m - cocoa_monitor.m cocoa_window.m iokit_joystick.m mach_time.c - posix_tls.c) + set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_monitor.m + cocoa_window.m iokit_joystick.m mach_time.c posix_tls.c) elseif (_GLFW_WIN32) set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_tls.h winmm_joystick.h) - set(glfw_SOURCES ${common_SOURCES} win32_clipboard.c win32_init.c - win32_monitor.c win32_time.c win32_tls.c win32_window.c - winmm_joystick.c) + set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_monitor.c win32_time.c + win32_tls.c win32_window.c winmm_joystick.c) elseif (_GLFW_X11) set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h linux_joystick.h posix_time.h posix_tls.h) - set(glfw_SOURCES ${common_SOURCES} x11_clipboard.c x11_init.c x11_monitor.c - x11_window.c xkb_unicode.c linux_joystick.c posix_time.c - posix_tls.c) + set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c + xkb_unicode.c linux_joystick.c posix_time.c posix_tls.c) elseif (_GLFW_WAYLAND) set(glfw_HEADERS ${common_HEADERS} wl_platform.h linux_joystick.h posix_time.h posix_tls.h xkb_unicode.h) - set(glfw_SOURCES ${common_SOURCES} wl_clipboard.c wl_init.c wl_monitor.c - wl_window.c linux_joystick.c posix_time.c posix_tls.c - xkb_unicode.c) + set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c + linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c) endif() if (_GLFW_EGL) diff --git a/src/clipboard.c b/src/clipboard.c deleted file mode 100644 index b5426c44..00000000 --- a/src/clipboard.c +++ /dev/null @@ -1,50 +0,0 @@ -//======================================================================== -// GLFW 3.1 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2010 Camilla Berglund -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT(); - _glfwPlatformSetClipboardString(window, string); -} - -GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetClipboardString(window); -} - diff --git a/src/cocoa_clipboard.m b/src/cocoa_clipboard.m deleted file mode 100644 index 6af5df61..00000000 --- a/src/cocoa_clipboard.m +++ /dev/null @@ -1,70 +0,0 @@ -//======================================================================== -// GLFW 3.1 OS X - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2010 Camilla Berglund -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) -{ - NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; - - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:types owner:nil]; - [pasteboard setString:[NSString stringWithUTF8String:string] - forType:NSStringPboardType]; -} - -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) -{ - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - - if (![[pasteboard types] containsObject:NSStringPboardType]) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, NULL); - return NULL; - } - - NSString* object = [pasteboard stringForType:NSStringPboardType]; - if (!object) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve object from pasteboard"); - return NULL; - } - - free(_glfw.ns.clipboardString); - _glfw.ns.clipboardString = strdup([object UTF8String]); - - return _glfw.ns.clipboardString; -} - diff --git a/src/cocoa_window.m b/src/cocoa_window.m index ad48dfe0..03ac4b9e 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -26,6 +26,8 @@ #include "internal.h" +#include + // Needed for _NSGetProgname #include @@ -1295,6 +1297,40 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:types owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:string] + forType:NSStringPboardType]; +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + + if (![[pasteboard types] containsObject:NSStringPboardType]) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, NULL); + return NULL; + } + + NSString* object = [pasteboard stringForType:NSStringPboardType]; + if (!object) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve object from pasteboard"); + return NULL; + } + + free(_glfw.ns.clipboardString); + _glfw.ns.clipboardString = strdup([object UTF8String]); + + return _glfw.ns.clipboardString; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/src/input.c b/src/input.c index fd061f85..7dce9238 100644 --- a/src/input.c +++ b/src/input.c @@ -497,3 +497,17 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) return cbfun; } +GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetClipboardString(window, string); +} + +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetClipboardString(window); +} + diff --git a/src/win32_clipboard.c b/src/win32_clipboard.c deleted file mode 100644 index 1b7f9723..00000000 --- a/src/win32_clipboard.c +++ /dev/null @@ -1,127 +0,0 @@ -//======================================================================== -// GLFW 3.1 Win32 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2010 Camilla Berglund -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) -{ - WCHAR* wideString; - HANDLE stringHandle; - size_t wideSize; - - wideString = _glfwCreateWideStringFromUTF8(string); - if (!wideString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert clipboard string to " - "wide string"); - return; - } - - wideSize = (wcslen(wideString) + 1) * sizeof(WCHAR); - - stringHandle = GlobalAlloc(GMEM_MOVEABLE, wideSize); - if (!stringHandle) - { - free(wideString); - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate global handle for clipboard"); - return; - } - - memcpy(GlobalLock(stringHandle), wideString, wideSize); - GlobalUnlock(stringHandle); - - if (!OpenClipboard(window->win32.handle)) - { - GlobalFree(stringHandle); - free(wideString); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); - return; - } - - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, stringHandle); - CloseClipboard(); - - free(wideString); -} - -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) -{ - HANDLE stringHandle; - - if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, NULL); - return NULL; - } - - if (!OpenClipboard(window->win32.handle)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); - return NULL; - } - - stringHandle = GetClipboardData(CF_UNICODETEXT); - if (!stringHandle) - { - CloseClipboard(); - - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to retrieve clipboard data"); - return NULL; - } - - free(_glfw.win32.clipboardString); - _glfw.win32.clipboardString = - _glfwCreateUTF8FromWideString(GlobalLock(stringHandle)); - - GlobalUnlock(stringHandle); - CloseClipboard(); - - if (!_glfw.win32.clipboardString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert wide string to UTF-8"); - return NULL; - } - - return _glfw.win32.clipboardString; -} - diff --git a/src/win32_window.c b/src/win32_window.c index a75ede97..7aa7d8ae 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -1372,6 +1373,95 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + WCHAR* wideString; + HANDLE stringHandle; + size_t wideSize; + + wideString = _glfwCreateWideStringFromUTF8(string); + if (!wideString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert clipboard string to " + "wide string"); + return; + } + + wideSize = (wcslen(wideString) + 1) * sizeof(WCHAR); + + stringHandle = GlobalAlloc(GMEM_MOVEABLE, wideSize); + if (!stringHandle) + { + free(wideString); + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate global handle for clipboard"); + return; + } + + memcpy(GlobalLock(stringHandle), wideString, wideSize); + GlobalUnlock(stringHandle); + + if (!OpenClipboard(window->win32.handle)) + { + GlobalFree(stringHandle); + free(wideString); + + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); + return; + } + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, stringHandle); + CloseClipboard(); + + free(wideString); +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + HANDLE stringHandle; + + if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, NULL); + return NULL; + } + + if (!OpenClipboard(window->win32.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); + return NULL; + } + + stringHandle = GetClipboardData(CF_UNICODETEXT); + if (!stringHandle) + { + CloseClipboard(); + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to retrieve clipboard data"); + return NULL; + } + + free(_glfw.win32.clipboardString); + _glfw.win32.clipboardString = + _glfwCreateUTF8FromWideString(GlobalLock(stringHandle)); + + GlobalUnlock(stringHandle); + CloseClipboard(); + + if (!_glfw.win32.clipboardString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert wide string to UTF-8"); + return NULL; + } + + return _glfw.win32.clipboardString; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// diff --git a/src/wl_clipboard.c b/src/wl_clipboard.c deleted file mode 100644 index 8e25cad4..00000000 --- a/src/wl_clipboard.c +++ /dev/null @@ -1,48 +0,0 @@ -//======================================================================== -// GLFW 3.1 Wayland - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014 Jonas Ã…dahl -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) -{ - // TODO - fprintf(stderr, "_glfwPlatformSetClipboardString not implemented yet\n"); -} - -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) -{ - // TODO - fprintf(stderr, "_glfwPlatformGetClipboardString not implemented yet\n"); - return NULL; -} - diff --git a/src/wl_window.c b/src/wl_window.c index 398cabe0..e02b0cdf 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -312,3 +312,16 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) fprintf(stderr, "_glfwPlatformSetCursor not implemented yet\n"); } +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + // TODO + fprintf(stderr, "_glfwPlatformSetClipboardString not implemented yet\n"); +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + // TODO + fprintf(stderr, "_glfwPlatformGetClipboardString not implemented yet\n"); + return NULL; +} + diff --git a/src/x11_clipboard.c b/src/x11_clipboard.c deleted file mode 100644 index 3428050d..00000000 --- a/src/x11_clipboard.c +++ /dev/null @@ -1,335 +0,0 @@ -//======================================================================== -// GLFW 3.1 X11 - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2010 Camilla Berglund -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include -#include -#include - - -// Returns whether the event is a selection event -// -static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) -{ - return event->type == SelectionRequest || - event->type == SelectionNotify || - event->type == SelectionClear; -} - -// Set the specified property to the selection converted to the requested target -// -static Atom writeTargetToProperty(const XSelectionRequestEvent* request) -{ - int i; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; - const int formatCount = sizeof(formats) / sizeof(formats[0]); - - if (request->property == None) - { - // The requestor is a legacy client (ICCCM section 2.2) - // We don't support legacy clients, so fail here - return None; - } - - if (request->target == _glfw.x11.TARGETS) - { - // The list of supported targets was requested - - const Atom targets[] = { _glfw.x11.TARGETS, - _glfw.x11.MULTIPLE, - _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - XA_ATOM, - 32, - PropModeReplace, - (unsigned char*) targets, - sizeof(targets) / sizeof(targets[0])); - - return request->property; - } - - if (request->target == _glfw.x11.MULTIPLE) - { - // Multiple conversions were requested - - Atom* targets; - unsigned long i, count; - - count = _glfwGetWindowProperty(request->requestor, - request->property, - _glfw.x11.ATOM_PAIR, - (unsigned char**) &targets); - - for (i = 0; i < count; i += 2) - { - int j; - - for (j = 0; j < formatCount; j++) - { - if (targets[i] == formats[j]) - break; - } - - if (j < formatCount) - { - XChangeProperty(_glfw.x11.display, - request->requestor, - targets[i + 1], - targets[i], - 8, - PropModeReplace, - (unsigned char*) _glfw.x11.clipboardString, - strlen(_glfw.x11.clipboardString)); - } - else - targets[i + 1] = None; - } - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - _glfw.x11.ATOM_PAIR, - 32, - PropModeReplace, - (unsigned char*) targets, - count); - - XFree(targets); - - return request->property; - } - - if (request->target == _glfw.x11.SAVE_TARGETS) - { - // The request is a check whether we support SAVE_TARGETS - // It should be handled as a no-op side effect target - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - _glfw.x11._NULL, - 32, - PropModeReplace, - NULL, - 0); - - return request->property; - } - - // Conversion to a data target was requested - - for (i = 0; i < formatCount; i++) - { - if (request->target == formats[i]) - { - // The requested target is one we support - - XChangeProperty(_glfw.x11.display, - request->requestor, - request->property, - request->target, - 8, - PropModeReplace, - (unsigned char*) _glfw.x11.clipboardString, - strlen(_glfw.x11.clipboardString)); - - return request->property; - } - } - - // The requested target is not supported - - return None; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwHandleSelectionClear(XEvent* event) -{ - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; -} - -void _glfwHandleSelectionRequest(XEvent* event) -{ - const XSelectionRequestEvent* request = &event->xselectionrequest; - - XEvent response; - memset(&response, 0, sizeof(response)); - - response.xselection.property = writeTargetToProperty(request); - response.xselection.type = SelectionNotify; - response.xselection.display = request->display; - response.xselection.requestor = request->requestor; - response.xselection.selection = request->selection; - response.xselection.target = request->target; - response.xselection.time = request->time; - - XSendEvent(_glfw.x11.display, request->requestor, False, 0, &response); -} - -void _glfwPushSelectionToManager(_GLFWwindow* window) -{ - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD_MANAGER, - _glfw.x11.SAVE_TARGETS, - None, - window->x11.handle, - CurrentTime); - - for (;;) - { - XEvent event; - - if (!XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) - continue; - - switch (event.type) - { - case SelectionRequest: - _glfwHandleSelectionRequest(&event); - break; - - case SelectionClear: - _glfwHandleSelectionClear(&event); - break; - - case SelectionNotify: - { - if (event.xselection.target == _glfw.x11.SAVE_TARGETS) - { - // This means one of two things; either the selection was - // not owned, which means there is no clipboard manager, or - // the transfer to the clipboard manager has completed - // In either case, it means we are done here - return; - } - - break; - } - } - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) -{ - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = strdup(string); - - 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) -{ - size_t i; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; - const size_t formatCount = sizeof(formats) / sizeof(formats[0]); - - 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.clipboardString; - } - - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; - - for (i = 0; i < formatCount; i++) - { - char* data; - XEvent event; - - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD, - formats[i], - _glfw.x11.GLFW_SELECTION, - window->x11.handle, CurrentTime); - - // 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 (event.xselection.property == None) - continue; - - if (_glfwGetWindowProperty(event.xselection.requestor, - event.xselection.property, - event.xselection.target, - (unsigned char**) &data)) - { - _glfw.x11.clipboardString = strdup(data); - } - - XFree(data); - - XDeleteProperty(_glfw.x11.display, - event.xselection.requestor, - event.xselection.property); - - if (_glfw.x11.clipboardString) - break; - } - - if (_glfw.x11.clipboardString == NULL) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "X11: Failed to convert selection to string"); - } - - return _glfw.x11.clipboardString; -} - diff --git a/src/x11_platform.h b/src/x11_platform.h index 1a290f04..8cfa4917 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -228,13 +228,8 @@ typedef struct _GLFWcursorX11 GLboolean _glfwSetVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoMode(_GLFWmonitor* monitor); -void _glfwHandleSelectionClear(XEvent* event); -void _glfwHandleSelectionRequest(XEvent* event); -void _glfwPushSelectionToManager(_GLFWwindow* window); - Cursor _glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); -_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 9b9ae20b..4e18cd22 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -108,6 +108,23 @@ static int translateChar(XKeyEvent* event) return (int) _glfwKeySym2Unicode(keysym); } +// Return the GLFW window corresponding to the specified X11 window +// +static _GLFWwindow* findWindowByHandle(Window handle) +{ + _GLFWwindow* window; + + if (XFindContext(_glfw.x11.display, + handle, + _glfw.x11.context, + (XPointer*) &window) != 0) + { + return NULL; + } + + return window; +} + // Adds or removes an EWMH state to a window // static void changeWindowState(_GLFWwindow* window, Atom state, int action) @@ -459,6 +476,215 @@ static void restoreCursor(_GLFWwindow* window) XUndefineCursor(_glfw.x11.display, window->x11.handle); } +// Returns whether the event is a selection event +// +static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) +{ + return event->type == SelectionRequest || + event->type == SelectionNotify || + event->type == SelectionClear; +} + +// Set the specified property to the selection converted to the requested target +// +static Atom writeTargetToProperty(const XSelectionRequestEvent* request) +{ + int i; + const Atom formats[] = { _glfw.x11.UTF8_STRING, + _glfw.x11.COMPOUND_STRING, + XA_STRING }; + const int formatCount = sizeof(formats) / sizeof(formats[0]); + + if (request->property == None) + { + // The requestor is a legacy client (ICCCM section 2.2) + // We don't support legacy clients, so fail here + return None; + } + + if (request->target == _glfw.x11.TARGETS) + { + // The list of supported targets was requested + + const Atom targets[] = { _glfw.x11.TARGETS, + _glfw.x11.MULTIPLE, + _glfw.x11.UTF8_STRING, + _glfw.x11.COMPOUND_STRING, + XA_STRING }; + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*) targets, + sizeof(targets) / sizeof(targets[0])); + + return request->property; + } + + if (request->target == _glfw.x11.MULTIPLE) + { + // Multiple conversions were requested + + Atom* targets; + unsigned long i, count; + + count = _glfwGetWindowProperty(request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + (unsigned char**) &targets); + + for (i = 0; i < count; i += 2) + { + int j; + + for (j = 0; j < formatCount; j++) + { + if (targets[i] == formats[j]) + break; + } + + if (j < formatCount) + { + XChangeProperty(_glfw.x11.display, + request->requestor, + targets[i + 1], + targets[i], + 8, + PropModeReplace, + (unsigned char*) _glfw.x11.clipboardString, + strlen(_glfw.x11.clipboardString)); + } + else + targets[i + 1] = None; + } + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + 32, + PropModeReplace, + (unsigned char*) targets, + count); + + XFree(targets); + + return request->property; + } + + if (request->target == _glfw.x11.SAVE_TARGETS) + { + // The request is a check whether we support SAVE_TARGETS + // It should be handled as a no-op side effect target + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + _glfw.x11._NULL, + 32, + PropModeReplace, + NULL, + 0); + + return request->property; + } + + // Conversion to a data target was requested + + for (i = 0; i < formatCount; i++) + { + if (request->target == formats[i]) + { + // The requested target is one we support + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + request->target, + 8, + PropModeReplace, + (unsigned char*) _glfw.x11.clipboardString, + strlen(_glfw.x11.clipboardString)); + + return request->property; + } + } + + // The requested target is not supported + + return None; +} + +static void handleSelectionClear(XEvent* event) +{ + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = NULL; +} + +static void handleSelectionRequest(XEvent* event) +{ + const XSelectionRequestEvent* request = &event->xselectionrequest; + + XEvent response; + memset(&response, 0, sizeof(response)); + + response.xselection.property = writeTargetToProperty(request); + response.xselection.type = SelectionNotify; + response.xselection.display = request->display; + response.xselection.requestor = request->requestor; + response.xselection.selection = request->selection; + response.xselection.target = request->target; + response.xselection.time = request->time; + + XSendEvent(_glfw.x11.display, request->requestor, False, 0, &response); +} + +static void pushSelectionToManager(_GLFWwindow* window) +{ + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD_MANAGER, + _glfw.x11.SAVE_TARGETS, + None, + window->x11.handle, + CurrentTime); + + for (;;) + { + XEvent event; + + if (!XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + continue; + + switch (event.type) + { + case SelectionRequest: + handleSelectionRequest(&event); + break; + + case SelectionClear: + handleSelectionClear(&event); + break; + + case SelectionNotify: + { + if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + { + // This means one of two things; either the selection was + // not owned, which means there is no clipboard manager, or + // the transfer to the clipboard manager has completed + // In either case, it means we are done here + return; + } + + break; + } + } + } +} + // Enter fullscreen mode // static void enterFullscreenMode(_GLFWwindow* window) @@ -590,7 +816,7 @@ static void processEvent(XEvent *event) if (event->type != GenericEvent) { - window = _glfwFindWindowByHandle(event->xany.window); + window = findWindowByHandle(event->xany.window); if (window == NULL) { // This is an event for a window that has already been destroyed @@ -1000,13 +1226,13 @@ static void processEvent(XEvent *event) case SelectionClear: { - _glfwHandleSelectionClear(event); + handleSelectionClear(event); break; } case SelectionRequest: { - _glfwHandleSelectionRequest(event); + handleSelectionRequest(event); break; } @@ -1022,7 +1248,7 @@ static void processEvent(XEvent *event) { XIDeviceEvent* data = (XIDeviceEvent*) event->xcookie.data; - window = _glfwFindWindowByHandle(data->event); + window = findWindowByHandle(data->event); if (window) { if (data->event_x != window->x11.warpPosX || @@ -1080,23 +1306,6 @@ static void processEvent(XEvent *event) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -// Return the GLFW window corresponding to the specified X11 window -// -_GLFWwindow* _glfwFindWindowByHandle(Window handle) -{ - _GLFWwindow* window; - - if (XFindContext(_glfw.x11.display, - handle, - _glfw.x11.context, - (XPointer*) &window) != 0) - { - return NULL; - } - - return window; -} - // Retrieve a single window property of the specified type // Inspired by fghGetWindowProperty from freeglut // @@ -1165,7 +1374,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->x11.handle == XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD)) { - _glfwPushSelectionToManager(window); + pushSelectionToManager(window); } XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); @@ -1484,6 +1693,88 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = strdup(string); + + 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) +{ + size_t i; + const Atom formats[] = { _glfw.x11.UTF8_STRING, + _glfw.x11.COMPOUND_STRING, + XA_STRING }; + const size_t formatCount = sizeof(formats) / sizeof(formats[0]); + + if (findWindowByHandle(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.clipboardString; + } + + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = NULL; + + for (i = 0; i < formatCount; i++) + { + char* data; + XEvent event; + + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD, + formats[i], + _glfw.x11.GLFW_SELECTION, + window->x11.handle, CurrentTime); + + // 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 (event.xselection.property == None) + continue; + + if (_glfwGetWindowProperty(event.xselection.requestor, + event.xselection.property, + event.xselection.target, + (unsigned char**) &data)) + { + _glfw.x11.clipboardString = strdup(data); + } + + XFree(data); + + XDeleteProperty(_glfw.x11.display, + event.xselection.requestor, + event.xselection.property); + + if (_glfw.x11.clipboardString) + break; + } + + if (_glfw.x11.clipboardString == NULL) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "X11: Failed to convert selection to string"); + } + + return _glfw.x11.clipboardString; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW native API //////