|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
-
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- 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 (::Display* display)
- {
- static bool isInitialised = false;
-
- if (! isInitialised)
- {
- isInitialised = true;
-
- atom_UTF8_STRING = Atoms::getCreating (display, "UTF8_STRING");
- atom_CLIPBOARD = Atoms::getCreating (display, "CLIPBOARD");
- atom_TARGETS = Atoms::getCreating (display, "TARGETS");
- }
- }
-
- //==============================================================================
- // 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 (::Display* display, 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 (::Display* display, 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 (display, 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)
- {
- ClipboardHelpers::initSelectionAtoms (evt.display);
-
- // 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)
- {
- ScopedXDisplay xDisplay;
-
- if (auto display = xDisplay.display)
- {
- ClipboardHelpers::initSelectionAtoms (display);
- ClipboardHelpers::localClipboardContent = clipText;
-
- XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
- XSetSelectionOwner (display, ClipboardHelpers::atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime);
- }
- }
-
- String SystemClipboard::getTextFromClipboard()
- {
- String content;
- ScopedXDisplay xDisplay;
-
- if (auto display = xDisplay.display)
- {
- ClipboardHelpers::initSelectionAtoms (display);
-
- /* 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 (display, content,
- selection, ClipboardHelpers::atom_UTF8_STRING);
-
- if (! ok)
- {
- // second chance, ask for a good old locale-dependent string ..
- ok = ClipboardHelpers::requestSelectionContent (display, content,
- selection, XA_STRING);
- }
- }
- }
- }
-
- return content;
- }
|