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 <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;
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)
{
// TODO: ISO Latin-1 specific characters don't get converted
// (yet). For cleanliness, would we need something like iconv?
int i;
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,
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;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
@ -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;
}

View File

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

View File

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

View File

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