Re-worked and fixed X11 clipboard support.

This commit is contained in:
Camilla Berglund 2012-04-11 23:32:50 +02:00
parent ad48c0e5ef
commit f231ed37f0
4 changed files with 144 additions and 135 deletions

View File

@ -27,8 +27,6 @@
// //
//======================================================================== //========================================================================
// TODO: Incremental support? Overkill perhaps.
#include "internal.h" #include "internal.h"
#include <stdio.h> #include <stdio.h>
@ -42,48 +40,92 @@
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//======================================================================== //========================================================================
// 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; Atom actualType;
char* target = _glfwLibrary.X11.selection.string; 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)
{ {
// TODO: ISO Latin-1 specific characters don't get converted int i;
// (yet). For cleanliness, would we need something like iconv? Atom property = request->property;
if (property == None)
property = _glfwLibrary.X11.selection.property;
if (request->target == _glfwLibrary.X11.selection.targets)
{
// The list of supported targets was requested
XChangeProperty(_glfwLibrary.X11.display, XChangeProperty(_glfwLibrary.X11.display,
request->requestor, 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, request->target,
8, 8,
PropModeReplace, PropModeReplace,
(unsigned char*) target, (unsigned char*) _glfwLibrary.X11.selection.string,
8); 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 None;
} }
return request->target;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW platform API ////// ////// GLFW platform API //////
@ -95,21 +137,14 @@ Atom _glfwSelectionRequest(XSelectionRequestEvent* request)
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
{ {
size_t size = strlen(string) + 1; // Store the new string in preparation for a selection request event
// Store the new string in preparation for a request event
free(_glfwLibrary.X11.selection.string); free(_glfwLibrary.X11.selection.string);
_glfwLibrary.X11.selection.string = malloc(size); _glfwLibrary.X11.selection.string = strdup(string);
_glfwLibrary.X11.selection.stringLength = size;
memcpy(_glfwLibrary.X11.selection.string, string, size);
// Set the selection owner to our active window // Set the specified window as owner of the selection
XSetSelectionOwner(_glfwLibrary.X11.display, XA_PRIMARY,
window->X11.handle, CurrentTime);
XSetSelectionOwner(_glfwLibrary.X11.display, XSetSelectionOwner(_glfwLibrary.X11.display,
_glfwLibrary.X11.selection.atom, _glfwLibrary.X11.selection.atom,
window->X11.handle, CurrentTime); window->X11.handle, CurrentTime);
XFlush(_glfwLibrary.X11.display);
} }
@ -117,103 +152,50 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
// Return the current clipboard contents // 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; int i;
unsigned char* d; size_t sourceSize, targetSize;
int i, fmt;
Atom type; _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_INACTIVE;
for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++) for (i = 0; i < _GLFW_CLIPBOARD_FORMAT_COUNT; i++)
{ {
// Specify the format we would like. // Request conversion to the selected format
_glfwLibrary.X11.selection.request = _glfwLibrary.X11.selection.target =
_glfwLibrary.X11.selection.formats[i]; _glfwLibrary.X11.selection.formats[i];
// Convert the selection into a format we would like.
XConvertSelection(_glfwLibrary.X11.display, XConvertSelection(_glfwLibrary.X11.display,
_glfwLibrary.X11.selection.atom, _glfwLibrary.X11.selection.atom,
_glfwLibrary.X11.selection.request, _glfwLibrary.X11.selection.target,
None, window->X11.handle, CurrentTime); _glfwLibrary.X11.selection.property,
window->X11.handle, CurrentTime);
// Process the resulting SelectionNotify event // Process the resulting SelectionNotify event
XSync(_glfwLibrary.X11.display, False); XSync(_glfwLibrary.X11.display, False);
_glfwProcessPendingEvents(); while (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_INACTIVE)
_glfwPlatformWaitEvents();
// Successful? if (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_SUCCEEDED)
if (_glfwLibrary.X11.selection.converted == 1)
break; break;
} }
// Successful? if (_glfwLibrary.X11.selection.status == _GLFW_CONVERSION_FAILED)
if (_glfwLibrary.X11.selection.converted == 1)
_glfwLibrary.X11.selection.converted = 0;
// Unsuccessful conversion, bail with no clipboard data
if (_glfwLibrary.X11.selection.converted)
{ {
_glfwSetError(GLFW_FORMAT_UNAVAILABLE, _glfwSetError(GLFW_FORMAT_UNAVAILABLE,
"X11/GLX: Failed to convert selection to string"); "X11/GLX: Failed to convert selection to string");
return 0; return 0;
} }
// Reset for the next selection sourceSize = strlen(_glfwLibrary.X11.selection.string) + 1;
_glfwLibrary.X11.selection.converted = 0;
// Check the length of data to receive (rembytes) targetSize = sourceSize;
XGetWindowProperty(_glfwLibrary.X11.display, if (targetSize > size)
window->X11.handle, targetSize = size;
_glfwLibrary.X11.selection.request,
0, 0,
0,
AnyPropertyType,
&type,
&fmt,
&len, &rembytes,
&d);
// The number of bytes remaining (which is all of them) memcpy(string, _glfwLibrary.X11.selection.string, targetSize);
if (rembytes > 0) string[targetSize - 1] = '\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;
if (rembytes < size - 1) return sourceSize;
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;
} }

View File

@ -597,11 +597,15 @@ static GLboolean initDisplay(void)
// the keyboard mapping. // the keyboard mapping.
updateKeyCodeLUT(); updateKeyCodeLUT();
// Find or create selection property atom
_glfwLibrary.X11.selection.property =
XInternAtom(_glfwLibrary.X11.display, "GLFW_SELECTION", False);
// Find or create clipboard atom // Find or create clipboard atom
_glfwLibrary.X11.selection.atom = _glfwLibrary.X11.selection.atom =
XInternAtom(_glfwLibrary.X11.display, "CLIPBOARD", False); 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] = _glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_UTF8] =
XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False); XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False);
_glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_COMPOUND] = _glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_COMPOUND] =
@ -609,6 +613,10 @@ static GLboolean initDisplay(void)
_glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_STRING] = _glfwLibrary.X11.selection.formats[_GLFW_CLIPBOARD_FORMAT_STRING] =
XA_STRING; XA_STRING;
_glfwLibrary.X11.selection.targets = XInternAtom(_glfwLibrary.X11.display,
"TARGETS",
False);
return GL_TRUE; return GL_TRUE;
} }

View File

@ -91,6 +91,12 @@
#define _GLFW_CLIPBOARD_FORMAT_STRING 2 #define _GLFW_CLIPBOARD_FORMAT_STRING 2
#define _GLFW_CLIPBOARD_FORMAT_COUNT 3 #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 // GLFW platform specific types
//======================================================================== //========================================================================
@ -235,10 +241,11 @@ typedef struct _GLFWlibraryX11
struct { struct {
Atom atom; Atom atom;
Atom formats[_GLFW_CLIPBOARD_FORMAT_COUNT]; Atom formats[_GLFW_CLIPBOARD_FORMAT_COUNT];
size_t stringLength;
char* string; char* string;
Atom request; Atom target;
int converted; Atom targets;
Atom property;
int status;
} selection; } selection;
#if defined(_GLFW_DLOPEN_LIBGL) #if defined(_GLFW_DLOPEN_LIBGL)
@ -281,7 +288,8 @@ void _glfwTerminateJoysticks(void);
long _glfwKeySym2Unicode(KeySym keysym); long _glfwKeySym2Unicode(KeySym keysym);
// Clipboard handling // Clipboard handling
Atom _glfwSelectionRequest(XSelectionRequestEvent *request); GLboolean _glfwReadSelection(XSelectionEvent* request);
Atom _glfwWriteSelection(XSelectionRequestEvent* request);
// Event processing // Event processing
void _glfwProcessPendingEvents(void); void _glfwProcessPendingEvents(void);

View File

@ -1241,28 +1241,39 @@ static void processSingleEvent(void)
break; break;
} }
case SelectionClear:
{
// The ownership of the selection was lost
free(_glfwLibrary.X11.selection.string);
_glfwLibrary.X11.selection.string = NULL;
break;
}
case SelectionNotify: case SelectionNotify:
{ {
// Selection notification triggered by the XConvertSelection // The selection conversion status is available
// Check if the notification property matches the request XSelectionEvent* request = &event.xselection;
if (event.xselection.property != _glfwLibrary.X11.selection.request)
_glfwLibrary.X11.selection.converted = 2; if (_glfwReadSelection(request))
else // It was successful _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED;
_glfwLibrary.X11.selection.converted = 1; else
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_FAILED;
break; break;
} }
case SelectionRequest: case SelectionRequest:
{ {
// Selection request triggered by someone wanting data from the // The contents of the selection was requested
// X11 clipboard
XSelectionRequestEvent* request = &event.xselectionrequest; XSelectionRequestEvent* request = &event.xselectionrequest;
// Construct the response
XEvent response; XEvent response;
response.xselection.property = _glfwSelectionRequest(request); memset(&response, 0, sizeof(response));
response.xselection.property = _glfwWriteSelection(request);
response.xselection.type = SelectionNotify; response.xselection.type = SelectionNotify;
response.xselection.display = request->display; response.xselection.display = request->display;
response.xselection.requestor = request->requestor; response.xselection.requestor = request->requestor;
@ -1270,9 +1281,9 @@ static void processSingleEvent(void)
response.xselection.target = request->target; response.xselection.target = request->target;
response.xselection.time = request->time; response.xselection.time = request->time;
// Send off the event XSendEvent(_glfwLibrary.X11.display,
XSendEvent(_glfwLibrary.X11.display, request->requestor, 0, 0, &response); request->requestor,
False, 0, &response);
break; break;
} }