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:
Kristian Nielsen
2017-08-02 16:10:13 +02:00
committed by Camilla Löwy
parent 3ee7f8f695
commit 29a75ab09d
6 changed files with 180 additions and 72 deletions

View File

@@ -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)

View File

@@ -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_;

View File

@@ -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);
}