mirror of
https://github.com/glfw/glfw.git
synced 2024-11-10 09:01:46 +00:00
Re-worked and fixed X11 clipboard support.
This commit is contained in:
parent
ad48c0e5ef
commit
f231ed37f0
@ -27,8 +27,6 @@
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
// TODO: Incremental support? Overkill perhaps.
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@ -42,46 +40,90 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//========================================================================
|
||||
// X11 selection request event
|
||||
// Save the contents of the specified property
|
||||
//========================================================================
|
||||
|
||||
Atom _glfwSelectionRequest(XSelectionRequestEvent* request)
|
||||
GLboolean _glfwReadSelection(XSelectionEvent* request)
|
||||
{
|
||||
Atom* formats = _glfwLibrary.X11.selection.formats;
|
||||
char* target = _glfwLibrary.X11.selection.string;
|
||||
Atom actualType;
|
||||
int actualFormat;
|
||||
unsigned long itemCount, bytesAfter;
|
||||
char* data;
|
||||
|
||||
if (request->target == XA_STRING)
|
||||
if (request->property == None)
|
||||
return GL_FALSE;
|
||||
|
||||
XGetWindowProperty(_glfwLibrary.X11.display,
|
||||
request->requestor,
|
||||
request->property,
|
||||
0, LONG_MAX,
|
||||
False,
|
||||
request->target,
|
||||
&actualType,
|
||||
&actualFormat,
|
||||
&itemCount,
|
||||
&bytesAfter,
|
||||
(unsigned char**) &data);
|
||||
|
||||
if (actualType == None)
|
||||
return GL_FALSE;
|
||||
|
||||
free(_glfwLibrary.X11.selection.string);
|
||||
_glfwLibrary.X11.selection.string = strdup(data);
|
||||
|
||||
XFree(data);
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//========================================================================
|
||||
// Set the specified property to the contents of the requested selection
|
||||
//========================================================================
|
||||
|
||||
Atom _glfwWriteSelection(XSelectionRequestEvent* request)
|
||||
{
|
||||
int i;
|
||||
Atom property = request->property;
|
||||
|
||||
if (property == None)
|
||||
property = _glfwLibrary.X11.selection.property;
|
||||
|
||||
if (request->target == _glfwLibrary.X11.selection.targets)
|
||||
{
|
||||
// TODO: ISO Latin-1 specific characters don't get converted
|
||||
// (yet). For cleanliness, would we need something like iconv?
|
||||
// The list of supported targets was requested
|
||||
|
||||
XChangeProperty(_glfwLibrary.X11.display,
|
||||
request->requestor,
|
||||
request->target,
|
||||
property,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char*) _glfwLibrary.X11.selection.formats,
|
||||
_GLFW_CLIPBOARD_FORMAT_COUNT);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++)
|
||||
{
|
||||
if (request->target == _glfwLibrary.X11.selection.formats[i])
|
||||
{
|
||||
// The requested format is one we support
|
||||
|
||||
XChangeProperty(_glfwLibrary.X11.display,
|
||||
request->requestor,
|
||||
property,
|
||||
request->target,
|
||||
8,
|
||||
PropModeReplace,
|
||||
(unsigned char*) target,
|
||||
8);
|
||||
(unsigned char*) _glfwLibrary.X11.selection.string,
|
||||
strlen(_glfwLibrary.X11.selection.string));
|
||||
|
||||
return property;
|
||||
}
|
||||
else if (request->target == formats[_GLFW_CLIPBOARD_FORMAT_COMPOUND] ||
|
||||
request->target == formats[_GLFW_CLIPBOARD_FORMAT_UTF8])
|
||||
{
|
||||
XChangeProperty(_glfwLibrary.X11.display,
|
||||
request->requestor,
|
||||
request->target,
|
||||
request->target,
|
||||
8,
|
||||
PropModeReplace,
|
||||
(unsigned char*) target,
|
||||
_glfwLibrary.X11.selection.stringLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Should we set an error? Probably not.
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
return request->target;
|
||||
}
|
||||
|
||||
|
||||
@ -95,21 +137,14 @@ Atom _glfwSelectionRequest(XSelectionRequestEvent* request)
|
||||
|
||||
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
|
||||
{
|
||||
size_t size = strlen(string) + 1;
|
||||
|
||||
// Store the new string in preparation for a request event
|
||||
// Store the new string in preparation for a selection request event
|
||||
free(_glfwLibrary.X11.selection.string);
|
||||
_glfwLibrary.X11.selection.string = malloc(size);
|
||||
_glfwLibrary.X11.selection.stringLength = size;
|
||||
memcpy(_glfwLibrary.X11.selection.string, string, size);
|
||||
_glfwLibrary.X11.selection.string = strdup(string);
|
||||
|
||||
// Set the selection owner to our active window
|
||||
XSetSelectionOwner(_glfwLibrary.X11.display, XA_PRIMARY,
|
||||
window->X11.handle, CurrentTime);
|
||||
// Set the specified window as owner of the selection
|
||||
XSetSelectionOwner(_glfwLibrary.X11.display,
|
||||
_glfwLibrary.X11.selection.atom,
|
||||
window->X11.handle, CurrentTime);
|
||||
XFlush(_glfwLibrary.X11.display);
|
||||
}
|
||||
|
||||
|
||||
@ -117,103 +152,50 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
|
||||
// Return the current clipboard contents
|
||||
//========================================================================
|
||||
|
||||
size_t _glfwPlatformGetClipboardString(_GLFWwindow* window, char* data, size_t size)
|
||||
size_t _glfwPlatformGetClipboardString(_GLFWwindow* window, char* string, size_t size)
|
||||
{
|
||||
size_t len, rembytes, dummy;
|
||||
unsigned char* d;
|
||||
int i, fmt;
|
||||
Atom type;
|
||||
int i;
|
||||
size_t sourceSize, targetSize;
|
||||
|
||||
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_INACTIVE;
|
||||
|
||||
for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++)
|
||||
{
|
||||
// Specify the format we would like.
|
||||
_glfwLibrary.X11.selection.request =
|
||||
// Request conversion to the selected format
|
||||
_glfwLibrary.X11.selection.target =
|
||||
_glfwLibrary.X11.selection.formats[i];
|
||||
|
||||
// Convert the selection into a format we would like.
|
||||
XConvertSelection(_glfwLibrary.X11.display,
|
||||
_glfwLibrary.X11.selection.atom,
|
||||
_glfwLibrary.X11.selection.request,
|
||||
None, window->X11.handle, CurrentTime);
|
||||
_glfwLibrary.X11.selection.target,
|
||||
_glfwLibrary.X11.selection.property,
|
||||
window->X11.handle, CurrentTime);
|
||||
|
||||
// Process the resulting SelectionNotify event
|
||||
XSync(_glfwLibrary.X11.display, False);
|
||||
_glfwProcessPendingEvents();
|
||||
while (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_INACTIVE)
|
||||
_glfwPlatformWaitEvents();
|
||||
|
||||
// Successful?
|
||||
if (_glfwLibrary.X11.selection.converted == 1)
|
||||
if (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_SUCCEEDED)
|
||||
break;
|
||||
}
|
||||
|
||||
// Successful?
|
||||
if (_glfwLibrary.X11.selection.converted == 1)
|
||||
_glfwLibrary.X11.selection.converted = 0;
|
||||
|
||||
// Unsuccessful conversion, bail with no clipboard data
|
||||
if (_glfwLibrary.X11.selection.converted)
|
||||
if (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_FAILED)
|
||||
{
|
||||
_glfwSetError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"X11/GLX: Failed to convert selection to string");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reset for the next selection
|
||||
_glfwLibrary.X11.selection.converted = 0;
|
||||
sourceSize = strlen(_glfwLibrary.X11.selection.string) + 1;
|
||||
|
||||
// Check the length of data to receive (rembytes)
|
||||
XGetWindowProperty(_glfwLibrary.X11.display,
|
||||
window->X11.handle,
|
||||
_glfwLibrary.X11.selection.request,
|
||||
0, 0,
|
||||
0,
|
||||
AnyPropertyType,
|
||||
&type,
|
||||
&fmt,
|
||||
&len, &rembytes,
|
||||
&d);
|
||||
targetSize = sourceSize;
|
||||
if (targetSize > size)
|
||||
targetSize = size;
|
||||
|
||||
// The number of bytes remaining (which is all of them)
|
||||
if (rembytes > 0)
|
||||
{
|
||||
int result = XGetWindowProperty(_glfwLibrary.X11.display,
|
||||
window->X11.handle,
|
||||
_glfwLibrary.X11.selection.request,
|
||||
0, rembytes,
|
||||
0,
|
||||
AnyPropertyType,
|
||||
&type,
|
||||
&fmt,
|
||||
&len, &dummy,
|
||||
&d);
|
||||
if (result == Success)
|
||||
{
|
||||
size_t s;
|
||||
memcpy(string, _glfwLibrary.X11.selection.string, targetSize);
|
||||
string[targetSize - 1] = '\0';
|
||||
|
||||
if (rembytes < size - 1)
|
||||
s = rembytes;
|
||||
else
|
||||
s = size - 1;
|
||||
|
||||
// Copy the data out.
|
||||
memcpy(data, d, s);
|
||||
|
||||
// Null-terminate strings.
|
||||
((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;
|
||||
return sourceSize;
|
||||
}
|
||||
|
||||
|
@ -597,11 +597,15 @@ static GLboolean initDisplay(void)
|
||||
// the keyboard mapping.
|
||||
updateKeyCodeLUT();
|
||||
|
||||
// Find or create selection property atom
|
||||
_glfwLibrary.X11.selection.property =
|
||||
XInternAtom(_glfwLibrary.X11.display, "GLFW_SELECTION", False);
|
||||
|
||||
// Find or create clipboard atom
|
||||
_glfwLibrary.X11.selection.atom =
|
||||
XInternAtom(_glfwLibrary.X11.display, "CLIPBOARD", False);
|
||||
|
||||
// Find or create selection atoms
|
||||
// Find or create selection target atoms
|
||||
_glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_UTF8] =
|
||||
XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False);
|
||||
_glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_COMPOUND] =
|
||||
@ -609,6 +613,10 @@ static GLboolean initDisplay(void)
|
||||
_glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_STRING] =
|
||||
XA_STRING;
|
||||
|
||||
_glfwLibrary.X11.selection.targets = XInternAtom(_glfwLibrary.X11.display,
|
||||
"TARGETS",
|
||||
False);
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,12 @@
|
||||
#define _GLFW_CLIPBOARD_FORMAT_STRING 2
|
||||
#define _GLFW_CLIPBOARD_FORMAT_COUNT 3
|
||||
|
||||
// Clipboard conversion status tokens
|
||||
#define _GLFW_CONVERSION_INACTIVE 0
|
||||
#define _GLFW_CONVERSION_SUCCEEDED 1
|
||||
#define _GLFW_CONVERSION_FAILED 2
|
||||
|
||||
|
||||
//========================================================================
|
||||
// GLFW platform specific types
|
||||
//========================================================================
|
||||
@ -235,10 +241,11 @@ typedef struct _GLFWlibraryX11
|
||||
struct {
|
||||
Atom atom;
|
||||
Atom formats[_GLFW_CLIPBOARD_FORMAT_COUNT];
|
||||
size_t stringLength;
|
||||
char* string;
|
||||
Atom request;
|
||||
int converted;
|
||||
Atom target;
|
||||
Atom targets;
|
||||
Atom property;
|
||||
int status;
|
||||
} selection;
|
||||
|
||||
#if defined(_GLFW_DLOPEN_LIBGL)
|
||||
@ -281,7 +288,8 @@ void _glfwTerminateJoysticks(void);
|
||||
long _glfwKeySym2Unicode(KeySym keysym);
|
||||
|
||||
// Clipboard handling
|
||||
Atom _glfwSelectionRequest(XSelectionRequestEvent *request);
|
||||
GLboolean _glfwReadSelection(XSelectionEvent* request);
|
||||
Atom _glfwWriteSelection(XSelectionRequestEvent* request);
|
||||
|
||||
// Event processing
|
||||
void _glfwProcessPendingEvents(void);
|
||||
|
@ -1241,28 +1241,39 @@ static void processSingleEvent(void)
|
||||
break;
|
||||
}
|
||||
|
||||
case SelectionClear:
|
||||
{
|
||||
// The ownership of the selection was lost
|
||||
|
||||
free(_glfwLibrary.X11.selection.string);
|
||||
_glfwLibrary.X11.selection.string = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
case SelectionNotify:
|
||||
{
|
||||
// Selection notification triggered by the XConvertSelection
|
||||
// The selection conversion status is available
|
||||
|
||||
// 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;
|
||||
XSelectionEvent* request = &event.xselection;
|
||||
|
||||
if (_glfwReadSelection(request))
|
||||
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED;
|
||||
else
|
||||
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_FAILED;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SelectionRequest:
|
||||
{
|
||||
// Selection request triggered by someone wanting data from the
|
||||
// X11 clipboard
|
||||
XSelectionRequestEvent *request = &event.xselectionrequest;
|
||||
// The contents of the selection was requested
|
||||
|
||||
XSelectionRequestEvent* request = &event.xselectionrequest;
|
||||
|
||||
// Construct the response
|
||||
XEvent response;
|
||||
response.xselection.property = _glfwSelectionRequest(request);
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
response.xselection.property = _glfwWriteSelection(request);
|
||||
response.xselection.type = SelectionNotify;
|
||||
response.xselection.display = request->display;
|
||||
response.xselection.requestor = request->requestor;
|
||||
@ -1270,9 +1281,9 @@ static void processSingleEvent(void)
|
||||
response.xselection.target = request->target;
|
||||
response.xselection.time = request->time;
|
||||
|
||||
// Send off the event
|
||||
XSendEvent(_glfwLibrary.X11.display, request->requestor, 0, 0, &response);
|
||||
|
||||
XSendEvent(_glfwLibrary.X11.display,
|
||||
request->requestor,
|
||||
False, 0, &response);
|
||||
break;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user