glfw/src/x11_clipboard.c

339 lines
11 KiB
C
Raw Normal View History

2011-09-21 09:09:47 +00:00
//========================================================================
// GLFW - An OpenGL library
2012-08-28 13:03:57 +00:00
// Platform: X11
2011-09-21 09:09:47 +00:00
// API version: 3.0
// WWW: http://www.glfw.org/
//------------------------------------------------------------------------
// Copyright (c) 2010 Camilla Berglund <elmindreda@elmindreda.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
#include "internal.h"
#include <stdio.h>
2011-09-21 09:09:47 +00:00
#include <limits.h>
#include <string.h>
#include <stdlib.h>
2011-09-21 09:09:47 +00:00
2013-04-29 11:16:56 +00:00
// Returns whether the event is a selection event
//
static Bool isSelectionMessage(Display* display, XEvent* event, XPointer pointer)
{
2013-04-30 00:16:24 +00:00
return event->type == SelectionRequest ||
event->type == SelectionNotify ||
event->type == SelectionClear;
2013-04-29 11:16:56 +00:00
}
2013-04-29 18:54:42 +00:00
// Set the specified property to the selection converted to the requested target
2013-02-04 12:22:10 +00:00
//
2013-04-29 18:54:42 +00:00
static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
{
int i;
2013-04-29 10:43:54 +00:00
const Atom formats[] = { _glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_STRING,
XA_STRING };
const int formatCount = sizeof(formats) / sizeof(formats[0]);
if (request->property == None)
{
// The requestor is a legacy client (ICCCM section 2.2)
2013-04-29 18:54:42 +00:00
// We don't support legacy clients, so fail here
return None;
}
2013-01-29 01:45:08 +00:00
if (request->target == _glfw.x11.TARGETS)
{
// The list of supported targets was requested
2013-04-29 10:43:54 +00:00
const Atom targets[] = { _glfw.x11.TARGETS,
_glfw.x11.MULTIPLE,
_glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_STRING,
XA_STRING };
XChangeProperty(_glfw.x11.display,
2012-04-09 13:12:59 +00:00
request->requestor,
request->property,
XA_ATOM,
32,
2012-04-09 13:12:59 +00:00
PropModeReplace,
2013-04-29 10:43:54 +00:00
(unsigned char*) targets,
sizeof(targets) / sizeof(targets[0]));
return request->property;
}
2013-04-29 10:26:25 +00:00
if (request->target == _glfw.x11.MULTIPLE)
{
// Multiple conversions were requested
Atom* targets;
unsigned long i, count;
count = _glfwGetWindowProperty(request->requestor,
request->property,
2013-04-29 11:16:56 +00:00
_glfw.x11.ATOM_PAIR,
2013-04-29 10:26:25 +00:00
(unsigned char**) &targets);
for (i = 0; i < count; i += 2)
{
int j;
2013-04-29 10:43:54 +00:00
for (j = 0; j < formatCount; j++)
2013-04-29 10:26:25 +00:00
{
2013-04-29 10:43:54 +00:00
if (targets[i] == formats[j])
2013-04-29 10:26:25 +00:00
break;
}
2013-04-29 10:43:54 +00:00
if (j < formatCount)
2013-04-29 10:26:25 +00:00
{
XChangeProperty(_glfw.x11.display,
request->requestor,
targets[i + 1],
targets[i],
8,
PropModeReplace,
(unsigned char*) _glfw.x11.selection.string,
strlen(_glfw.x11.selection.string));
}
else
targets[i + 1] = None;
}
XChangeProperty(_glfw.x11.display,
request->requestor,
request->property,
2013-04-29 11:16:56 +00:00
_glfw.x11.ATOM_PAIR,
2013-04-29 10:26:25 +00:00
32,
PropModeReplace,
(unsigned char*) targets,
count);
XFree(targets);
return request->property;
}
2013-04-29 11:16:56 +00:00
if (request->target == _glfw.x11.SAVE_TARGETS)
{
2013-04-29 18:54:42 +00:00
// The request is a check whether we support SAVE_TARGETS
// It should be handled as a no-op side effect target
2013-04-29 11:16:56 +00:00
XChangeProperty(_glfw.x11.display,
request->requestor,
request->property,
XInternAtom(_glfw.x11.display, "NULL", False),
32,
PropModeReplace,
NULL,
0);
return request->property;
}
2013-04-29 18:54:42 +00:00
// Conversion to a data target was requested
2013-04-29 10:43:54 +00:00
for (i = 0; i < formatCount; i++)
{
2013-04-29 10:43:54 +00:00
if (request->target == formats[i])
{
2012-04-11 21:53:47 +00:00
// The requested target is one we support
XChangeProperty(_glfw.x11.display,
request->requestor,
request->property,
request->target,
8,
PropModeReplace,
(unsigned char*) _glfw.x11.selection.string,
strlen(_glfw.x11.selection.string));
return request->property;
}
}
2012-03-28 13:05:17 +00:00
2013-04-29 18:54:42 +00:00
// The requested target is not supported
return None;
}
2013-04-29 18:54:42 +00:00
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
void _glfwHandleSelectionClear(XEvent* event)
2013-04-29 11:16:56 +00:00
{
2013-04-29 18:54:42 +00:00
free(_glfw.x11.selection.string);
_glfw.x11.selection.string = NULL;
}
2013-04-29 11:16:56 +00:00
2013-04-29 18:54:42 +00:00
void _glfwHandleSelectionRequest(XEvent* event)
{
const XSelectionRequestEvent* request = &event->xselectionrequest;
2013-04-29 11:16:56 +00:00
2013-04-29 18:54:42 +00:00
XEvent response;
memset(&response, 0, sizeof(response));
response.xselection.property = writeTargetToProperty(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;
2013-04-29 11:16:56 +00:00
2013-04-29 18:54:42 +00:00
XSendEvent(_glfw.x11.display, request->requestor, False, 0, &response);
}
void _glfwPushSelectionToManager(_GLFWwindow* window)
{
2013-04-29 11:16:56 +00:00
XConvertSelection(_glfw.x11.display,
_glfw.x11.CLIPBOARD_MANAGER,
_glfw.x11.SAVE_TARGETS,
None,
window->x11.handle,
CurrentTime);
for (;;)
{
2013-04-29 18:54:42 +00:00
XEvent event;
if (!XCheckIfEvent(_glfw.x11.display, &event, isSelectionMessage, NULL))
2013-04-29 11:16:56 +00:00
continue;
2013-04-29 18:54:42 +00:00
switch (event.type)
2013-04-29 11:16:56 +00:00
{
case SelectionRequest:
2013-04-29 18:54:42 +00:00
_glfwHandleSelectionRequest(&event);
2013-04-29 11:16:56 +00:00
break;
case SelectionClear:
2013-04-29 18:54:42 +00:00
_glfwHandleSelectionClear(&event);
2013-04-29 11:16:56 +00:00
break;
case SelectionNotify:
{
2013-04-29 18:54:42 +00:00
if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
{
// This means one of two things; either the selection was
// not owned, which means there is no clipboard manager, or
// the transfer to the clipboard manager has completed
// In either case, it means we are done here
2013-04-29 11:16:56 +00:00
return;
2013-04-29 18:54:42 +00:00
}
2013-04-29 11:16:56 +00:00
break;
}
}
}
}
2012-03-28 13:05:17 +00:00
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
2011-09-21 09:09:47 +00:00
{
free(_glfw.x11.selection.string);
_glfw.x11.selection.string = strdup(string);
XSetSelectionOwner(_glfw.x11.display,
2013-01-29 01:45:08 +00:00
_glfw.x11.CLIPBOARD,
window->x11.handle, CurrentTime);
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
window->x11.handle)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: Failed to become owner of the clipboard selection");
}
2011-09-21 09:09:47 +00:00
}
2012-04-11 22:51:58 +00:00
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
2011-09-21 09:09:47 +00:00
{
2013-07-15 16:37:02 +00:00
size_t i;
2013-04-29 10:43:54 +00:00
const Atom formats[] = { _glfw.x11.UTF8_STRING,
_glfw.x11.COMPOUND_STRING,
XA_STRING };
2013-07-15 16:37:02 +00:00
const size_t formatCount = sizeof(formats) / sizeof(formats[0]);
if (_glfwFindWindowByHandle(XGetSelectionOwner(_glfw.x11.display,
_glfw.x11.CLIPBOARD)))
{
// Instead of doing a large number of X round-trips just to put this
// string into a window property and then read it back, just return it
return _glfw.x11.selection.string;
}
free(_glfw.x11.selection.string);
_glfw.x11.selection.string = NULL;
2013-04-30 00:16:24 +00:00
for (i = 0; i < formatCount; i++)
{
char* data;
XEvent event;
XConvertSelection(_glfw.x11.display,
2013-01-29 01:45:08 +00:00
_glfw.x11.CLIPBOARD,
2013-04-29 10:43:54 +00:00
formats[i],
2013-04-29 10:45:18 +00:00
_glfw.x11.GLFW_SELECTION,
window->x11.handle, CurrentTime);
// XCheckTypedEvent is used instead of XIfEvent in order not to lock
// other threads out from the display during the entire wait period
while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event))
;
if (event.xselection.property == None)
continue;
2013-04-29 10:33:27 +00:00
if (_glfwGetWindowProperty(event.xselection.requestor,
event.xselection.property,
event.xselection.target,
(unsigned char**) &data))
{
_glfw.x11.selection.string = strdup(data);
}
XFree(data);
XDeleteProperty(_glfw.x11.display,
event.xselection.requestor,
event.xselection.property);
if (_glfw.x11.selection.string)
break;
}
if (_glfw.x11.selection.string == NULL)
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"X11: Failed to convert selection to string");
}
return _glfw.x11.selection.string;
2011-09-21 09:09:47 +00:00
}