X11 copying code and support PRIMARY & CLIPBOARD clipboards.

This commit is contained in:
Ralph Eastwood 2011-09-22 12:03:45 +01:00
parent 57522db6e2
commit 9f41e5b67a
6 changed files with 179 additions and 40 deletions

View File

@ -466,6 +466,7 @@ extern "C" {
#define GLFW_PLATFORM_ERROR 0x00070008 #define GLFW_PLATFORM_ERROR 0x00070008
#define GLFW_WINDOW_NOT_ACTIVE 0x00070009 #define GLFW_WINDOW_NOT_ACTIVE 0x00070009
#define GLFW_CLIPBOARD_FORMAT_UNAVAILABLE 0x00070010 #define GLFW_CLIPBOARD_FORMAT_UNAVAILABLE 0x00070010
#define GLFW_CLIPBOARD_CANNOT_OWN 0x00070011
/* Gamma ramps */ /* Gamma ramps */
#define GLFW_GAMMA_RAMP_SIZE 256 #define GLFW_GAMMA_RAMP_SIZE 256

View File

@ -71,5 +71,8 @@ GLFWAPI size_t glfwGetClipboardData(void *data, size_t size, int format)
if (format == GLFW_CLIPBOARD_FORMAT_NONE) if (format == GLFW_CLIPBOARD_FORMAT_NONE)
return 0; return 0;
if (!data || !size)
return 0;
return _glfwPlatformGetClipboardData(data, size, format); return _glfwPlatformGetClipboardData(data, size, format);
} }

View File

@ -27,6 +27,8 @@
// //
//======================================================================== //========================================================================
// TODO: Incremental support? Overkill perhaps.
#include "internal.h" #include "internal.h"
#include <stdio.h> #include <stdio.h>
@ -45,28 +47,101 @@
static Atom *getInternalFormat(int fmt) static Atom *getInternalFormat(int fmt)
{ {
// Get the necessary atoms // Get the necessary atoms
switch (fmt) switch (fmt)
{ {
case GLFW_CLIPBOARD_FORMAT_STRING: case GLFW_CLIPBOARD_FORMAT_STRING:
return _glfwLibrary.X11.selection.stringatoms; return _glfwLibrary.X11.selection.atoms.string;
default: default:
return 0; return 0;
} }
} }
//========================================================================
// X11 selection request event
//========================================================================
Atom _glfwSelectionRequest(XSelectionRequestEvent *request)
{
Atom *atoms = _glfwLibrary.X11.selection.atoms.string;
if (request->target == XA_STRING)
{
// TODO: ISO Latin-1 specific characters don't get converted
// (yet). For cleanliness, would we need something like iconv?
XChangeProperty(_glfwLibrary.X11.display,
request->requestor,
request->target,
request->target,
8,
PropModeReplace,
(unsigned char *)_glfwLibrary.X11.selection.clipboard.string,
8);
}
else if (request->target == atoms[_GLFW_STRING_ATOM_COMPOUND] ||
request->target == atoms[_GLFW_STRING_ATOM_UTF8])
{
XChangeProperty(_glfwLibrary.X11.display,
request->requestor,
request->target,
request->target,
8,
PropModeReplace,
(unsigned char *)_glfwLibrary.X11.selection.clipboard.string,
_glfwLibrary.X11.selection.clipboard.stringlen);
}
else
{
// TODO: Should we set an error? Probably not.
return None;
}
return request->target;
}
//======================================================================== //========================================================================
// Set the clipboard contents // Set the clipboard contents
//======================================================================== //========================================================================
void _glfwPlatformSetClipboardData(void *data, size_t size, int format) void _glfwPlatformSetClipboardData(void *data, size_t size, int format)
{ {
switch (format)
{
case GLFW_CLIPBOARD_FORMAT_STRING:
{
// Allocate memory to keep track of the clipboard
char *cb = malloc(size+1);
// Copy the clipboard data
memcpy(cb, data, size);
// Set the string length
_glfwLibrary.X11.selection.clipboard.stringlen = size;
// Check if existing clipboard memory needs to be freed
if (_glfwLibrary.X11.selection.clipboard.string)
free(_glfwLibrary.X11.selection.clipboard.string);
// Now set the clipboard (awaiting the event SelectionRequest)
_glfwLibrary.X11.selection.clipboard.string = cb;
break;
}
default:
_glfwSetError(GLFW_CLIPBOARD_FORMAT_UNAVAILABLE,
"X11/GLX: Unavailable clipboard format");
return;
}
// Set the selection owner to our active window
XSetSelectionOwner(_glfwLibrary.X11.display, XA_PRIMARY,
_glfwLibrary.activeWindow->X11.handle, CurrentTime);
XSetSelectionOwner(_glfwLibrary.X11.display,
_glfwLibrary.X11.selection.atoms.clipboard
[_GLFW_CLIPBOARD_ATOM_CLIPBOARD],
_glfwLibrary.activeWindow->X11.handle, CurrentTime);
XFlush(_glfwLibrary.X11.display);
} }
//======================================================================== //========================================================================
// Return the current clipboard contents // Return the current clipboard contents
// TODO: Incremental support? Overkill perhaps.
//======================================================================== //========================================================================
size_t _glfwPlatformGetClipboardData(void *data, size_t size, int format) size_t _glfwPlatformGetClipboardData(void *data, size_t size, int format)
@ -74,46 +149,60 @@ size_t _glfwPlatformGetClipboardData(void *data, size_t size, int format)
size_t len, rembytes, dummy; size_t len, rembytes, dummy;
unsigned char *d; unsigned char *d;
int fmt; int fmt;
Window window; Atom type;
Atom *xfmt, type;
// Try different formats that relate to the GLFW format with preference // Try different clipboards and formats that relate to the GLFW
// for better formats first // format with preference for more appropriate formats first
for (xfmt = getInternalFormat(format); *xfmt; xfmt++) Atom *xcbrd = _glfwLibrary.X11.selection.atoms.clipboard;
Atom *xcbrdend = _glfwLibrary.X11.selection.atoms.clipboard +
_GLFW_CLIPBOARD_ATOM_COUNT;
Atom *xfmt = getInternalFormat(format);
Atom *xfmtend = xfmt + _GLFW_STRING_ATOM_COUNT;
// Get the currently active window
Window window = _glfwLibrary.activeWindow->X11.handle;
for (; xcbrd != xcbrdend; xcbrd++)
{ {
// Specify the format we would like. for (; xfmt != xfmtend; xfmt++)
_glfwLibrary.X11.selection.request = *xfmt;
// Convert the selection into a format we would like.
window = _glfwLibrary.activeWindow->X11.handle;
XConvertSelection(_glfwLibrary.X11.display, XA_PRIMARY,
*xfmt, None, window,
CurrentTime);
XFlush(_glfwLibrary.X11.display);
// Process pending events until we get a SelectionNotify.
while (!_glfwLibrary.X11.selection.converted)
_glfwPlatformWaitEvents();
// If there is no owner to the selection/wrong request, bail out.
if (_glfwLibrary.X11.selection.converted == 2)
{ {
_glfwLibrary.X11.selection.converted = 0; // Specify the format we would like.
_glfwSetError(GLFW_CLIPBOARD_FORMAT_UNAVAILABLE, _glfwLibrary.X11.selection.request = *xfmt;
"X11/GLX: Unavailable clipboard format");
return 0; // Convert the selection into a format we would like.
XConvertSelection(_glfwLibrary.X11.display, *xcbrd,
*xfmt, None, window, CurrentTime);
XFlush(_glfwLibrary.X11.display);
// Process pending events until we get a SelectionNotify.
while (!_glfwLibrary.X11.selection.converted)
_glfwPlatformWaitEvents();
// Successful?
if (_glfwLibrary.X11.selection.converted == 1)
break;
} }
else // Right format, stop checking
// Successful?
if (_glfwLibrary.X11.selection.converted == 1)
{ {
_glfwLibrary.X11.selection.converted = 0; _glfwLibrary.X11.selection.converted = 0;
break; break;
} }
} }
// Unsuccessful conversion, bail with no clipboard data
if (_glfwLibrary.X11.selection.converted)
{
_glfwSetError(GLFW_CLIPBOARD_FORMAT_UNAVAILABLE,
"X11/GLX: Unavailable clipboard format");
return 0;
}
// Reset for the next selection // Reset for the next selection
_glfwLibrary.X11.selection.converted = 0; _glfwLibrary.X11.selection.converted = 0;
// Check the length of data to receive // Check the length of data to receive (rembytes)
XGetWindowProperty(_glfwLibrary.X11.display, XGetWindowProperty(_glfwLibrary.X11.display,
window, window,
*xfmt, *xfmt,

View File

@ -446,13 +446,20 @@ static GLboolean initDisplay(void)
// the keyboard mapping. // the keyboard mapping.
updateKeyCodeLUT(); updateKeyCodeLUT();
// Find or create clipboard atoms
_glfwLibrary.X11.selection.atoms.clipboard[_GLFW_CLIPBOARD_ATOM_PRIMARY] =
XA_PRIMARY;
_glfwLibrary.X11.selection.atoms.clipboard[_GLFW_CLIPBOARD_ATOM_CLIPBOARD] =
XInternAtom(_glfwLibrary.X11.display, "CLIPBOARD", False);
_glfwLibrary.X11.selection.atoms.clipboard[_GLFW_CLIPBOARD_ATOM_SECONDARY] =
XA_SECONDARY;
// Find or create selection atoms // Find or create selection atoms
_glfwLibrary.X11.selection.stringatoms[0] = _glfwLibrary.X11.selection.atoms.string[_GLFW_STRING_ATOM_UTF8] =
XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False); XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False);
_glfwLibrary.X11.selection.stringatoms[1] = _glfwLibrary.X11.selection.atoms.string[_GLFW_STRING_ATOM_COMPOUND] =
XInternAtom(_glfwLibrary.X11.display, "COMPOUND_STRING", False); XInternAtom(_glfwLibrary.X11.display, "COMPOUND_STRING", False);
_glfwLibrary.X11.selection.stringatoms[2] = XA_STRING; _glfwLibrary.X11.selection.atoms.string[_GLFW_STRING_ATOM_STRING] = XA_STRING;
_glfwLibrary.X11.selection.stringatoms[3] = 0;
return GL_TRUE; return GL_TRUE;
} }
@ -616,6 +623,10 @@ int _glfwPlatformTerminate(void)
} }
#endif #endif
// Free clipboard memory
if (_glfwLibrary.X11.selection.clipboard.string)
free(_glfwLibrary.X11.selection.clipboard.string);
return GL_TRUE; return GL_TRUE;
} }

View File

@ -89,8 +89,17 @@
#define _GLFW_PLATFORM_LIBRARY_STATE _GLFWlibraryX11 X11 #define _GLFW_PLATFORM_LIBRARY_STATE _GLFWlibraryX11 X11
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX GLX #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX GLX
// Number of string atoms that will be checked // Clipboard atoms
#define _GLFW_STRING_ATOMS_COUNT 4 #define _GLFW_CLIPBOARD_ATOM_PRIMARY 0
#define _GLFW_CLIPBOARD_ATOM_CLIPBOARD 1
#define _GLFW_CLIPBOARD_ATOM_SECONDARY 2
#define _GLFW_CLIPBOARD_ATOM_COUNT 3
// String atoms
#define _GLFW_STRING_ATOM_UTF8 0
#define _GLFW_STRING_ATOM_COMPOUND 1
#define _GLFW_STRING_ATOM_STRING 2
#define _GLFW_STRING_ATOM_COUNT 3
//======================================================================== //========================================================================
// GLFW platform specific types // GLFW platform specific types
@ -229,8 +238,15 @@ typedef struct _GLFWlibraryX11
// Selection data // Selection data
struct { struct {
Atom stringatoms[_GLFW_STRING_ATOMS_COUNT]; struct {
Atom request; Atom clipboard[_GLFW_CLIPBOARD_ATOM_COUNT];
Atom string[_GLFW_STRING_ATOM_COUNT];
} atoms;
struct {
size_t stringlen;
char *string;
} clipboard;
Atom request;
int converted; int converted;
} selection; } selection;
@ -273,5 +289,7 @@ void _glfwTerminateJoysticks(void);
// Unicode support // Unicode support
long _glfwKeySym2Unicode(KeySym keysym); long _glfwKeySym2Unicode(KeySym keysym);
// Clipboard handling
Atom _glfwSelectionRequest(XSelectionRequestEvent *request);
#endif // _platform_h_ #endif // _platform_h_

View File

@ -41,8 +41,8 @@
#define _NET_WM_STATE_TOGGLE 2 #define _NET_WM_STATE_TOGGLE 2
// Additional mouse button names for XButtonEvent // Additional mouse button names for XButtonEvent
#define Button6 6 #define Button6 6
#define Button7 7 #define Button7 7
//======================================================================== //========================================================================
// Error handler for BadMatch errors when requesting context with // Error handler for BadMatch errors when requesting context with
@ -1394,6 +1394,23 @@ static void processSingleEvent(void)
break; break;
} }
case SelectionRequest:
{
XSelectionRequestEvent *request = &event.xselectionrequest;
// Construct the response
XEvent response;
response.xselection.property = _glfwSelectionRequest(request);
response.xselection.type = SelectionNotify;
response.xselection.display = request->display;
response.xselection.requestor = request->requestor;
response.xselection.selection = request->selection;
response.xselection.target = request->target;
response.xselection.time = request->time;
// Send off the event
XSendEvent(_glfwLibrary.X11.display, request->requestor, 0, 0, &response);
break;
}
// Was the window destroyed? // Was the window destroyed?
case DestroyNotify: case DestroyNotify:
return; return;