mirror of
https://github.com/gwm17/glfw.git
synced 2024-11-26 20:28:49 -05:00
X11: Add native access to primary selection
This adds the native access functions glfwSetX11SelectionString and glfwGetX11SelectionString under GLFW_EXPOSE_NATIVE_X11. They are similar to glfwSetClipboardString and glfwGetClipboardString but operate on the PRIMARY selection. The primary selection is widely used in X11, and so seems important to support. Primary selection is mostly an X11-specific thing, hence it's exposed as an X11 native interface. Fixes #894. Closes #1056. Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
This commit is contained in:
parent
3ee7f8f695
commit
29a75ab09d
|
@ -124,6 +124,9 @@ information on what to include when reporting a bug.
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
- Added `glfwSetX11SelectionString` and `glfwGetX11SelectionString`
|
||||||
|
native functions for accessing X11 primary selection as a supplement to
|
||||||
|
the clipboard on the X11 platform (#894)
|
||||||
- Added `glfwGetError` function for querying the last error code and its
|
- Added `glfwGetError` function for querying the last error code and its
|
||||||
description (#970)
|
description (#970)
|
||||||
- Added `glfwUpdateGamepadMappings` function for importing gamepad mappings in
|
- Added `glfwUpdateGamepadMappings` function for importing gamepad mappings in
|
||||||
|
|
|
@ -5,6 +5,15 @@
|
||||||
@section news_33 Release notes for 3.3
|
@section news_33 Release notes for 3.3
|
||||||
|
|
||||||
|
|
||||||
|
@subsection news_33_native_x11_selection X11 native Primary Selection access
|
||||||
|
|
||||||
|
GLFW now supports X11 platform specific native functions for accessing
|
||||||
|
the X11 primary selection, as a supplement to the clipboard on this
|
||||||
|
platform. See @ref clipboard.
|
||||||
|
|
||||||
|
@see @ref error_handling
|
||||||
|
|
||||||
|
|
||||||
@subsection news_33_geterror Error query
|
@subsection news_33_geterror Error query
|
||||||
|
|
||||||
GLFW now supports querying the last error code for the calling thread and its
|
GLFW now supports querying the last error code for the calling thread and its
|
||||||
|
|
|
@ -289,6 +289,56 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
|
||||||
* @ingroup native
|
* @ingroup native
|
||||||
*/
|
*/
|
||||||
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
|
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
|
||||||
|
|
||||||
|
/*! @brief Sets the current primary selection to the specified string.
|
||||||
|
*
|
||||||
|
* @param[in] string A UTF-8 encoded string.
|
||||||
|
*
|
||||||
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
|
* GLFW_PLATFORM_ERROR.
|
||||||
|
*
|
||||||
|
* @pointer_lifetime The specified string is copied before this function
|
||||||
|
* returns.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref clipboard
|
||||||
|
* @sa glfwGetX11SelectionString
|
||||||
|
* @sa glfwSetClipboardString
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @ingroup native
|
||||||
|
*/
|
||||||
|
GLFWAPI void glfwSetX11SelectionString(const char* string);
|
||||||
|
|
||||||
|
/*! @brief Returns the contents of the current primary selection as a string.
|
||||||
|
*
|
||||||
|
* If the selection is empty or if its contents cannot be converted, `NULL`
|
||||||
|
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
|
||||||
|
*
|
||||||
|
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
|
||||||
|
* if an [error](@ref error_handling) occurred.
|
||||||
|
*
|
||||||
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
|
* GLFW_PLATFORM_ERROR.
|
||||||
|
*
|
||||||
|
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
|
||||||
|
* should not free it yourself. It is valid until the next call to @ref
|
||||||
|
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
|
||||||
|
* library is terminated.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref clipboard
|
||||||
|
* @sa glfwSetX11SelectionString
|
||||||
|
* @sa glfwGetClipboardString
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @ingroup native
|
||||||
|
*/
|
||||||
|
GLFWAPI const char* glfwGetX11SelectionString(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(GLFW_EXPOSE_NATIVE_GLX)
|
#if defined(GLFW_EXPOSE_NATIVE_GLX)
|
||||||
|
|
|
@ -611,6 +611,7 @@ static GLFWbool initExtensions(void)
|
||||||
// ICCCM standard clipboard atoms
|
// ICCCM standard clipboard atoms
|
||||||
_glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
|
_glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
|
||||||
_glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
|
_glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False);
|
||||||
|
_glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False);
|
||||||
_glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
|
_glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
|
||||||
|
|
||||||
// Clipboard manager atoms
|
// Clipboard manager atoms
|
||||||
|
@ -853,6 +854,7 @@ void _glfwPlatformTerminate(void)
|
||||||
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(_glfw.x11.primarySelectionString);
|
||||||
free(_glfw.x11.clipboardString);
|
free(_glfw.x11.clipboardString);
|
||||||
|
|
||||||
if (_glfw.x11.im)
|
if (_glfw.x11.im)
|
||||||
|
|
|
@ -161,6 +161,8 @@ typedef struct _GLFWlibraryX11
|
||||||
XIM im;
|
XIM im;
|
||||||
// Most recent error code received by X error handler
|
// Most recent error code received by X error handler
|
||||||
int errorCode;
|
int errorCode;
|
||||||
|
// Primary selection string (while the primary selection is owned)
|
||||||
|
char* primarySelectionString;
|
||||||
// Clipboard string (while the selection is owned)
|
// Clipboard string (while the selection is owned)
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
// Key name string
|
// Key name string
|
||||||
|
@ -214,6 +216,7 @@ typedef struct _GLFWlibraryX11
|
||||||
Atom TARGETS;
|
Atom TARGETS;
|
||||||
Atom MULTIPLE;
|
Atom MULTIPLE;
|
||||||
Atom CLIPBOARD;
|
Atom CLIPBOARD;
|
||||||
|
Atom PRIMARY;
|
||||||
Atom CLIPBOARD_MANAGER;
|
Atom CLIPBOARD_MANAGER;
|
||||||
Atom SAVE_TARGETS;
|
Atom SAVE_TARGETS;
|
||||||
Atom NULL_;
|
Atom NULL_;
|
||||||
|
|
181
src/x11_window.c
181
src/x11_window.c
|
@ -675,6 +675,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||||
_glfw.x11.COMPOUND_STRING,
|
_glfw.x11.COMPOUND_STRING,
|
||||||
XA_STRING };
|
XA_STRING };
|
||||||
const int formatCount = sizeof(formats) / sizeof(formats[0]);
|
const int formatCount = sizeof(formats) / sizeof(formats[0]);
|
||||||
|
char *selectionString = request->selection == _glfw.x11.PRIMARY ?
|
||||||
|
_glfw.x11.primarySelectionString : _glfw.x11.clipboardString;
|
||||||
|
|
||||||
if (request->property == None)
|
if (request->property == None)
|
||||||
{
|
{
|
||||||
|
@ -735,8 +737,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||||
targets[i],
|
targets[i],
|
||||||
8,
|
8,
|
||||||
PropModeReplace,
|
PropModeReplace,
|
||||||
(unsigned char*) _glfw.x11.clipboardString,
|
(unsigned char *) selectionString,
|
||||||
strlen(_glfw.x11.clipboardString));
|
strlen(selectionString));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
targets[i + 1] = None;
|
targets[i + 1] = None;
|
||||||
|
@ -787,8 +789,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||||
request->target,
|
request->target,
|
||||||
8,
|
8,
|
||||||
PropModeReplace,
|
PropModeReplace,
|
||||||
(unsigned char*) _glfw.x11.clipboardString,
|
(unsigned char *) selectionString,
|
||||||
strlen(_glfw.x11.clipboardString));
|
strlen(selectionString));
|
||||||
|
|
||||||
return request->property;
|
return request->property;
|
||||||
}
|
}
|
||||||
|
@ -800,10 +802,19 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleSelectionClear(XEvent* event)
|
static void handleSelectionClear(XEvent* event)
|
||||||
|
{
|
||||||
|
const XSelectionClearEvent* request = &event->xselectionclear;
|
||||||
|
if (request->selection == _glfw.x11.PRIMARY)
|
||||||
|
{
|
||||||
|
free(_glfw.x11.primarySelectionString);
|
||||||
|
_glfw.x11.primarySelectionString = NULL;
|
||||||
|
}
|
||||||
|
else if (request->selection == _glfw.x11.CLIPBOARD)
|
||||||
{
|
{
|
||||||
free(_glfw.x11.clipboardString);
|
free(_glfw.x11.clipboardString);
|
||||||
_glfw.x11.clipboardString = NULL;
|
_glfw.x11.clipboardString = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handleSelectionRequest(XEvent* event)
|
static void handleSelectionRequest(XEvent* event)
|
||||||
{
|
{
|
||||||
|
@ -823,6 +834,76 @@ static void handleSelectionRequest(XEvent* event)
|
||||||
XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
|
XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *getSelection(Atom selection, char **ptr)
|
||||||
|
{
|
||||||
|
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 (XGetSelectionOwner(_glfw.x11.display, selection) ==
|
||||||
|
_glfw.x11.helperWindowHandle)
|
||||||
|
{
|
||||||
|
// 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 *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(*ptr);
|
||||||
|
*ptr = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < formatCount; i++)
|
||||||
|
{
|
||||||
|
char* data;
|
||||||
|
XEvent event;
|
||||||
|
|
||||||
|
XConvertSelection(_glfw.x11.display,
|
||||||
|
selection,
|
||||||
|
formats[i],
|
||||||
|
_glfw.x11.GLFW_SELECTION,
|
||||||
|
_glfw.x11.helperWindowHandle,
|
||||||
|
CurrentTime);
|
||||||
|
|
||||||
|
while (!XCheckTypedWindowEvent(_glfw.x11.display,
|
||||||
|
_glfw.x11.helperWindowHandle,
|
||||||
|
SelectionNotify,
|
||||||
|
&event))
|
||||||
|
{
|
||||||
|
waitForEvent(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.xselection.property == None)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (_glfwGetWindowPropertyX11(event.xselection.requestor,
|
||||||
|
event.xselection.property,
|
||||||
|
event.xselection.target,
|
||||||
|
(unsigned char**) &data))
|
||||||
|
{
|
||||||
|
*ptr = strdup(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
XFree(data);
|
||||||
|
|
||||||
|
XDeleteProperty(_glfw.x11.display,
|
||||||
|
event.xselection.requestor,
|
||||||
|
event.xselection.property);
|
||||||
|
|
||||||
|
if (*ptr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*ptr == NULL)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||||
|
"X11: Failed to convert clipboard to string");
|
||||||
|
}
|
||||||
|
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Make the specified window and its video mode active on its monitor
|
// Make the specified window and its video mode active on its monitor
|
||||||
//
|
//
|
||||||
static GLFWbool acquireMonitor(_GLFWwindow* window)
|
static GLFWbool acquireMonitor(_GLFWwindow* window)
|
||||||
|
@ -2572,72 +2653,7 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
|
||||||
|
|
||||||
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
|
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
size_t i;
|
return getSelection(_glfw.x11.CLIPBOARD, &_glfw.x11.clipboardString);
|
||||||
const Atom formats[] = { _glfw.x11.UTF8_STRING,
|
|
||||||
_glfw.x11.COMPOUND_STRING,
|
|
||||||
XA_STRING };
|
|
||||||
const size_t formatCount = sizeof(formats) / sizeof(formats[0]);
|
|
||||||
|
|
||||||
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
|
|
||||||
_glfw.x11.helperWindowHandle)
|
|
||||||
{
|
|
||||||
// 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,
|
|
||||||
_glfw.x11.helperWindowHandle,
|
|
||||||
CurrentTime);
|
|
||||||
|
|
||||||
while (!XCheckTypedWindowEvent(_glfw.x11.display,
|
|
||||||
_glfw.x11.helperWindowHandle,
|
|
||||||
SelectionNotify,
|
|
||||||
&event))
|
|
||||||
{
|
|
||||||
waitForEvent(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.xselection.property == None)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (_glfwGetWindowPropertyX11(event.xselection.requestor,
|
|
||||||
event.xselection.property,
|
|
||||||
event.xselection.target,
|
|
||||||
(unsigned char**) &data))
|
|
||||||
{
|
|
||||||
_glfw.x11.clipboardString = strdup(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (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 clipboard to string");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _glfw.x11.clipboardString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||||
|
@ -2807,3 +2823,28 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
|
||||||
return window->x11.handle;
|
return window->x11.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLFWAPI void glfwSetX11SelectionString(const char* string)
|
||||||
|
{
|
||||||
|
_GLFW_REQUIRE_INIT();
|
||||||
|
|
||||||
|
free(_glfw.x11.primarySelectionString);
|
||||||
|
_glfw.x11.primarySelectionString = strdup(string);
|
||||||
|
|
||||||
|
XSetSelectionOwner(_glfw.x11.display,
|
||||||
|
_glfw.x11.PRIMARY,
|
||||||
|
_glfw.x11.helperWindowHandle,
|
||||||
|
CurrentTime);
|
||||||
|
|
||||||
|
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
|
||||||
|
_glfw.x11.helperWindowHandle)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
|
"X11: Failed to become owner of primary selection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWAPI const char* glfwGetX11SelectionString(void)
|
||||||
|
{
|
||||||
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
|
return getSelection(_glfw.x11.PRIMARY, &_glfw.x11.primarySelectionString);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user