mirror of https://github.com/dcoredump/dexed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
10 KiB
271 lines
10 KiB
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2015 - ROLI Ltd.
|
|
|
|
Permission is granted to use this software under the terms of either:
|
|
a) the GPL v2 (or any later version)
|
|
b) the Affero GPL v3
|
|
|
|
Details of these licenses can be found at: www.gnu.org/licenses
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.juce.com for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
extern ::Display* display;
|
|
extern ::Window juce_messageWindowHandle;
|
|
|
|
namespace ClipboardHelpers
|
|
{
|
|
static String localClipboardContent;
|
|
static Atom atom_UTF8_STRING;
|
|
static Atom atom_CLIPBOARD;
|
|
static Atom atom_TARGETS;
|
|
|
|
//==============================================================================
|
|
static void initSelectionAtoms()
|
|
{
|
|
static bool isInitialised = false;
|
|
|
|
if (! isInitialised)
|
|
{
|
|
isInitialised = true;
|
|
|
|
atom_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
|
|
atom_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False);
|
|
atom_TARGETS = XInternAtom (display, "TARGETS", False);
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
// Read the content of a window property as either a locale-dependent string or an utf8 string
|
|
// works only for strings shorter than 1000000 bytes
|
|
static String readWindowProperty (Window window, Atom prop)
|
|
{
|
|
String returnData;
|
|
|
|
if (display != nullptr)
|
|
{
|
|
char* clipData;
|
|
Atom actualType;
|
|
int actualFormat;
|
|
unsigned long numItems, bytesLeft;
|
|
|
|
if (XGetWindowProperty (display, window, prop,
|
|
0L /* offset */, 1000000 /* length (max) */, False,
|
|
AnyPropertyType /* format */,
|
|
&actualType, &actualFormat, &numItems, &bytesLeft,
|
|
(unsigned char**) &clipData) == Success)
|
|
{
|
|
if (actualType == atom_UTF8_STRING && actualFormat == 8)
|
|
returnData = String::fromUTF8 (clipData, (int) numItems);
|
|
else if (actualType == XA_STRING && actualFormat == 8)
|
|
returnData = String (clipData, numItems);
|
|
|
|
if (clipData != nullptr)
|
|
XFree (clipData);
|
|
|
|
jassert (bytesLeft == 0 || numItems == 1000000);
|
|
}
|
|
|
|
XDeleteProperty (display, window, prop);
|
|
}
|
|
|
|
return returnData;
|
|
}
|
|
|
|
//==============================================================================
|
|
// Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */
|
|
static bool requestSelectionContent (String& selectionContent, Atom selection, Atom requestedFormat)
|
|
{
|
|
Atom property_name = XInternAtom (display, "JUCE_SEL", false);
|
|
|
|
// The selection owner will be asked to set the JUCE_SEL property on the
|
|
// juce_messageWindowHandle with the selection content
|
|
XConvertSelection (display, selection, requestedFormat, property_name,
|
|
juce_messageWindowHandle, CurrentTime);
|
|
|
|
int count = 50; // will wait at most for 200 ms
|
|
|
|
while (--count >= 0)
|
|
{
|
|
XEvent event;
|
|
|
|
if (XCheckTypedWindowEvent (display, juce_messageWindowHandle, SelectionNotify, &event))
|
|
{
|
|
if (event.xselection.property == property_name)
|
|
{
|
|
jassert (event.xselection.requestor == juce_messageWindowHandle);
|
|
|
|
selectionContent = readWindowProperty (event.xselection.requestor,
|
|
event.xselection.property);
|
|
return true;
|
|
}
|
|
|
|
return false; // the format we asked for was denied.. (event.xselection.property == None)
|
|
}
|
|
|
|
// not very elegant.. we could do a select() or something like that...
|
|
// however clipboard content requesting is inherently slow on x11, it
|
|
// often takes 50ms or more so...
|
|
Thread::sleep (4);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//==============================================================================
|
|
// Called from the event loop in juce_linux_Messaging in response to SelectionRequest events
|
|
static void handleSelection (XSelectionRequestEvent& evt)
|
|
{
|
|
if (display != nullptr)
|
|
{
|
|
ClipboardHelpers::initSelectionAtoms();
|
|
|
|
// the selection content is sent to the target window as a window property
|
|
XSelectionEvent reply;
|
|
reply.type = SelectionNotify;
|
|
reply.display = evt.display;
|
|
reply.requestor = evt.requestor;
|
|
reply.selection = evt.selection;
|
|
reply.target = evt.target;
|
|
reply.property = None; // == "fail"
|
|
reply.time = evt.time;
|
|
|
|
HeapBlock <char> data;
|
|
int propertyFormat = 0;
|
|
size_t numDataItems = 0;
|
|
|
|
if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD)
|
|
{
|
|
if (evt.target == XA_STRING || evt.target == ClipboardHelpers::atom_UTF8_STRING)
|
|
{
|
|
// translate to utf8
|
|
numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1;
|
|
data.calloc (numDataItems + 1);
|
|
ClipboardHelpers::localClipboardContent.copyToUTF8 (data, numDataItems);
|
|
propertyFormat = 8; // bits/item
|
|
}
|
|
else if (evt.target == ClipboardHelpers::atom_TARGETS)
|
|
{
|
|
// another application wants to know what we are able to send
|
|
numDataItems = 2;
|
|
propertyFormat = 32; // atoms are 32-bit
|
|
data.calloc (numDataItems * 4);
|
|
Atom* atoms = reinterpret_cast<Atom*> (data.getData());
|
|
atoms[0] = ClipboardHelpers::atom_UTF8_STRING;
|
|
atoms[1] = XA_STRING;
|
|
|
|
evt.target = XA_ATOM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG ("requested unsupported clipboard");
|
|
}
|
|
|
|
if (data != nullptr)
|
|
{
|
|
const size_t maxReasonableSelectionSize = 1000000;
|
|
|
|
// for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss
|
|
if (evt.property != None && numDataItems < maxReasonableSelectionSize)
|
|
{
|
|
XChangeProperty (evt.display, evt.requestor,
|
|
evt.property, evt.target,
|
|
propertyFormat /* 8 or 32 */, PropModeReplace,
|
|
reinterpret_cast<const unsigned char*> (data.getData()), (int) numDataItems);
|
|
reply.property = evt.property; // " == success"
|
|
}
|
|
}
|
|
|
|
XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply);
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
|
|
extern SelectionRequestCallback handleSelectionRequest;
|
|
|
|
struct ClipboardCallbackInitialiser
|
|
{
|
|
ClipboardCallbackInitialiser()
|
|
{
|
|
handleSelectionRequest = ClipboardHelpers::handleSelection;
|
|
}
|
|
};
|
|
|
|
static ClipboardCallbackInitialiser clipboardInitialiser;
|
|
|
|
//==============================================================================
|
|
void SystemClipboard::copyTextToClipboard (const String& clipText)
|
|
{
|
|
if (display != nullptr)
|
|
{
|
|
ClipboardHelpers::initSelectionAtoms();
|
|
ClipboardHelpers::localClipboardContent = clipText;
|
|
|
|
XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
|
|
XSetSelectionOwner (display, ClipboardHelpers::atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime);
|
|
}
|
|
}
|
|
|
|
String SystemClipboard::getTextFromClipboard()
|
|
{
|
|
String content;
|
|
|
|
if (display != nullptr)
|
|
{
|
|
ClipboardHelpers::initSelectionAtoms();
|
|
|
|
/* 1) try to read from the "CLIPBOARD" selection first (the "high
|
|
level" clipboard that is supposed to be filled by ctrl-C
|
|
etc). When a clipboard manager is running, the content of this
|
|
selection is preserved even when the original selection owner
|
|
exits.
|
|
|
|
2) and then try to read from "PRIMARY" selection (the "legacy" selection
|
|
filled by good old x11 apps such as xterm)
|
|
*/
|
|
Atom selection = XA_PRIMARY;
|
|
Window selectionOwner = None;
|
|
|
|
if ((selectionOwner = XGetSelectionOwner (display, selection)) == None)
|
|
{
|
|
selection = ClipboardHelpers::atom_CLIPBOARD;
|
|
selectionOwner = XGetSelectionOwner (display, selection);
|
|
}
|
|
|
|
if (selectionOwner != None)
|
|
{
|
|
if (selectionOwner == juce_messageWindowHandle)
|
|
{
|
|
content = ClipboardHelpers::localClipboardContent;
|
|
}
|
|
else
|
|
{
|
|
// first try: we want an utf8 string
|
|
bool ok = ClipboardHelpers::requestSelectionContent (content, selection, ClipboardHelpers::atom_UTF8_STRING);
|
|
|
|
if (! ok)
|
|
{
|
|
// second chance, ask for a good old locale-dependent string ..
|
|
ok = ClipboardHelpers::requestSelectionContent (content, selection, XA_STRING);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|