mirror of
https://github.com/glfw/glfw.git
synced 2024-11-26 20:11:58 +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 "internal.h"
|
||||||
|
|
||||||
#include <stdio.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;
|
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)
|
||||||
|
{
|
||||||
|
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
|
// The list of supported targets was requested
|
||||||
// (yet). For cleanliness, would we need something like iconv?
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user