Browse Source

Linux/X11: Prefer CLIPBOARD selection to PRIMARY

As specified in https://specifications.freedesktop.org/clipboards-spec/clipboards-latest.txt, CLIPBOARD should be used for explicit cut/copy/paste operations over PRIMARY
v6.1.6
ed 4 years ago
parent
commit
faf5ed4023
3 changed files with 86 additions and 55 deletions
  1. +52
    -29
      modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp
  2. +30
    -21
      modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
  3. +4
    -5
      modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h

+ 52
- 29
modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp View File

@@ -60,17 +60,19 @@ public:
s.xselection.property = None; s.xselection.property = None;
s.xselection.time = evt.xselectionrequest.time; s.xselection.time = evt.xselectionrequest.time;
auto* display = getDisplay();
if (allowedTypes.contains (targetType)) if (allowedTypes.contains (targetType))
{ {
s.xselection.property = evt.xselectionrequest.property; s.xselection.property = evt.xselectionrequest.property;
X11Symbols::getInstance()->xChangeProperty (getDisplay(), evt.xselectionrequest.requestor, evt.xselectionrequest.property,
X11Symbols::getInstance()->xChangeProperty (display, evt.xselectionrequest.requestor, evt.xselectionrequest.property,
targetType, 8, PropModeReplace, targetType, 8, PropModeReplace,
reinterpret_cast<const unsigned char*> (textOrFiles.toRawUTF8()), reinterpret_cast<const unsigned char*> (textOrFiles.toRawUTF8()),
(int) textOrFiles.getNumBytesAsUTF8()); (int) textOrFiles.getNumBytesAsUTF8());
} }
X11Symbols::getInstance()->xSendEvent (getDisplay(), evt.xselectionrequest.requestor, True, 0, &s);
X11Symbols::getInstance()->xSendEvent (display, evt.xselectionrequest.requestor, True, 0, &s);
} }
void handleExternalDragAndDropStatus (const XClientMessageEvent& clientMsg) void handleExternalDragAndDropStatus (const XClientMessageEvent& clientMsg)
@@ -81,9 +83,11 @@ public:
canDrop = false; canDrop = false;
silentRect = {}; silentRect = {};
const auto& atoms = getAtoms();
if ((clientMsg.data.l[1] & 1) != 0 if ((clientMsg.data.l[1] & 1) != 0
&& ((Atom) clientMsg.data.l[4] == getAtoms().XdndActionCopy
|| (Atom) clientMsg.data.l[4] == getAtoms().XdndActionPrivate))
&& ((Atom) clientMsg.data.l[4] == atoms.XdndActionCopy
|| (Atom) clientMsg.data.l[4] == atoms.XdndActionPrivate))
{ {
if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle
silentRect.setBounds ((int) clientMsg.data.l[2] >> 16, (int) clientMsg.data.l[2] & 0xffff, silentRect.setBounds ((int) clientMsg.data.l[2] >> 16, (int) clientMsg.data.l[2] & 0xffff,
@@ -112,8 +116,11 @@ public:
void handleExternalDragMotionNotify() void handleExternalDragMotionNotify()
{ {
auto newTargetWindow = externalFindDragTargetWindow (X11Symbols::getInstance()->xRootWindow (getDisplay(),
X11Symbols::getInstance()->xDefaultScreen (getDisplay())));
auto* display = getDisplay();
auto newTargetWindow = externalFindDragTargetWindow (X11Symbols::getInstance()
->xRootWindow (display,
X11Symbols::getInstance()->xDefaultScreen (display)));
if (targetWindow != newTargetWindow) if (targetWindow != newTargetWindow)
{ {
@@ -153,13 +160,15 @@ public:
(int) clientMsg.data.l[2] & 0xffff)); (int) clientMsg.data.l[2] & 0xffff));
dropPos -= peer->getBounds().getPosition(); dropPos -= peer->getBounds().getPosition();
auto targetAction = getAtoms().XdndActionCopy;
const auto& atoms = getAtoms();
for (int i = numElementsInArray (getAtoms().allowedActions); --i >= 0;)
auto targetAction = atoms.XdndActionCopy;
for (int i = numElementsInArray (atoms.allowedActions); --i >= 0;)
{ {
if ((Atom) clientMsg.data.l[4] == getAtoms().allowedActions[i])
if ((Atom) clientMsg.data.l[4] == atoms.allowedActions[i])
{ {
targetAction = getAtoms().allowedActions[i];
targetAction = atoms.allowedActions[i];
break; break;
} }
} }
@@ -206,12 +215,14 @@ public:
return; return;
} }
const auto& atoms = getAtoms();
dragAndDropSourceWindow = (::Window) clientMsg.data.l[0]; dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
if ((clientMsg.data.l[1] & 1) != 0) if ((clientMsg.data.l[1] & 1) != 0)
{ {
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
XWindowSystemUtilities::GetXProperty prop (dragAndDropSourceWindow, getAtoms().XdndTypeList, 0, 0x8000000L, false, XA_ATOM);
XWindowSystemUtilities::GetXProperty prop (dragAndDropSourceWindow, atoms.XdndTypeList, 0, 0x8000000L, false, XA_ATOM);
if (prop.success && prop.actualType == XA_ATOM && prop.actualFormat == 32 && prop.numItems != 0) if (prop.success && prop.actualType == XA_ATOM && prop.actualFormat == 32 && prop.numItems != 0)
{ {
@@ -244,9 +255,9 @@ public:
} }
for (int i = 0; i < srcMimeTypeAtomList.size() && dragAndDropCurrentMimeType == 0; ++i) for (int i = 0; i < srcMimeTypeAtomList.size() && dragAndDropCurrentMimeType == 0; ++i)
for (int j = 0; j < numElementsInArray (getAtoms().allowedMimeTypes); ++j)
if (srcMimeTypeAtomList[i] == getAtoms().allowedMimeTypes[j])
dragAndDropCurrentMimeType = getAtoms().allowedMimeTypes[j];
for (int j = 0; j < numElementsInArray (atoms.allowedMimeTypes); ++j)
if (srcMimeTypeAtomList[i] == atoms.allowedMimeTypes[j])
dragAndDropCurrentMimeType = atoms.allowedMimeTypes[j];
handleDragAndDropPosition (clientMsg, peer); handleDragAndDropPosition (clientMsg, peer);
} }
@@ -326,22 +337,26 @@ public:
targetWindow = windowH; targetWindow = windowH;
completionCallback = std::move (cb); completionCallback = std::move (cb);
allowedTypes.add (XWindowSystemUtilities::Atoms::getCreating (getDisplay(), isText ? "text/plain" : "text/uri-list"));
auto* display = getDisplay();
allowedTypes.add (XWindowSystemUtilities::Atoms::getCreating (display, isText ? "text/plain" : "text/uri-list"));
auto pointerGrabMask = (unsigned int) (Button1MotionMask | ButtonReleaseMask); auto pointerGrabMask = (unsigned int) (Button1MotionMask | ButtonReleaseMask);
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
if (X11Symbols::getInstance()->xGrabPointer (getDisplay(), windowH, True, pointerGrabMask,
if (X11Symbols::getInstance()->xGrabPointer (display, windowH, True, pointerGrabMask,
GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess) GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess)
{ {
const auto& atoms = getAtoms();
// No other method of changing the pointer seems to work, this call is needed from this very context // No other method of changing the pointer seems to work, this call is needed from this very context
X11Symbols::getInstance()->xChangeActivePointerGrab (getDisplay(), pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime);
X11Symbols::getInstance()->xChangeActivePointerGrab (display, pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime);
X11Symbols::getInstance()->xSetSelectionOwner (getDisplay(), getAtoms().XdndSelection, windowH, CurrentTime);
X11Symbols::getInstance()->xSetSelectionOwner (display, atoms.XdndSelection, windowH, CurrentTime);
// save the available types to XdndTypeList // save the available types to XdndTypeList
X11Symbols::getInstance()->xChangeProperty (getDisplay(), windowH, getAtoms().XdndTypeList, XA_ATOM, 32, PropModeReplace,
X11Symbols::getInstance()->xChangeProperty (display, windowH, atoms.XdndTypeList, XA_ATOM, 32, PropModeReplace,
reinterpret_cast<const unsigned char*> (allowedTypes.getRawDataPointer()), allowedTypes.size()); reinterpret_cast<const unsigned char*> (allowedTypes.getRawDataPointer()), allowedTypes.size());
dragging = true; dragging = true;
@@ -358,32 +373,36 @@ public:
private: private:
//============================================================================== //==============================================================================
XWindowSystemUtilities::Atoms& getAtoms() const { return XWindowSystem::getInstance()->getAtoms(); }
::Display* getDisplay() const { return XWindowSystem::getInstance()->getDisplay(); }
const XWindowSystemUtilities::Atoms& getAtoms() const noexcept { return XWindowSystem::getInstance()->getAtoms(); }
::Display* getDisplay() const noexcept { return XWindowSystem::getInstance()->getDisplay(); }
//============================================================================== //==============================================================================
void sendDragAndDropMessage (XClientMessageEvent& msg) void sendDragAndDropMessage (XClientMessageEvent& msg)
{ {
auto* display = getDisplay();
msg.type = ClientMessage; msg.type = ClientMessage;
msg.display = getDisplay();
msg.display = display;
msg.window = dragAndDropSourceWindow; msg.window = dragAndDropSourceWindow;
msg.format = 32; msg.format = 32;
msg.data.l[0] = (long) windowH; msg.data.l[0] = (long) windowH;
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
X11Symbols::getInstance()->xSendEvent (getDisplay(), dragAndDropSourceWindow, False, 0, (XEvent*) &msg);
X11Symbols::getInstance()->xSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg);
} }
bool sendExternalDragAndDropMessage (XClientMessageEvent& msg) bool sendExternalDragAndDropMessage (XClientMessageEvent& msg)
{ {
auto* display = getDisplay();
msg.type = ClientMessage; msg.type = ClientMessage;
msg.display = getDisplay();
msg.display = display;
msg.window = targetWindow; msg.window = targetWindow;
msg.format = 32; msg.format = 32;
msg.data.l[0] = (long) windowH; msg.data.l[0] = (long) windowH;
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
return X11Symbols::getInstance()->xSendEvent (getDisplay(), targetWindow, False, 0, (XEvent*) &msg) != 0;
return X11Symbols::getInstance()->xSendEvent (display, targetWindow, False, 0, (XEvent*) &msg) != 0;
} }
void sendExternalDragAndDropDrop() void sendExternalDragAndDropDrop()
@@ -416,7 +435,9 @@ private:
XClientMessageEvent msg; XClientMessageEvent msg;
zerostruct (msg); zerostruct (msg);
msg.message_type = getAtoms().XdndPosition;
const auto& atoms = getAtoms();
msg.message_type = atoms.XdndPosition;
auto mousePos = Desktop::getInstance().getMousePosition(); auto mousePos = Desktop::getInstance().getMousePosition();
@@ -428,7 +449,7 @@ private:
msg.data.l[1] = 0; msg.data.l[1] = 0;
msg.data.l[2] = (mousePos.x << 16) | mousePos.y; msg.data.l[2] = (mousePos.x << 16) | mousePos.y;
msg.data.l[3] = CurrentTime; msg.data.l[3] = CurrentTime;
msg.data.l[4] = (long) getAtoms().XdndActionCopy; // this is all JUCE currently supports
msg.data.l[4] = (long) atoms.XdndActionCopy; // this is all JUCE currently supports
expectingStatus = sendExternalDragAndDropMessage (msg); expectingStatus = sendExternalDragAndDropMessage (msg);
} }
@@ -469,9 +490,11 @@ private:
if (dragAndDropSourceWindow != None && dragAndDropCurrentMimeType != None) if (dragAndDropSourceWindow != None && dragAndDropCurrentMimeType != None)
{ {
auto* display = getDisplay();
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
X11Symbols::getInstance()->xConvertSelection (getDisplay(), getAtoms().XdndSelection, dragAndDropCurrentMimeType,
XWindowSystemUtilities::Atoms::getCreating (getDisplay(), "JXSelectionWindowProperty"),
X11Symbols::getInstance()->xConvertSelection (display, getAtoms().XdndSelection, dragAndDropCurrentMimeType,
XWindowSystemUtilities::Atoms::getCreating (display, "JXSelectionWindowProperty"),
requestor, (::Time) clientMsg.data.l[2]); requestor, (::Time) clientMsg.data.l[2]);
} }
} }


+ 30
- 21
modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp View File

@@ -1328,9 +1328,11 @@ namespace ClipboardHelpers
int propertyFormat = 0; int propertyFormat = 0;
size_t numDataItems = 0; size_t numDataItems = 0;
if (evt.selection == XA_PRIMARY || evt.selection == XWindowSystem::getInstance()->getAtoms().clipboard)
const auto& atoms = XWindowSystem::getInstance()->getAtoms();
if (evt.selection == XA_PRIMARY || evt.selection == atoms.clipboard)
{ {
if (evt.target == XA_STRING || evt.target == XWindowSystem::getInstance()->getAtoms().utf8String)
if (evt.target == XA_STRING || evt.target == atoms.utf8String)
{ {
auto localContent = XWindowSystem::getInstance()->getLocalClipboardContent(); auto localContent = XWindowSystem::getInstance()->getLocalClipboardContent();
@@ -1340,15 +1342,17 @@ namespace ClipboardHelpers
localContent.copyToUTF8 (data, numDataItems); localContent.copyToUTF8 (data, numDataItems);
propertyFormat = 8; // bits/item propertyFormat = 8; // bits/item
} }
else if (evt.target == XWindowSystem::getInstance()->getAtoms().targets)
else if (evt.target == atoms.targets)
{ {
// another application wants to know what we are able to send // another application wants to know what we are able to send
numDataItems = 2; numDataItems = 2;
propertyFormat = 32; // atoms are 32-bit propertyFormat = 32; // atoms are 32-bit
data.calloc (numDataItems * 4); data.calloc (numDataItems * 4);
Atom* atoms = unalignedPointerCast<Atom*> (data.getData());
atoms[0] = XWindowSystem::getInstance()->getAtoms().utf8String;
atoms[1] = XA_STRING;
auto* dataAtoms = unalignedPointerCast<Atom*> (data.getData());
dataAtoms[0] = atoms.utf8String;
dataAtoms[1] = XA_STRING;
evt.target = XA_ATOM; evt.target = XA_ATOM;
} }
@@ -2588,14 +2592,12 @@ void XWindowSystem::copyTextToClipboard (const String& clipText)
{ {
localClipboardContent = clipText; localClipboardContent = clipText;
X11Symbols::getInstance()->xSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
X11Symbols::getInstance()->xSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
X11Symbols::getInstance()->xSetSelectionOwner (display, atoms.clipboard, juce_messageWindowHandle, CurrentTime); X11Symbols::getInstance()->xSetSelectionOwner (display, atoms.clipboard, juce_messageWindowHandle, CurrentTime);
} }
String XWindowSystem::getTextFromClipboard() const String XWindowSystem::getTextFromClipboard() const
{ {
String content;
/* 1) try to read from the "CLIPBOARD" selection first (the "high /* 1) try to read from the "CLIPBOARD" selection first (the "high
level" clipboard that is supposed to be filled by ctrl-C level" clipboard that is supposed to be filled by ctrl-C
etc). When a clipboard manager is running, the content of this etc). When a clipboard manager is running, the content of this
@@ -2605,22 +2607,29 @@ String XWindowSystem::getTextFromClipboard() const
2) and then try to read from "PRIMARY" selection (the "legacy" selection 2) and then try to read from "PRIMARY" selection (the "legacy" selection
filled by good old x11 apps such as xterm) filled by good old x11 apps such as xterm)
*/ */
auto selection = XA_PRIMARY;
Window selectionOwner = None;
if ((selectionOwner = X11Symbols::getInstance()->xGetSelectionOwner (display, selection)) == None)
auto getContentForSelection = [this] (Atom selectionAtom) -> String
{ {
selection = atoms.clipboard;
selectionOwner = X11Symbols::getInstance()->xGetSelectionOwner (display, selection);
}
auto selectionOwner = X11Symbols::getInstance()->xGetSelectionOwner (display, selectionAtom);
if (selectionOwner == None)
return {};
if (selectionOwner != None)
{
if (selectionOwner == juce_messageWindowHandle) if (selectionOwner == juce_messageWindowHandle)
content = localClipboardContent;
else if (! ClipboardHelpers::requestSelectionContent (display, content, selection, atoms.utf8String))
ClipboardHelpers::requestSelectionContent (display, content, selection, XA_STRING);
}
return localClipboardContent;
String content;
if (! ClipboardHelpers::requestSelectionContent (display, content, selectionAtom, atoms.utf8String))
ClipboardHelpers::requestSelectionContent (display, content, selectionAtom, XA_STRING);
return content;
};
auto content = getContentForSelection (atoms.clipboard);
if (content.isEmpty())
content = getContentForSelection (XA_PRIMARY);
return content; return content;
} }


+ 4
- 5
modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h View File

@@ -158,13 +158,12 @@ public:
void copyTextToClipboard (const String&); void copyTextToClipboard (const String&);
String getTextFromClipboard() const; String getTextFromClipboard() const;
String getLocalClipboardContent() const noexcept { return localClipboardContent; }
String getLocalClipboardContent() const { return localClipboardContent; }
::Display* getDisplay() noexcept { return display; }
const XWindowSystemUtilities::Atoms& getAtoms() const noexcept { return atoms; }
::Display* getDisplay() { return display; }
XWindowSystemUtilities::Atoms& getAtoms() { return atoms; }
bool isX11Available() const noexcept { return xIsAvailable; }
bool isX11Available() const noexcept { return xIsAvailable; }
//============================================================================== //==============================================================================
void handleWindowMessage (LinuxComponentPeer*, XEvent&) const; void handleWindowMessage (LinuxComponentPeer*, XEvent&) const;


Loading…
Cancel
Save