mirror of
https://github.com/glfw/glfw.git
synced 2026-02-21 06:33:03 +01: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:
committed by
Camilla Löwy
parent
3ee7f8f695
commit
29a75ab09d
@@ -611,6 +611,7 @@ static GLFWbool initExtensions(void)
|
||||
// ICCCM standard clipboard atoms
|
||||
_glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", 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);
|
||||
|
||||
// Clipboard manager atoms
|
||||
@@ -853,6 +854,7 @@ void _glfwPlatformTerminate(void)
|
||||
_glfw.x11.hiddenCursorHandle = (Cursor) 0;
|
||||
}
|
||||
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
free(_glfw.x11.clipboardString);
|
||||
|
||||
if (_glfw.x11.im)
|
||||
|
||||
@@ -161,6 +161,8 @@ typedef struct _GLFWlibraryX11
|
||||
XIM im;
|
||||
// Most recent error code received by X error handler
|
||||
int errorCode;
|
||||
// Primary selection string (while the primary selection is owned)
|
||||
char* primarySelectionString;
|
||||
// Clipboard string (while the selection is owned)
|
||||
char* clipboardString;
|
||||
// Key name string
|
||||
@@ -214,6 +216,7 @@ typedef struct _GLFWlibraryX11
|
||||
Atom TARGETS;
|
||||
Atom MULTIPLE;
|
||||
Atom CLIPBOARD;
|
||||
Atom PRIMARY;
|
||||
Atom CLIPBOARD_MANAGER;
|
||||
Atom SAVE_TARGETS;
|
||||
Atom NULL_;
|
||||
|
||||
185
src/x11_window.c
185
src/x11_window.c
@@ -675,6 +675,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
_glfw.x11.COMPOUND_STRING,
|
||||
XA_STRING };
|
||||
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)
|
||||
{
|
||||
@@ -735,8 +737,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
targets[i],
|
||||
8,
|
||||
PropModeReplace,
|
||||
(unsigned char*) _glfw.x11.clipboardString,
|
||||
strlen(_glfw.x11.clipboardString));
|
||||
(unsigned char *) selectionString,
|
||||
strlen(selectionString));
|
||||
}
|
||||
else
|
||||
targets[i + 1] = None;
|
||||
@@ -787,8 +789,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
request->target,
|
||||
8,
|
||||
PropModeReplace,
|
||||
(unsigned char*) _glfw.x11.clipboardString,
|
||||
strlen(_glfw.x11.clipboardString));
|
||||
(unsigned char *) selectionString,
|
||||
strlen(selectionString));
|
||||
|
||||
return request->property;
|
||||
}
|
||||
@@ -801,8 +803,17 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
|
||||
static void handleSelectionClear(XEvent* event)
|
||||
{
|
||||
free(_glfw.x11.clipboardString);
|
||||
_glfw.x11.clipboardString = NULL;
|
||||
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);
|
||||
_glfw.x11.clipboardString = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleSelectionRequest(XEvent* event)
|
||||
@@ -823,6 +834,76 @@ static void handleSelectionRequest(XEvent* event)
|
||||
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
|
||||
//
|
||||
static GLFWbool acquireMonitor(_GLFWwindow* window)
|
||||
@@ -2572,72 +2653,7 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
|
||||
|
||||
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 (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;
|
||||
return getSelection(_glfw.x11.CLIPBOARD, &_glfw.x11.clipboardString);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||
@@ -2807,3 +2823,28 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user