|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-11 by Raw Material Software Ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the GNU General
- Public License (Version 2), as published by the Free Software Foundation.
- A copy of the license is included in the JUCE distribution, or can be found
- online 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.rawmaterialsoftware.com/juce 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;
-
- //==============================================================================
- void initSelectionAtoms()
- {
- static bool isInitialised = false;
- if (! isInitialised)
- {
- 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
- String readWindowProperty (Window window, Atom prop, Atom fmt)
- {
- String returnData;
- 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, 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) */
- 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,
- requestedFormat);
- return true;
- }
- else
- {
- 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
- void juce_handleSelectionRequest (XSelectionRequestEvent &evt)
- {
- 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, 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;
- }
- }
- else
- {
- DBG ("requested unsupported clipboard");
- }
-
- if (data != nullptr)
- {
- const int 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()), numDataItems);
- reply.property = evt.property; // " == success"
- }
- }
-
- XSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply);
- }
-
- //==============================================================================
- void SystemClipboard::copyTextToClipboard (const String& clipText)
- {
- ClipboardHelpers::initSelectionAtoms();
- ClipboardHelpers::localClipboardContent = clipText;
-
- XSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
- XSetSelectionOwner (display, ClipboardHelpers::atom_CLIPBOARD, juce_messageWindowHandle, CurrentTime);
- }
-
- String SystemClipboard::getTextFromClipboard()
- {
- 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)
- */
- String content;
- 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;
- }
|