mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Linux: Removed X11-specific code from LinuxComponentPeer
This commit is contained in:
parent
1c1f74562f
commit
74ca3b44c4
17 changed files with 4904 additions and 4825 deletions
|
|
@ -214,8 +214,6 @@ struct SharedMessageThread : public Thread
|
|||
|
||||
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
|
||||
|
||||
ScopedXDisplay xDisplay;
|
||||
|
||||
while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
|
||||
{}
|
||||
}
|
||||
|
|
@ -1088,7 +1086,7 @@ public:
|
|||
#elif JUCE_LINUX
|
||||
addToDesktop (0, args.ptr);
|
||||
hostWindow = (Window) args.ptr;
|
||||
X11Symbols::getInstance()->xReparentWindow (display.display, (Window) getWindowHandle(), hostWindow, 0, 0);
|
||||
X11Symbols::getInstance()->xReparentWindow (display, (Window) getWindowHandle(), hostWindow, 0, 0);
|
||||
#else
|
||||
hostWindow = attachComponentToWindowRefVST (this, args.ptr, wrapper.useNSView);
|
||||
#endif
|
||||
|
|
@ -1197,7 +1195,7 @@ public:
|
|||
if (auto* peer = ed->getPeer())
|
||||
scale *= (float) peer->getPlatformScaleFactor();
|
||||
|
||||
X11Symbols::getInstance()->xResizeWindow (display.display, (Window) getWindowHandle(),
|
||||
X11Symbols::getInstance()->xResizeWindow (display, (Window) getWindowHandle(),
|
||||
static_cast<unsigned int> (roundToInt (pos.getWidth() * scale)),
|
||||
static_cast<unsigned int> (roundToInt (pos.getHeight() * scale)));
|
||||
#endif
|
||||
|
|
@ -1366,7 +1364,7 @@ public:
|
|||
#if JUCE_MAC
|
||||
void* hostWindow = nullptr;
|
||||
#elif JUCE_LINUX
|
||||
ScopedXDisplay display;
|
||||
::Display* display = XWindowSystem::getInstance()->getDisplay();
|
||||
Window hostWindow = {};
|
||||
#elif JUCE_WINDOWS
|
||||
HWND hostWindow = {};
|
||||
|
|
|
|||
|
|
@ -237,10 +237,8 @@ namespace
|
|||
Window* childWindows;
|
||||
unsigned int numChildren = 0;
|
||||
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
X11Symbols::getInstance()->xQueryTree (xDisplay.display, windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
|
||||
}
|
||||
X11Symbols::getInstance()->xQueryTree (XWindowSystem::getInstance()->getDisplay(),
|
||||
windowToCheck, &rootWindow, &parentWindow, &childWindows, &numChildren);
|
||||
|
||||
if (numChildren > 0)
|
||||
return childWindows [0];
|
||||
|
|
@ -2743,7 +2741,6 @@ public:
|
|||
{
|
||||
#if JUCE_LINUX
|
||||
pluginWindow = None;
|
||||
display = XWindowSystem::getInstance()->displayRef();
|
||||
ignoreUnused (pluginRefusesToResize, alreadyInside);
|
||||
#elif JUCE_MAC
|
||||
ignoreUnused (recursiveResize, pluginRefusesToResize, alreadyInside);
|
||||
|
|
@ -2786,10 +2783,6 @@ public:
|
|||
#endif
|
||||
cocoaWrapper.reset();
|
||||
#else
|
||||
#if JUCE_LINUX
|
||||
display = XWindowSystem::getInstance()->displayUnref();
|
||||
#endif
|
||||
|
||||
removeScaleFactorListeners();
|
||||
#endif
|
||||
|
||||
|
|
@ -3403,15 +3396,14 @@ private:
|
|||
#if ! JUCE_MAC
|
||||
bool pluginRespondsToDPIChanges = false;
|
||||
float nativeScaleFactor = 1.0f;
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
HWND pluginHWND = {};
|
||||
void* originalWndProc = {};
|
||||
int sizeCheckCount = 0;
|
||||
#elif JUCE_LINUX
|
||||
::Display* display;
|
||||
Window pluginWindow;
|
||||
#if JUCE_WINDOWS
|
||||
HWND pluginHWND = {};
|
||||
void* originalWndProc = {};
|
||||
int sizeCheckCount = 0;
|
||||
#elif JUCE_LINUX
|
||||
::Display* display = XWindowSystem::getInstance()->getDisplay();
|
||||
Window pluginWindow = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -250,13 +250,13 @@ namespace juce
|
|||
#include "native/juce_win32_FileChooser.cpp"
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include "native/juce_linux_X11Symbols.cpp"
|
||||
#include "native/juce_linux_X11.cpp"
|
||||
#include "native/juce_linux_X11_Clipboard.cpp"
|
||||
#include "native/x11/juce_linux_X11_Symbols.cpp"
|
||||
#include "native/x11/juce_linux_X11_DragAndDrop.cpp"
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant")
|
||||
|
||||
#include "native/juce_linux_X11_Windowing.cpp"
|
||||
#include "native/juce_linux_Windowing.cpp"
|
||||
#include "native/x11/juce_linux_XWindowSystem.cpp"
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
|
|
|
|||
|
|
@ -288,8 +288,6 @@ namespace juce
|
|||
#include "mouse/juce_LassoComponent.h"
|
||||
|
||||
#if JUCE_LINUX
|
||||
#include "native/juce_linux_X11.h"
|
||||
|
||||
#if JUCE_GUI_BASICS_INCLUDE_XHEADERS
|
||||
// If you're missing these headers, you need to install the libx11-dev package
|
||||
#include <X11/Xlib.h>
|
||||
|
|
@ -332,7 +330,8 @@ namespace juce
|
|||
#undef SIZEOF
|
||||
#undef KeyPress
|
||||
|
||||
#include "native/juce_linux_X11Symbols.h"
|
||||
#include "native/x11/juce_linux_XWindowSystem.h"
|
||||
#include "native/x11/juce_linux_X11_Symbols.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
|||
682
modules/juce_gui_basics/native/juce_linux_Windowing.cpp
Normal file
682
modules/juce_gui_basics/native/juce_linux_Windowing.cpp
Normal file
|
|
@ -0,0 +1,682 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
static int numAlwaysOnTopPeers = 0;
|
||||
bool juce_areThereAnyAlwaysOnTopWindows() { return numAlwaysOnTopPeers > 0; }
|
||||
|
||||
//==============================================================================
|
||||
template<typename WindowHandleType>
|
||||
class LinuxComponentPeer : public ComponentPeer
|
||||
{
|
||||
public:
|
||||
LinuxComponentPeer (Component& comp, int windowStyleFlags, WindowHandleType parentToAddTo)
|
||||
: ComponentPeer (comp, windowStyleFlags),
|
||||
isAlwaysOnTop (comp.isAlwaysOnTop())
|
||||
{
|
||||
// it's dangerous to create a window on a thread other than the message thread..
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
if (isAlwaysOnTop)
|
||||
++numAlwaysOnTopPeers;
|
||||
|
||||
repainter = std::make_unique<LinuxRepaintManager> (*this);
|
||||
|
||||
windowH = XWindowSystem::getInstance()->createWindow (parentToAddTo, this);
|
||||
parentWindow = parentToAddTo;
|
||||
|
||||
setTitle (component.getName());
|
||||
|
||||
getNativeRealtimeModifiers = []() -> ModifierKeys { return XWindowSystem::getInstance()->getNativeRealtimeModifiers(); };
|
||||
}
|
||||
|
||||
~LinuxComponentPeer() override
|
||||
{
|
||||
// it's dangerous to delete a window on a thread other than the message thread..
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
repainter = nullptr;
|
||||
XWindowSystem::getInstance()->destroyWindow (windowH);
|
||||
|
||||
if (isAlwaysOnTop)
|
||||
--numAlwaysOnTopPeers;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void* getNativeHandle() const override
|
||||
{
|
||||
return (void*) windowH;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) override
|
||||
{
|
||||
bounds = newBounds.withSize (jmax (1, newBounds.getWidth()),
|
||||
jmax (1, newBounds.getHeight()));
|
||||
|
||||
updateScaleFactorFromNewBounds (bounds, false);
|
||||
|
||||
auto physicalBounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds)
|
||||
: bounds * currentScaleFactor);
|
||||
|
||||
WeakReference<Component> deletionChecker (&component);
|
||||
|
||||
XWindowSystem::getInstance()->setBounds (windowH, physicalBounds, isNowFullScreen);
|
||||
|
||||
fullScreen = isNowFullScreen;
|
||||
|
||||
if (deletionChecker != nullptr)
|
||||
{
|
||||
updateBorderSize();
|
||||
handleMovedOrResized();
|
||||
}
|
||||
}
|
||||
|
||||
Point<int> getScreenPosition (bool physical) const
|
||||
{
|
||||
auto parentPosition = XWindowSystem::getInstance()->getParentScreenPosition();
|
||||
|
||||
auto screenBounds = (parentWindow == 0 ? bounds
|
||||
: bounds.translated (parentPosition.x, parentPosition.y));
|
||||
|
||||
if (physical)
|
||||
return Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft());
|
||||
|
||||
return screenBounds.getTopLeft();
|
||||
}
|
||||
|
||||
Rectangle<int> getBounds() const override
|
||||
{
|
||||
return bounds;
|
||||
}
|
||||
|
||||
BorderSize<int> getFrameSize() const override
|
||||
{
|
||||
return windowBorder;
|
||||
}
|
||||
|
||||
using ComponentPeer::localToGlobal;
|
||||
Point<float> localToGlobal (Point<float> relativePosition) override
|
||||
{
|
||||
return relativePosition + getScreenPosition (false).toFloat();
|
||||
}
|
||||
|
||||
using ComponentPeer::globalToLocal;
|
||||
Point<float> globalToLocal (Point<float> screenPosition) override
|
||||
{
|
||||
return screenPosition - getScreenPosition (false).toFloat();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringArray getAvailableRenderingEngines() override
|
||||
{
|
||||
return { "Software Renderer" };
|
||||
}
|
||||
|
||||
void setVisible (bool shouldBeVisible) override
|
||||
{
|
||||
XWindowSystem::getInstance()->setVisible (windowH, shouldBeVisible);
|
||||
}
|
||||
|
||||
void setTitle (const String& title) override
|
||||
{
|
||||
XWindowSystem::getInstance()->setTitle (windowH, title);
|
||||
}
|
||||
|
||||
void setMinimised (bool shouldBeMinimised) override
|
||||
{
|
||||
if (shouldBeMinimised)
|
||||
XWindowSystem::getInstance()->setMinimised (windowH, shouldBeMinimised);
|
||||
else
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
bool isMinimised() const override
|
||||
{
|
||||
return XWindowSystem::getInstance()->isMinimised (windowH);
|
||||
}
|
||||
|
||||
void setFullScreen (bool shouldBeFullScreen) override
|
||||
{
|
||||
auto r = lastNonFullscreenBounds; // (get a copy of this before de-minimising)
|
||||
|
||||
setMinimised (false);
|
||||
|
||||
if (fullScreen != shouldBeFullScreen)
|
||||
{
|
||||
if (shouldBeFullScreen)
|
||||
r = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
|
||||
|
||||
if (! r.isEmpty())
|
||||
setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
|
||||
|
||||
component.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool isFullScreen() const override
|
||||
{
|
||||
return fullScreen;
|
||||
}
|
||||
|
||||
bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
|
||||
{
|
||||
if (! bounds.withZeroOrigin().contains (localPos))
|
||||
return false;
|
||||
|
||||
for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
|
||||
{
|
||||
auto* c = Desktop::getInstance().getComponent (i);
|
||||
|
||||
if (c == &component)
|
||||
break;
|
||||
|
||||
if (! c->isVisible())
|
||||
continue;
|
||||
|
||||
if (auto* peer = c->getPeer())
|
||||
if (peer->contains (localPos + bounds.getPosition() - peer->getBounds().getPosition(), true))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trueIfInAChildWindow)
|
||||
return true;
|
||||
|
||||
return XWindowSystem::getInstance()->contains (windowH, localPos * currentScaleFactor);
|
||||
}
|
||||
|
||||
void toFront (bool makeActive) override
|
||||
{
|
||||
if (makeActive)
|
||||
{
|
||||
setVisible (true);
|
||||
grabFocus();
|
||||
}
|
||||
|
||||
XWindowSystem::getInstance()->toFront (windowH, makeActive);
|
||||
handleBroughtToFront();
|
||||
}
|
||||
|
||||
void toBehind (ComponentPeer* other) override
|
||||
{
|
||||
if (auto* otherPeer = dynamic_cast<LinuxComponentPeer*> (other))
|
||||
{
|
||||
if (otherPeer->styleFlags & windowIsTemporary)
|
||||
return;
|
||||
|
||||
setMinimised (false);
|
||||
XWindowSystem::getInstance()->toBehind (windowH, otherPeer->windowH);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // wrong type of window?
|
||||
}
|
||||
}
|
||||
|
||||
bool isFocused() const override
|
||||
{
|
||||
return XWindowSystem::getInstance()->isFocused (windowH);
|
||||
}
|
||||
|
||||
void grabFocus() override
|
||||
{
|
||||
if (XWindowSystem::getInstance()->grabFocus (windowH))
|
||||
isActiveApplication = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void repaint (const Rectangle<int>& area) override
|
||||
{
|
||||
repainter->repaint (area.getIntersection (bounds.withZeroOrigin()));
|
||||
}
|
||||
|
||||
void performAnyPendingRepaintsNow() override
|
||||
{
|
||||
repainter->performAnyPendingRepaintsNow();
|
||||
}
|
||||
|
||||
void setIcon (const Image& newIcon) override
|
||||
{
|
||||
XWindowSystem::getInstance()->setIcon (windowH, newIcon);
|
||||
}
|
||||
|
||||
double getPlatformScaleFactor() const noexcept override
|
||||
{
|
||||
return currentScaleFactor;
|
||||
}
|
||||
|
||||
void setAlpha (float) override {}
|
||||
bool setAlwaysOnTop (bool) override { return false; }
|
||||
void textInputRequired (Point<int>, TextInputTarget&) override {}
|
||||
|
||||
//==============================================================================
|
||||
void addOpenGLRepaintListener (Component* dummy)
|
||||
{
|
||||
if (dummy != nullptr)
|
||||
glRepaintListeners.addIfNotAlreadyThere (dummy);
|
||||
}
|
||||
|
||||
void removeOpenGLRepaintListener (Component* dummy)
|
||||
{
|
||||
if (dummy != nullptr)
|
||||
glRepaintListeners.removeAllInstancesOf (dummy);
|
||||
}
|
||||
|
||||
void repaintOpenGLContexts()
|
||||
{
|
||||
for (auto* c : glRepaintListeners)
|
||||
c->handleCommandMessage (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
WindowHandleType getParentWindow() { return parentWindow; }
|
||||
void setParentWindow (WindowHandleType newParent) { parentWindow = newParent; }
|
||||
|
||||
//==============================================================================
|
||||
void updateWindowBounds()
|
||||
{
|
||||
jassert (windowH != 0);
|
||||
if (windowH != 0)
|
||||
{
|
||||
auto physicalBounds = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow);
|
||||
|
||||
updateScaleFactorFromNewBounds (physicalBounds, true);
|
||||
|
||||
bounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds)
|
||||
: physicalBounds / currentScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
void updateBorderSize()
|
||||
{
|
||||
if ((styleFlags & windowHasTitleBar) == 0)
|
||||
windowBorder = {};
|
||||
else if (windowBorder.getTopAndBottom() == 0 && windowBorder.getLeftAndRight() == 0)
|
||||
windowBorder = XWindowSystem::getInstance()->getBorderSize (windowH);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool isActiveApplication;
|
||||
bool focused = false;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class LinuxRepaintManager : public Timer
|
||||
{
|
||||
public:
|
||||
LinuxRepaintManager (LinuxComponentPeer& p) : peer (p) {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (XWindowSystem::getInstance()->getNumPaintsPending (peer.windowH) > 0)
|
||||
return;
|
||||
|
||||
if (! regionsNeedingRepaint.isEmpty())
|
||||
{
|
||||
stopTimer();
|
||||
performAnyPendingRepaintsNow();
|
||||
}
|
||||
else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
|
||||
{
|
||||
stopTimer();
|
||||
image = Image();
|
||||
}
|
||||
}
|
||||
|
||||
void repaint (Rectangle<int> area)
|
||||
{
|
||||
if (! isTimerRunning())
|
||||
startTimer (repaintTimerPeriod);
|
||||
|
||||
regionsNeedingRepaint.add (area * peer.currentScaleFactor);
|
||||
}
|
||||
|
||||
void performAnyPendingRepaintsNow()
|
||||
{
|
||||
if (XWindowSystem::getInstance()->getNumPaintsPending (peer.windowH) > 0)
|
||||
{
|
||||
startTimer (repaintTimerPeriod);
|
||||
return;
|
||||
}
|
||||
|
||||
auto originalRepaintRegion = regionsNeedingRepaint;
|
||||
regionsNeedingRepaint.clear();
|
||||
auto totalArea = originalRepaintRegion.getBounds();
|
||||
|
||||
if (! totalArea.isEmpty())
|
||||
{
|
||||
if (image.isNull() || image.getWidth() < totalArea.getWidth()
|
||||
|| image.getHeight() < totalArea.getHeight())
|
||||
{
|
||||
image = XWindowSystem::getInstance()->createImage (totalArea.getWidth(), totalArea.getHeight(),
|
||||
useARGBImagesForRendering);
|
||||
}
|
||||
|
||||
startTimer (repaintTimerPeriod);
|
||||
|
||||
RectangleList<int> adjustedList (originalRepaintRegion);
|
||||
adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY());
|
||||
|
||||
if (XWindowSystem::getInstance()->canUseARGBImages())
|
||||
for (auto& i : originalRepaintRegion)
|
||||
image.clear (i - totalArea.getPosition());
|
||||
|
||||
{
|
||||
auto context = peer.getComponent().getLookAndFeel()
|
||||
.createGraphicsContext (image, -totalArea.getPosition(), adjustedList);
|
||||
|
||||
context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor));
|
||||
peer.handlePaint (*context);
|
||||
}
|
||||
|
||||
for (auto& i : originalRepaintRegion)
|
||||
XWindowSystem::getInstance()->blitToWindow (peer.windowH, image, i, totalArea);
|
||||
}
|
||||
|
||||
lastTimeImageUsed = Time::getApproximateMillisecondCounter();
|
||||
startTimer (repaintTimerPeriod);
|
||||
}
|
||||
|
||||
private:
|
||||
enum { repaintTimerPeriod = 1000 / 100 };
|
||||
|
||||
LinuxComponentPeer& peer;
|
||||
Image image;
|
||||
uint32 lastTimeImageUsed = 0;
|
||||
RectangleList<int> regionsNeedingRepaint;
|
||||
|
||||
bool useARGBImagesForRendering = XWindowSystem::getInstance()->canUseARGBImages();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
|
||||
{
|
||||
Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
|
||||
|
||||
auto newScaleFactor = Desktop::getInstance().getDisplays().findDisplayForRect (newBounds.translated (translation.x, translation.y), isPhysical).scale
|
||||
/ Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
|
||||
{
|
||||
currentScaleFactor = newScaleFactor;
|
||||
scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<LinuxRepaintManager> repainter;
|
||||
|
||||
WindowHandleType windowH = {}, parentWindow = {}, keyProxy = {};
|
||||
Rectangle<int> bounds;
|
||||
BorderSize<int> windowBorder;
|
||||
bool fullScreen = false, isAlwaysOnTop = false;
|
||||
double currentScaleFactor = 1.0;
|
||||
Array<Component*> glRepaintListeners;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
|
||||
};
|
||||
|
||||
template<typename WindowHandleType>
|
||||
bool LinuxComponentPeer<WindowHandleType>::isActiveApplication = false;
|
||||
|
||||
//==============================================================================
|
||||
ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
|
||||
{
|
||||
return new LinuxComponentPeer<::Window> (*this, styleFlags, (::Window) nativeWindowToAttachTo);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return LinuxComponentPeer<::Window>::isActiveApplication; }
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
|
||||
JUCE_API void JUCE_CALLTYPE Process::hide() {}
|
||||
|
||||
//==============================================================================
|
||||
void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
|
||||
{
|
||||
if (enableOrDisable)
|
||||
comp->setBounds (getDisplays().findDisplayForRect (comp->getScreenBounds()).totalArea);
|
||||
}
|
||||
|
||||
void Displays::findDisplays (float masterScale)
|
||||
{
|
||||
displays = XWindowSystem::getInstance()->findDisplays (masterScale);
|
||||
|
||||
if (! displays.isEmpty())
|
||||
updateToLogical();
|
||||
}
|
||||
|
||||
bool Desktop::canUseSemiTransparentWindows() noexcept
|
||||
{
|
||||
return XWindowSystem::getInstance()->canUseSemiTransparentWindows();
|
||||
}
|
||||
|
||||
static bool screenSaverAllowed = true;
|
||||
|
||||
void Desktop::setScreenSaverEnabled (bool isEnabled)
|
||||
{
|
||||
if (screenSaverAllowed != isEnabled)
|
||||
{
|
||||
screenSaverAllowed = isEnabled;
|
||||
XWindowSystem::getInstance()->setScreenSaverEnabled (screenSaverAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
bool Desktop::isScreenSaverEnabled()
|
||||
{
|
||||
return screenSaverAllowed;
|
||||
}
|
||||
|
||||
double Desktop::getDefaultMasterScale() { return 1.0; }
|
||||
|
||||
Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; }
|
||||
void Desktop::allowedOrientationsChanged() {}
|
||||
|
||||
//==============================================================================
|
||||
bool MouseInputSource::SourceList::addSource()
|
||||
{
|
||||
if (sources.isEmpty())
|
||||
{
|
||||
addSource (0, MouseInputSource::InputSourceType::mouse);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MouseInputSource::SourceList::canUseTouch()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Point<float> MouseInputSource::getCurrentRawMousePosition()
|
||||
{
|
||||
return Desktop::getInstance().getDisplays().physicalToLogical (XWindowSystem::getInstance()->getCurrentMousePosition());
|
||||
}
|
||||
|
||||
void MouseInputSource::setRawMousePosition (Point<float> newPosition)
|
||||
{
|
||||
XWindowSystem::getInstance()->setMousePosition (Desktop::getInstance().getDisplays().logicalToPhysical (newPosition));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void* CustomMouseCursorInfo::create() const
|
||||
{
|
||||
return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image, hotspot);
|
||||
}
|
||||
|
||||
void MouseCursor::deleteMouseCursor (void* cursorHandle, bool)
|
||||
{
|
||||
if (cursorHandle != nullptr)
|
||||
XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle);
|
||||
}
|
||||
|
||||
void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type)
|
||||
{
|
||||
return XWindowSystem::getInstance()->createStandardMouseCursor (type);
|
||||
}
|
||||
|
||||
void MouseCursor::showInWindow (ComponentPeer* peer) const
|
||||
{
|
||||
if (peer != nullptr)
|
||||
XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), getHandle());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template<typename WindowHandleType>
|
||||
static LinuxComponentPeer<WindowHandleType>* getPeerForDragEvent (Component* sourceComp)
|
||||
{
|
||||
if (sourceComp == nullptr)
|
||||
if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
|
||||
sourceComp = draggingSource->getComponentUnderMouse();
|
||||
|
||||
if (sourceComp != nullptr)
|
||||
if (auto* lp = dynamic_cast<LinuxComponentPeer<::Window>*> (sourceComp->getPeer()))
|
||||
return lp;
|
||||
|
||||
jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
||||
Component* sourceComp, std::function<void()> callback)
|
||||
{
|
||||
if (files.isEmpty())
|
||||
return false;
|
||||
|
||||
if (auto* peer = getPeerForDragEvent<::Window> (sourceComp))
|
||||
return XWindowSystem::getInstance()->externalDragFileInit (peer, files, canMoveFiles, std::move (callback));
|
||||
|
||||
// This method must be called in response to a component's mouseDown or mouseDrag event!
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
|
||||
std::function<void()> callback)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return false;
|
||||
|
||||
if (auto* peer = getPeerForDragEvent<::Window> (sourceComp))
|
||||
return XWindowSystem::getInstance()->externalDragTextInit (peer, text, std::move (callback));
|
||||
|
||||
// This method must be called in response to a component's mouseDown or mouseDrag event!
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SystemClipboard::copyTextToClipboard (const String& clipText)
|
||||
{
|
||||
XWindowSystem::getInstance()->copyTextToClipboard (clipText);
|
||||
}
|
||||
|
||||
String SystemClipboard::getTextFromClipboard()
|
||||
{
|
||||
return XWindowSystem::getInstance()->getTextFromClipboard();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPress::isKeyCurrentlyDown (int keyCode)
|
||||
{
|
||||
return XWindowSystem::getInstance()->isKeyCurrentlyDown (keyCode);
|
||||
}
|
||||
|
||||
void LookAndFeel::playAlertSound()
|
||||
{
|
||||
std::cout << "\a" << std::flush;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
|
||||
const String& title, const String& message,
|
||||
Component*)
|
||||
{
|
||||
AlertWindow::showMessageBox (iconType, title, message);
|
||||
}
|
||||
#endif
|
||||
|
||||
void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
|
||||
const String& title, const String& message,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (iconType, title, message, {}, associatedComponent, callback);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
|
||||
const String& title, const String& message,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
return AlertWindow::showOkCancelBox (iconType, title, message, {}, {}, associatedComponent, callback);
|
||||
}
|
||||
|
||||
int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
|
||||
const String& title, const String& message,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
return AlertWindow::showYesNoCancelBox (iconType, title, message, {}, {}, {},
|
||||
associatedComponent, callback);
|
||||
}
|
||||
|
||||
int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType,
|
||||
const String& title, const String& message,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
return AlertWindow::showOkCancelBox (iconType, title, message, TRANS ("Yes"), TRANS ("No"),
|
||||
associatedComponent, callback);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Image juce_createIconForFile (const File&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
|
||||
{
|
||||
if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer<::Window>*> (peer))
|
||||
linuxPeer->addOpenGLRepaintListener (dummy);
|
||||
}
|
||||
|
||||
void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
|
||||
{
|
||||
if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer<::Window>*> (peer))
|
||||
linuxPeer->removeOpenGLRepaintListener (dummy);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE 6 technical preview.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
You may use this code under the terms of the GPL v3
|
||||
(see www.gnu.org/licenses).
|
||||
|
||||
For this technical preview, this file is not subject to commercial licensing.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
using WindowMessageReceiveCallback = void (*) (XEvent&);
|
||||
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
|
||||
|
||||
using SelectionRequestCallback = void (*) (XSelectionRequestEvent&);
|
||||
SelectionRequestCallback handleSelectionRequest = nullptr;
|
||||
|
||||
::Window juce_messageWindowHandle;
|
||||
XContext windowHandleXContext;
|
||||
|
||||
//==============================================================================
|
||||
namespace X11ErrorHandling
|
||||
{
|
||||
static XErrorHandler oldErrorHandler = {};
|
||||
static XIOErrorHandler oldIOErrorHandler = {};
|
||||
|
||||
//==============================================================================
|
||||
// Usually happens when client-server connection is broken
|
||||
int ioErrorHandler (::Display*)
|
||||
{
|
||||
DBG ("ERROR: connection to X server broken.. terminating.");
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp())
|
||||
MessageManager::getInstance()->stopDispatchLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int errorHandler (::Display* display, XErrorEvent* event)
|
||||
{
|
||||
ignoreUnused (display, event);
|
||||
|
||||
#if JUCE_DEBUG_XERRORS
|
||||
char errorStr[64] = { 0 };
|
||||
char requestStr[64] = { 0 };
|
||||
|
||||
X11Symbols::getInstance()->xGetErrorText (display, event->error_code, errorStr, 64);
|
||||
X11Symbols::getInstance()->xGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
|
||||
|
||||
DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void installXErrorHandlers()
|
||||
{
|
||||
oldIOErrorHandler = X11Symbols::getInstance()->xSetIOErrorHandler (ioErrorHandler);
|
||||
oldErrorHandler = X11Symbols::getInstance()->xSetErrorHandler (errorHandler);
|
||||
}
|
||||
|
||||
void removeXErrorHandlers()
|
||||
{
|
||||
X11Symbols::getInstance()->xSetIOErrorHandler (oldIOErrorHandler);
|
||||
oldIOErrorHandler = {};
|
||||
|
||||
X11Symbols::getInstance()->xSetErrorHandler (oldErrorHandler);
|
||||
oldErrorHandler = {};
|
||||
}
|
||||
}
|
||||
|
||||
XWindowSystem::XWindowSystem() noexcept
|
||||
{
|
||||
xIsAvailable = X11Symbols::getInstance()->areXFunctionsAvailable();
|
||||
|
||||
if (JUCEApplicationBase::isStandaloneApp() && xIsAvailable)
|
||||
{
|
||||
// Initialise xlib for multiple thread support
|
||||
static bool initThreadCalled = false;
|
||||
|
||||
if (! initThreadCalled)
|
||||
{
|
||||
if (! X11Symbols::getInstance()->xInitThreads())
|
||||
{
|
||||
// This is fatal! Print error and closedown
|
||||
Logger::outputDebugString ("Failed to initialise xlib thread support.");
|
||||
Process::terminate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
initThreadCalled = true;
|
||||
}
|
||||
|
||||
X11ErrorHandling::installXErrorHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
XWindowSystem::~XWindowSystem() noexcept
|
||||
{
|
||||
if (JUCEApplicationBase::isStandaloneApp() && xIsAvailable)
|
||||
{
|
||||
X11ErrorHandling::removeXErrorHandlers();
|
||||
X11Symbols::deleteInstance();
|
||||
}
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
::Display* XWindowSystem::displayRef() noexcept
|
||||
{
|
||||
if (xIsAvailable && ++displayCount == 1)
|
||||
{
|
||||
jassert (display == nullptr);
|
||||
|
||||
String displayName (getenv ("DISPLAY"));
|
||||
|
||||
if (displayName.isEmpty())
|
||||
displayName = ":0.0";
|
||||
|
||||
// it seems that on some systems XOpenDisplay will occasionally
|
||||
// fail the first time, but succeed on a second attempt..
|
||||
for (int retries = 2; --retries >= 0;)
|
||||
{
|
||||
display = X11Symbols::getInstance()->xOpenDisplay (displayName.toUTF8());
|
||||
|
||||
if (display != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
initialiseXDisplay();
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
::Display* XWindowSystem::displayUnref() noexcept
|
||||
{
|
||||
if (xIsAvailable)
|
||||
{
|
||||
jassert (display != nullptr);
|
||||
jassert (displayCount.get() > 0);
|
||||
|
||||
if (--displayCount == 0)
|
||||
{
|
||||
destroyXDisplay();
|
||||
X11Symbols::getInstance()->xCloseDisplay (display);
|
||||
display = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
void XWindowSystem::initialiseXDisplay() noexcept
|
||||
{
|
||||
if (xIsAvailable)
|
||||
{
|
||||
// This is fatal! Print error and closedown
|
||||
if (display == nullptr)
|
||||
{
|
||||
Logger::outputDebugString ("Failed to connect to the X Server.");
|
||||
Process::terminate();
|
||||
}
|
||||
|
||||
// Create a context to store user data associated with Windows we create
|
||||
windowHandleXContext = (XContext) X11Symbols::getInstance()->xrmUniqueQuark();
|
||||
|
||||
// We're only interested in client messages for this window, which are always sent
|
||||
XSetWindowAttributes swa;
|
||||
swa.event_mask = NoEventMask;
|
||||
|
||||
// Create our message window (this will never be mapped)
|
||||
auto screen = X11Symbols::getInstance()->xDefaultScreen (display);
|
||||
juce_messageWindowHandle = X11Symbols::getInstance()->xCreateWindow (display, X11Symbols::getInstance()->xRootWindow (display, screen),
|
||||
0, 0, 1, 1, 0, 0, InputOnly,
|
||||
X11Symbols::getInstance()->xDefaultVisual (display, screen),
|
||||
CWEventMask, &swa);
|
||||
|
||||
X11Symbols::getInstance()->xSync (display, False);
|
||||
|
||||
// Setup input event handler
|
||||
LinuxEventLoop::registerFdCallback (X11Symbols::getInstance()->xConnectionNumber (display),
|
||||
[this](int)
|
||||
{
|
||||
do
|
||||
{
|
||||
XEvent evt;
|
||||
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
|
||||
if (! X11Symbols::getInstance()->xPending (display))
|
||||
return;
|
||||
|
||||
X11Symbols::getInstance()->xNextEvent (display, &evt);
|
||||
}
|
||||
|
||||
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
|
||||
&& handleSelectionRequest != nullptr)
|
||||
{
|
||||
handleSelectionRequest (evt.xselectionrequest);
|
||||
}
|
||||
else if (evt.xany.window != juce_messageWindowHandle
|
||||
&& dispatchWindowMessage != nullptr)
|
||||
{
|
||||
dispatchWindowMessage (evt);
|
||||
}
|
||||
|
||||
} while (display != nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void XWindowSystem::destroyXDisplay() noexcept
|
||||
{
|
||||
if (xIsAvailable)
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
|
||||
X11Symbols::getInstance()->xDestroyWindow (display, juce_messageWindowHandle);
|
||||
juce_messageWindowHandle = 0;
|
||||
X11Symbols::getInstance()->xSync (display, True);
|
||||
|
||||
LinuxEventLoop::unregisterFdCallback (X11Symbols::getInstance()->xConnectionNumber (display));
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (XWindowSystem)
|
||||
|
||||
//==============================================================================
|
||||
ScopedXDisplay::ScopedXDisplay()
|
||||
: display (XWindowSystem::getInstance()->displayRef())
|
||||
{
|
||||
}
|
||||
|
||||
ScopedXDisplay::~ScopedXDisplay()
|
||||
{
|
||||
XWindowSystem::getInstance()->displayUnref();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ScopedXLock::ScopedXLock (::Display* d)
|
||||
: display (d)
|
||||
{
|
||||
if (display != nullptr)
|
||||
X11Symbols::getInstance()->xLockDisplay (display);
|
||||
}
|
||||
|
||||
ScopedXLock::~ScopedXLock()
|
||||
{
|
||||
if (display != nullptr)
|
||||
X11Symbols::getInstance()->xUnlockDisplay (display);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Atoms::Atoms (::Display* display)
|
||||
{
|
||||
protocols = getIfExists (display, "WM_PROTOCOLS");
|
||||
protocolList [TAKE_FOCUS] = getIfExists (display, "WM_TAKE_FOCUS");
|
||||
protocolList [DELETE_WINDOW] = getIfExists (display, "WM_DELETE_WINDOW");
|
||||
protocolList [PING] = getIfExists (display, "_NET_WM_PING");
|
||||
changeState = getIfExists (display, "WM_CHANGE_STATE");
|
||||
state = getIfExists (display, "WM_STATE");
|
||||
userTime = getCreating (display, "_NET_WM_USER_TIME");
|
||||
activeWin = getCreating (display, "_NET_ACTIVE_WINDOW");
|
||||
pid = getCreating (display, "_NET_WM_PID");
|
||||
windowType = getIfExists (display, "_NET_WM_WINDOW_TYPE");
|
||||
windowState = getIfExists (display, "_NET_WM_STATE");
|
||||
|
||||
XdndAware = getCreating (display, "XdndAware");
|
||||
XdndEnter = getCreating (display, "XdndEnter");
|
||||
XdndLeave = getCreating (display, "XdndLeave");
|
||||
XdndPosition = getCreating (display, "XdndPosition");
|
||||
XdndStatus = getCreating (display, "XdndStatus");
|
||||
XdndDrop = getCreating (display, "XdndDrop");
|
||||
XdndFinished = getCreating (display, "XdndFinished");
|
||||
XdndSelection = getCreating (display, "XdndSelection");
|
||||
|
||||
XdndTypeList = getCreating (display, "XdndTypeList");
|
||||
XdndActionList = getCreating (display, "XdndActionList");
|
||||
XdndActionCopy = getCreating (display, "XdndActionCopy");
|
||||
XdndActionPrivate = getCreating (display, "XdndActionPrivate");
|
||||
XdndActionDescription = getCreating (display, "XdndActionDescription");
|
||||
|
||||
XembedMsgType = getCreating (display, "_XEMBED");
|
||||
XembedInfo = getCreating (display, "_XEMBED_INFO");
|
||||
|
||||
allowedMimeTypes[0] = getCreating (display, "UTF8_STRING");
|
||||
allowedMimeTypes[1] = getCreating (display, "text/plain;charset=utf-8");
|
||||
allowedMimeTypes[2] = getCreating (display, "text/plain");
|
||||
allowedMimeTypes[3] = getCreating (display, "text/uri-list");
|
||||
|
||||
allowedActions[0] = getCreating (display, "XdndActionMove");
|
||||
allowedActions[1] = XdndActionCopy;
|
||||
allowedActions[2] = getCreating (display, "XdndActionLink");
|
||||
allowedActions[3] = getCreating (display, "XdndActionAsk");
|
||||
allowedActions[4] = XdndActionPrivate;
|
||||
}
|
||||
|
||||
Atom Atoms::getIfExists (::Display* display, const char* name) { return X11Symbols::getInstance()->xInternAtom (display, name, True); }
|
||||
Atom Atoms::getCreating (::Display* display, const char* name) { return X11Symbols::getInstance()->xInternAtom (display, name, False); }
|
||||
|
||||
String Atoms::getName (::Display* display, const Atom atom)
|
||||
{
|
||||
if (atom == None)
|
||||
return "None";
|
||||
|
||||
return X11Symbols::getInstance()->xGetAtomName (display, atom);
|
||||
}
|
||||
|
||||
bool Atoms::isMimeTypeFile (::Display* display, const Atom atom)
|
||||
{
|
||||
return getName (display, atom).equalsIgnoreCase ("text/uri-list");
|
||||
}
|
||||
|
||||
|
||||
const unsigned long Atoms::DndVersion = 3;
|
||||
|
||||
//==============================================================================
|
||||
GetXProperty::GetXProperty (::Display* display, Window window, Atom atom,
|
||||
long offset, long length, bool shouldDelete,
|
||||
Atom requestedType)
|
||||
{
|
||||
success = (X11Symbols::getInstance()->xGetWindowProperty (display, window, atom, offset, length,
|
||||
(Bool) shouldDelete, requestedType, &actualType,
|
||||
&actualFormat, &numItems, &bytesLeft, &data) == Success)
|
||||
&& data != nullptr;
|
||||
}
|
||||
|
||||
GetXProperty::~GetXProperty()
|
||||
{
|
||||
if (data != nullptr)
|
||||
X11Symbols::getInstance()->xFree (data);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE 6 technical preview.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
You may use this code under the terms of the GPL v3
|
||||
(see www.gnu.org/licenses).
|
||||
|
||||
For this technical preview, this file is not subject to commercial licensing.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
struct _XDisplay;
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
typedef ::_XDisplay* XDisplay;
|
||||
|
||||
typedef unsigned long AtomType;
|
||||
typedef unsigned long WindowType;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class XWindowSystem : public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
XDisplay displayRef() noexcept;
|
||||
XDisplay displayUnref() noexcept;
|
||||
|
||||
JUCE_DECLARE_SINGLETON (XWindowSystem, false)
|
||||
|
||||
private:
|
||||
bool xIsAvailable = false;
|
||||
|
||||
XDisplay display = {};
|
||||
Atomic<int> displayCount;
|
||||
|
||||
XWindowSystem() noexcept;
|
||||
~XWindowSystem() noexcept;
|
||||
|
||||
void initialiseXDisplay() noexcept;
|
||||
void destroyXDisplay() noexcept;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates and holds a reference to the X display.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct ScopedXDisplay
|
||||
{
|
||||
ScopedXDisplay();
|
||||
~ScopedXDisplay();
|
||||
|
||||
const XDisplay display;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A handy class that uses XLockDisplay and XUnlockDisplay to lock the X server
|
||||
using RAII (Only available in Linux!).
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class ScopedXLock
|
||||
{
|
||||
public:
|
||||
/** Creating a ScopedXLock object locks the X display.
|
||||
This uses XLockDisplay() to grab the display that JUCE is using.
|
||||
*/
|
||||
ScopedXLock (XDisplay);
|
||||
|
||||
/** Deleting a ScopedXLock object unlocks the X display.
|
||||
This calls XUnlockDisplay() to release the lock.
|
||||
*/
|
||||
~ScopedXLock();
|
||||
|
||||
private:
|
||||
XDisplay display;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct Atoms
|
||||
{
|
||||
Atoms (XDisplay);
|
||||
|
||||
enum ProtocolItems
|
||||
{
|
||||
TAKE_FOCUS = 0,
|
||||
DELETE_WINDOW = 1,
|
||||
PING = 2
|
||||
};
|
||||
|
||||
AtomType protocols, protocolList[3], changeState, state, userTime,
|
||||
activeWin, pid, windowType, windowState,
|
||||
XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus,
|
||||
XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList,
|
||||
XdndActionDescription, XdndActionCopy, XdndActionPrivate,
|
||||
XembedMsgType, XembedInfo,
|
||||
allowedActions[5],
|
||||
allowedMimeTypes[4];
|
||||
|
||||
static const unsigned long DndVersion;
|
||||
|
||||
static AtomType getIfExists (XDisplay, const char* name);
|
||||
static AtomType getCreating (XDisplay, const char* name);
|
||||
|
||||
static String getName (XDisplay, AtomType);
|
||||
|
||||
static bool isMimeTypeFile (XDisplay, AtomType);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct GetXProperty
|
||||
{
|
||||
GetXProperty (XDisplay, WindowType, AtomType,
|
||||
long offset, long length, bool shouldDelete,
|
||||
AtomType requestedType);
|
||||
|
||||
~GetXProperty();
|
||||
|
||||
bool success;
|
||||
unsigned char* data = nullptr;
|
||||
unsigned long numItems, bytesLeft;
|
||||
AtomType actualType;
|
||||
int actualFormat;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE 6 technical preview.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
You may use this code under the terms of the GPL v3
|
||||
(see www.gnu.org/licenses).
|
||||
|
||||
For this technical preview, this file is not subject to commercial licensing.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
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 (X11Symbols::getInstance()->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)
|
||||
X11Symbols::getInstance()->xFree (clipData);
|
||||
|
||||
jassert (bytesLeft == 0 || numItems == 1000000);
|
||||
}
|
||||
|
||||
X11Symbols::getInstance()->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)
|
||||
{
|
||||
auto property_name = X11Symbols::getInstance()->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
|
||||
X11Symbols::getInstance()->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 (X11Symbols::getInstance()->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)
|
||||
{
|
||||
X11Symbols::getInstance()->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"
|
||||
}
|
||||
}
|
||||
|
||||
X11Symbols::getInstance()->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;
|
||||
|
||||
X11Symbols::getInstance()->xSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
|
||||
X11Symbols::getInstance()->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 = X11Symbols::getInstance()->xGetSelectionOwner (display, selection)) == None)
|
||||
{
|
||||
selection = ClipboardHelpers::atom_CLIPBOARD;
|
||||
selectionOwner = X11Symbols::getInstance()->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;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern void* createDraggingHandCursor();
|
||||
extern ComponentPeer* getPeerFor (::Window);
|
||||
|
||||
//==============================================================================
|
||||
class X11DragState
|
||||
{
|
||||
public:
|
||||
X11DragState() = default;
|
||||
|
||||
//==============================================================================
|
||||
bool isDragging() const noexcept
|
||||
{
|
||||
return dragging;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleExternalSelectionClear()
|
||||
{
|
||||
if (dragging)
|
||||
externalResetDragAndDrop();
|
||||
}
|
||||
|
||||
void handleExternalSelectionRequest (const XEvent& evt)
|
||||
{
|
||||
auto targetType = evt.xselectionrequest.target;
|
||||
|
||||
XEvent s;
|
||||
s.xselection.type = SelectionNotify;
|
||||
s.xselection.requestor = evt.xselectionrequest.requestor;
|
||||
s.xselection.selection = evt.xselectionrequest.selection;
|
||||
s.xselection.target = targetType;
|
||||
s.xselection.property = None;
|
||||
s.xselection.time = evt.xselectionrequest.time;
|
||||
|
||||
if (allowedTypes.contains (targetType))
|
||||
{
|
||||
s.xselection.property = evt.xselectionrequest.property;
|
||||
|
||||
X11Symbols::getInstance()->xChangeProperty (getDisplay(), evt.xselectionrequest.requestor, evt.xselectionrequest.property,
|
||||
targetType, 8, PropModeReplace,
|
||||
reinterpret_cast<const unsigned char*> (textOrFiles.toRawUTF8()),
|
||||
(int) textOrFiles.getNumBytesAsUTF8());
|
||||
}
|
||||
|
||||
X11Symbols::getInstance()->xSendEvent (getDisplay(), evt.xselectionrequest.requestor, True, 0, &s);
|
||||
}
|
||||
|
||||
void handleExternalDragAndDropStatus (const XClientMessageEvent& clientMsg)
|
||||
{
|
||||
if (expectingStatus)
|
||||
{
|
||||
expectingStatus = false;
|
||||
canDrop = false;
|
||||
silentRect = {};
|
||||
|
||||
if ((clientMsg.data.l[1] & 1) != 0
|
||||
&& ((Atom) clientMsg.data.l[4] == getAtoms().XdndActionCopy
|
||||
|| (Atom) clientMsg.data.l[4] == getAtoms().XdndActionPrivate))
|
||||
{
|
||||
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,
|
||||
(int) clientMsg.data.l[3] >> 16, (int) clientMsg.data.l[3] & 0xffff);
|
||||
|
||||
canDrop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleExternalDragButtonReleaseEvent()
|
||||
{
|
||||
if (dragging)
|
||||
X11Symbols::getInstance()->xUngrabPointer (getDisplay(), CurrentTime);
|
||||
|
||||
if (canDrop)
|
||||
{
|
||||
sendExternalDragAndDropDrop();
|
||||
}
|
||||
else
|
||||
{
|
||||
sendExternalDragAndDropLeave();
|
||||
externalResetDragAndDrop();
|
||||
}
|
||||
}
|
||||
|
||||
void handleExternalDragMotionNotify()
|
||||
{
|
||||
auto newTargetWindow = externalFindDragTargetWindow (X11Symbols::getInstance()->xRootWindow (getDisplay(),
|
||||
X11Symbols::getInstance()->xDefaultScreen (getDisplay())));
|
||||
|
||||
if (targetWindow != newTargetWindow)
|
||||
{
|
||||
if (targetWindow != None)
|
||||
sendExternalDragAndDropLeave();
|
||||
|
||||
canDrop = false;
|
||||
silentRect = {};
|
||||
|
||||
if (newTargetWindow == None)
|
||||
return;
|
||||
|
||||
xdndVersion = getDnDVersionForWindow (newTargetWindow);
|
||||
|
||||
if (xdndVersion == -1)
|
||||
return;
|
||||
|
||||
targetWindow = newTargetWindow;
|
||||
sendExternalDragAndDropEnter();
|
||||
}
|
||||
|
||||
if (! expectingStatus)
|
||||
sendExternalDragAndDropPosition();
|
||||
}
|
||||
|
||||
void handleDragAndDropPosition (const XClientMessageEvent& clientMsg, ComponentPeer* peer)
|
||||
{
|
||||
if (dragAndDropSourceWindow == 0)
|
||||
return;
|
||||
|
||||
dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
|
||||
|
||||
if (windowH == 0)
|
||||
windowH = (::Window) peer->getNativeHandle();
|
||||
|
||||
auto dropPos = Desktop::getInstance().getDisplays().physicalToLogical (Point<int> ((int) clientMsg.data.l[2] >> 16,
|
||||
(int) clientMsg.data.l[2] & 0xffff));
|
||||
dropPos -= peer->getBounds().getPosition();
|
||||
|
||||
auto targetAction = getAtoms().XdndActionCopy;
|
||||
|
||||
for (int i = numElementsInArray (getAtoms().allowedActions); --i >= 0;)
|
||||
{
|
||||
if ((Atom) clientMsg.data.l[4] == getAtoms().allowedActions[i])
|
||||
{
|
||||
targetAction = getAtoms().allowedActions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sendDragAndDropStatus (true, targetAction);
|
||||
|
||||
if (dragInfo.position != dropPos)
|
||||
{
|
||||
dragInfo.position = dropPos;
|
||||
|
||||
if (dragInfo.isEmpty())
|
||||
updateDraggedFileList (clientMsg, (::Window) peer->getNativeHandle());
|
||||
|
||||
if (! dragInfo.isEmpty())
|
||||
peer->handleDragMove (dragInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void handleDragAndDropDrop (const XClientMessageEvent& clientMsg, ComponentPeer* peer)
|
||||
{
|
||||
if (dragInfo.isEmpty())
|
||||
{
|
||||
// no data, transaction finished in handleDragAndDropSelection()
|
||||
finishAfterDropDataReceived = true;
|
||||
updateDraggedFileList (clientMsg, (::Window) peer->getNativeHandle());
|
||||
}
|
||||
else
|
||||
{
|
||||
handleDragAndDropDataReceived(); // data was already received
|
||||
}
|
||||
}
|
||||
|
||||
void handleDragAndDropEnter (const XClientMessageEvent& clientMsg, ComponentPeer* peer)
|
||||
{
|
||||
dragInfo.clear();
|
||||
srcMimeTypeAtomList.clear();
|
||||
|
||||
dragAndDropCurrentMimeType = 0;
|
||||
auto dndCurrentVersion = static_cast<unsigned long> (clientMsg.data.l[1] & 0xff000000) >> 24;
|
||||
|
||||
if (dndCurrentVersion < 3 || dndCurrentVersion > XWindowSystemUtilities::Atoms::DndVersion)
|
||||
{
|
||||
dragAndDropSourceWindow = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
|
||||
|
||||
if ((clientMsg.data.l[1] & 1) != 0)
|
||||
{
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
XWindowSystemUtilities::GetXProperty prop (dragAndDropSourceWindow, getAtoms().XdndTypeList, 0, 0x8000000L, false, XA_ATOM);
|
||||
|
||||
if (prop.success && prop.actualType == XA_ATOM && prop.actualFormat == 32 && prop.numItems != 0)
|
||||
{
|
||||
auto* types = prop.data;
|
||||
|
||||
for (unsigned long i = 0; i < prop.numItems; ++i)
|
||||
{
|
||||
unsigned long type;
|
||||
memcpy (&type, types, sizeof (unsigned long));
|
||||
|
||||
if (type != None)
|
||||
srcMimeTypeAtomList.add (type);
|
||||
|
||||
types += sizeof (unsigned long);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (srcMimeTypeAtomList.isEmpty())
|
||||
{
|
||||
for (int i = 2; i < 5; ++i)
|
||||
if (clientMsg.data.l[i] != None)
|
||||
srcMimeTypeAtomList.add ((unsigned long) clientMsg.data.l[i]);
|
||||
|
||||
if (srcMimeTypeAtomList.isEmpty())
|
||||
{
|
||||
dragAndDropSourceWindow = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
handleDragAndDropPosition (clientMsg, peer);
|
||||
}
|
||||
|
||||
void handleDragAndDropExit()
|
||||
{
|
||||
if (auto* peer = getPeerFor (windowH))
|
||||
peer->handleDragExit (dragInfo);
|
||||
}
|
||||
|
||||
void handleDragAndDropSelection (const XEvent& evt)
|
||||
{
|
||||
dragInfo.clear();
|
||||
|
||||
if (evt.xselection.property != None)
|
||||
{
|
||||
StringArray lines;
|
||||
|
||||
{
|
||||
MemoryBlock dropData;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
XWindowSystemUtilities::GetXProperty prop (evt.xany.window, evt.xselection.property,
|
||||
(long) (dropData.getSize() / 4), 65536, false, AnyPropertyType);
|
||||
|
||||
if (! prop.success)
|
||||
break;
|
||||
|
||||
dropData.append (prop.data, (size_t) (prop.actualFormat / 8) * prop.numItems);
|
||||
|
||||
if (prop.bytesLeft <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
lines.addLines (dropData.toString());
|
||||
}
|
||||
|
||||
if (XWindowSystemUtilities::Atoms::isMimeTypeFile (getDisplay(), dragAndDropCurrentMimeType))
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
dragInfo.files.add (URL::removeEscapeChars (lines[i].replace ("file://", String(), true)));
|
||||
|
||||
dragInfo.files.trim();
|
||||
dragInfo.files.removeEmptyStrings();
|
||||
}
|
||||
else
|
||||
{
|
||||
dragInfo.text = lines.joinIntoString ("\n");
|
||||
}
|
||||
|
||||
if (finishAfterDropDataReceived)
|
||||
handleDragAndDropDataReceived();
|
||||
}
|
||||
}
|
||||
|
||||
void externalResetDragAndDrop()
|
||||
{
|
||||
if (dragging)
|
||||
{
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
X11Symbols::getInstance()->xUngrabPointer (getDisplay(), CurrentTime);
|
||||
}
|
||||
|
||||
if (completionCallback != nullptr)
|
||||
completionCallback();
|
||||
}
|
||||
|
||||
bool externalDragInit (::Window window, bool text, const String& str, std::function<void()>&& cb)
|
||||
{
|
||||
windowH = window;
|
||||
isText = text;
|
||||
textOrFiles = str;
|
||||
targetWindow = windowH;
|
||||
completionCallback = std::move (cb);
|
||||
|
||||
allowedTypes.add (XWindowSystemUtilities::Atoms::getCreating (getDisplay(), isText ? "text/plain" : "text/uri-list"));
|
||||
|
||||
auto pointerGrabMask = (unsigned int) (Button1MotionMask | ButtonReleaseMask);
|
||||
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
|
||||
if (X11Symbols::getInstance()->xGrabPointer (getDisplay(), windowH, True, pointerGrabMask,
|
||||
GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess)
|
||||
{
|
||||
// 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()->xSetSelectionOwner (getDisplay(), getAtoms().XdndSelection, windowH, CurrentTime);
|
||||
|
||||
// save the available types to XdndTypeList
|
||||
X11Symbols::getInstance()->xChangeProperty (getDisplay(), windowH, getAtoms().XdndTypeList, XA_ATOM, 32, PropModeReplace,
|
||||
reinterpret_cast<const unsigned char*> (allowedTypes.getRawDataPointer()), allowedTypes.size());
|
||||
|
||||
dragging = true;
|
||||
xdndVersion = getDnDVersionForWindow (targetWindow);
|
||||
|
||||
sendExternalDragAndDropEnter();
|
||||
handleExternalDragMotionNotify();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
XWindowSystemUtilities::Atoms& getAtoms() const { return XWindowSystem::getInstance()->getAtoms(); }
|
||||
::Display* getDisplay() const { return XWindowSystem::getInstance()->getDisplay(); }
|
||||
|
||||
//==============================================================================
|
||||
void sendDragAndDropMessage (XClientMessageEvent& msg)
|
||||
{
|
||||
msg.type = ClientMessage;
|
||||
msg.display = getDisplay();
|
||||
msg.window = dragAndDropSourceWindow;
|
||||
msg.format = 32;
|
||||
msg.data.l[0] = (long) windowH;
|
||||
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
X11Symbols::getInstance()->xSendEvent (getDisplay(), dragAndDropSourceWindow, False, 0, (XEvent*) &msg);
|
||||
}
|
||||
|
||||
bool sendExternalDragAndDropMessage (XClientMessageEvent& msg)
|
||||
{
|
||||
msg.type = ClientMessage;
|
||||
msg.display = getDisplay();
|
||||
msg.window = targetWindow;
|
||||
msg.format = 32;
|
||||
msg.data.l[0] = (long) windowH;
|
||||
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
return X11Symbols::getInstance()->xSendEvent (getDisplay(), targetWindow, False, 0, (XEvent*) &msg) != 0;
|
||||
}
|
||||
|
||||
void sendExternalDragAndDropDrop()
|
||||
{
|
||||
XClientMessageEvent msg;
|
||||
zerostruct (msg);
|
||||
|
||||
msg.message_type = getAtoms().XdndDrop;
|
||||
msg.data.l[2] = CurrentTime;
|
||||
|
||||
sendExternalDragAndDropMessage (msg);
|
||||
}
|
||||
|
||||
void sendExternalDragAndDropEnter()
|
||||
{
|
||||
XClientMessageEvent msg;
|
||||
zerostruct (msg);
|
||||
|
||||
msg.message_type = getAtoms().XdndEnter;
|
||||
msg.data.l[1] = (xdndVersion << 24);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
msg.data.l[i + 2] = (long) allowedTypes[i];
|
||||
|
||||
sendExternalDragAndDropMessage (msg);
|
||||
}
|
||||
|
||||
void sendExternalDragAndDropPosition()
|
||||
{
|
||||
XClientMessageEvent msg;
|
||||
zerostruct (msg);
|
||||
|
||||
msg.message_type = getAtoms().XdndPosition;
|
||||
|
||||
auto mousePos = Desktop::getInstance().getMousePosition();
|
||||
|
||||
if (silentRect.contains (mousePos)) // we've been asked to keep silent
|
||||
return;
|
||||
|
||||
mousePos = Desktop::getInstance().getDisplays().logicalToPhysical (mousePos);
|
||||
|
||||
msg.data.l[1] = 0;
|
||||
msg.data.l[2] = (mousePos.x << 16) | mousePos.y;
|
||||
msg.data.l[3] = CurrentTime;
|
||||
msg.data.l[4] = (long) getAtoms().XdndActionCopy; // this is all JUCE currently supports
|
||||
|
||||
expectingStatus = sendExternalDragAndDropMessage (msg);
|
||||
}
|
||||
|
||||
void sendDragAndDropStatus (bool acceptDrop, Atom dropAction)
|
||||
{
|
||||
XClientMessageEvent msg;
|
||||
zerostruct (msg);
|
||||
|
||||
msg.message_type = getAtoms().XdndStatus;
|
||||
msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages
|
||||
msg.data.l[4] = (long) dropAction;
|
||||
|
||||
sendDragAndDropMessage (msg);
|
||||
}
|
||||
|
||||
void sendExternalDragAndDropLeave()
|
||||
{
|
||||
XClientMessageEvent msg;
|
||||
zerostruct (msg);
|
||||
|
||||
msg.message_type = getAtoms().XdndLeave;
|
||||
sendExternalDragAndDropMessage (msg);
|
||||
}
|
||||
|
||||
void sendDragAndDropFinish()
|
||||
{
|
||||
XClientMessageEvent msg;
|
||||
zerostruct (msg);
|
||||
|
||||
msg.message_type = getAtoms().XdndFinished;
|
||||
sendDragAndDropMessage (msg);
|
||||
}
|
||||
|
||||
void updateDraggedFileList (const XClientMessageEvent& clientMsg, ::Window requestor)
|
||||
{
|
||||
jassert (dragInfo.isEmpty());
|
||||
|
||||
if (dragAndDropSourceWindow != None && dragAndDropCurrentMimeType != None)
|
||||
{
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
X11Symbols::getInstance()->xConvertSelection (getDisplay(), getAtoms().XdndSelection, dragAndDropCurrentMimeType,
|
||||
XWindowSystemUtilities::Atoms::getCreating (getDisplay(), "JXSelectionWindowProperty"),
|
||||
requestor, (::Time) clientMsg.data.l[2]);
|
||||
}
|
||||
}
|
||||
|
||||
bool isWindowDnDAware (::Window w) const
|
||||
{
|
||||
int numProperties = 0;
|
||||
auto* properties = X11Symbols::getInstance()->xListProperties (getDisplay(), w, &numProperties);
|
||||
|
||||
bool dndAwarePropFound = false;
|
||||
|
||||
for (int i = 0; i < numProperties; ++i)
|
||||
if (properties[i] == getAtoms().XdndAware)
|
||||
dndAwarePropFound = true;
|
||||
|
||||
if (properties != nullptr)
|
||||
X11Symbols::getInstance()->xFree (properties);
|
||||
|
||||
return dndAwarePropFound;
|
||||
}
|
||||
|
||||
int getDnDVersionForWindow (::Window target)
|
||||
{
|
||||
XWindowSystemUtilities::GetXProperty prop (target, getAtoms().XdndAware, 0, 2, false, AnyPropertyType);
|
||||
|
||||
if (prop.success && prop.data != None && prop.actualFormat == 32 && prop.numItems == 1)
|
||||
return jmin ((int) prop.data[0], (int) XWindowSystemUtilities::Atoms::DndVersion);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
::Window externalFindDragTargetWindow (::Window target)
|
||||
{
|
||||
if (target == None)
|
||||
return None;
|
||||
|
||||
if (isWindowDnDAware (target))
|
||||
return target;
|
||||
|
||||
::Window child, phonyWin;
|
||||
int phony;
|
||||
unsigned int uphony;
|
||||
|
||||
X11Symbols::getInstance()->xQueryPointer (getDisplay(), target, &phonyWin, &child, &phony, &phony, &phony, &phony, &uphony);
|
||||
|
||||
return externalFindDragTargetWindow (child);
|
||||
}
|
||||
|
||||
void handleDragAndDropDataReceived()
|
||||
{
|
||||
ComponentPeer::DragInfo dragInfoCopy (dragInfo);
|
||||
|
||||
sendDragAndDropFinish();
|
||||
|
||||
if (! dragInfoCopy.isEmpty())
|
||||
if (auto* peer = getPeerFor (windowH))
|
||||
peer->handleDragDrop (dragInfoCopy);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
::Window windowH = 0, targetWindow = 0, dragAndDropSourceWindow = 0;
|
||||
|
||||
int xdndVersion = -1;
|
||||
bool isText = false, dragging = false, expectingStatus = false, canDrop = false, finishAfterDropDataReceived = false;
|
||||
|
||||
Atom dragAndDropCurrentMimeType;
|
||||
Array<Atom> allowedTypes, srcMimeTypeAtomList;
|
||||
|
||||
ComponentPeer::DragInfo dragInfo;
|
||||
Rectangle<int> silentRect;
|
||||
String textOrFiles;
|
||||
|
||||
std::function<void()> completionCallback = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (X11DragState)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -43,7 +43,7 @@ namespace ReturnHelpers
|
|||
|
||||
|
||||
//==============================================================================
|
||||
class JUCE_API X11Symbols : public DeletedAtShutdown
|
||||
class JUCE_API X11Symbols
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
|
|
@ -223,7 +223,7 @@ public:
|
|||
char*)
|
||||
|
||||
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XGetErrorDatabaseText, xGetErrorDatabaseText,
|
||||
(::Display*, char*, const char*, const char*, int),
|
||||
(::Display*, const char*, const char*, const char*, const char*, int),
|
||||
void)
|
||||
|
||||
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XGetErrorText, xGetErrorText,
|
||||
3373
modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
Normal file
3373
modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
Normal file
File diff suppressed because it is too large
Load diff
233
modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h
Normal file
233
modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
namespace XWindowSystemUtilities
|
||||
{
|
||||
//==============================================================================
|
||||
/** A handy struct that uses XLockDisplay and XUnlockDisplay to lock the X server
|
||||
using RAII.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct ScopedXLock
|
||||
{
|
||||
ScopedXLock();
|
||||
~ScopedXLock();
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Gets a specified window property and stores its associated data, freeing it
|
||||
on deletion.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct GetXProperty
|
||||
{
|
||||
GetXProperty (::Window windowH, Atom property, long offset,
|
||||
long length, bool shouldDelete, Atom requestedType);
|
||||
~GetXProperty();
|
||||
|
||||
bool success = false;
|
||||
unsigned char* data = nullptr;
|
||||
unsigned long numItems = 0, bytesLeft = 0;
|
||||
Atom actualType;
|
||||
int actualFormat = -1;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Initialises and stores some atoms for the display.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct Atoms
|
||||
{
|
||||
enum ProtocolItems
|
||||
{
|
||||
TAKE_FOCUS = 0,
|
||||
DELETE_WINDOW = 1,
|
||||
PING = 2
|
||||
};
|
||||
|
||||
Atoms (::Display*);
|
||||
|
||||
static Atom getIfExists (::Display*, const char* name);
|
||||
static Atom getCreating (::Display*, const char* name);
|
||||
|
||||
static String getName (::Display*, Atom);
|
||||
static bool isMimeTypeFile (::Display*, Atom);
|
||||
|
||||
static constexpr unsigned long DndVersion = 3;
|
||||
|
||||
Atom protocols, protocolList[3], changeState, state, userTime, activeWin, pid, windowType, windowState,
|
||||
XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus, XdndDrop, XdndFinished, XdndSelection,
|
||||
XdndTypeList, XdndActionList, XdndActionDescription, XdndActionCopy, XdndActionPrivate,
|
||||
XembedMsgType, XembedInfo, allowedActions[5], allowedMimeTypes[4], utf8String, clipboard, targets;
|
||||
};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template<typename WindowHandle>
|
||||
class LinuxComponentPeer;
|
||||
|
||||
class XWindowSystem : public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
::Window createWindow (::Window parentWindow, LinuxComponentPeer<::Window>* peer) const;
|
||||
void destroyWindow (::Window windowH);
|
||||
|
||||
void setTitle (::Window windowH, const String& title) const;
|
||||
void setIcon (::Window windowH, const Image& newIcon) const;
|
||||
void setVisible (::Window windowH, bool shouldBeVisible) const;
|
||||
void setBounds (::Window windowH, Rectangle<int> newBounds, bool fullScreen) const;
|
||||
|
||||
BorderSize<int> getBorderSize (::Window windowH) const;
|
||||
Rectangle<int> getWindowBounds (::Window windowH, ::Window parentWindow);
|
||||
Point<int> getParentScreenPosition() const;
|
||||
|
||||
bool contains (::Window windowH, Point<int> localPos) const;
|
||||
|
||||
void setMinimised (::Window windowH, bool shouldBeMinimised) const;
|
||||
bool isMinimised (::Window windowH) const;
|
||||
|
||||
void toFront (::Window windowH, bool makeActive) const;
|
||||
void toBehind (::Window windowH, ::Window otherWindow) const;
|
||||
|
||||
bool isFocused (::Window windowH) const;
|
||||
bool grabFocus (::Window windowH) const;
|
||||
|
||||
bool canUseSemiTransparentWindows() const;
|
||||
bool canUseARGBImages() const;
|
||||
|
||||
int getNumPaintsPending (::Window windowH) const;
|
||||
|
||||
Image createImage (int width, int height, bool argb) const;
|
||||
void blitToWindow (::Window windowH, Image image, Rectangle<int> destinationRect, Rectangle<int> totalRect) const;
|
||||
|
||||
void setScreenSaverEnabled (bool enabled) const;
|
||||
|
||||
Point<float> getCurrentMousePosition() const;
|
||||
void setMousePosition (Point<float> pos) const;
|
||||
|
||||
void* createCustomMouseCursorInfo (const Image& image, Point<int> hotspot) const;
|
||||
void deleteMouseCursor (void* cursorHandle) const;
|
||||
void* createStandardMouseCursor (MouseCursor::StandardCursorType type) const;
|
||||
void showCursor (::Window windowH, void* cursorHandle) const;
|
||||
|
||||
bool isKeyCurrentlyDown (int keyCode) const;
|
||||
ModifierKeys getNativeRealtimeModifiers() const;
|
||||
|
||||
Array<Displays::Display> findDisplays (float masterScale) const;
|
||||
|
||||
::Window createKeyProxy (::Window windowH) const;
|
||||
void deleteKeyProxy (::Window keyProxy) const;
|
||||
|
||||
bool externalDragFileInit (LinuxComponentPeer<::Window>* peer, const StringArray& files, bool canMove, std::function<void()>&& callback) const;
|
||||
bool externalDragTextInit (LinuxComponentPeer<::Window>* peer, const String& text, std::function<void()>&& callback) const;
|
||||
|
||||
void copyTextToClipboard (const String& clipText);
|
||||
String getTextFromClipboard() const;
|
||||
|
||||
String getLocalClipboardContent() const { return localClipboardContent; }
|
||||
|
||||
::Display* getDisplay() { return display; }
|
||||
XWindowSystemUtilities::Atoms& getAtoms() { jassert (atoms.get() != nullptr); return *atoms; }
|
||||
|
||||
//==============================================================================
|
||||
void handleWindowMessage (LinuxComponentPeer<::Window>* peer, XEvent& event) const;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON (XWindowSystem, false)
|
||||
|
||||
private:
|
||||
XWindowSystem();
|
||||
~XWindowSystem();
|
||||
|
||||
//==============================================================================
|
||||
void initialiseXDisplay();
|
||||
void destroyXDisplay();
|
||||
|
||||
//==============================================================================
|
||||
::Window getFocusWindow (::Window windowH) const;
|
||||
|
||||
bool isParentWindowOf (::Window windowH, ::Window possibleChild) const;
|
||||
bool isFrontWindow (::Window windowH) const;
|
||||
|
||||
//==============================================================================
|
||||
void xchangeProperty (::Window windowH, Atom property, Atom type, int format, const void* data, int numElements) const;
|
||||
|
||||
void removeWindowDecorations (::Window windowH) const;
|
||||
void addWindowButtons (::Window windowH, int styleFlags) const;
|
||||
void setWindowType (::Window windowH, int styleFlags) const;
|
||||
|
||||
void initialisePointerMap();
|
||||
void deleteIconPixmaps (::Window windowH) const;
|
||||
void updateModifierMappings() const;
|
||||
|
||||
long getUserTime (::Window windowH) const;
|
||||
|
||||
//==============================================================================
|
||||
void handleKeyPressEvent (LinuxComponentPeer<::Window>*, XKeyEvent&) const;
|
||||
void handleKeyReleaseEvent (LinuxComponentPeer<::Window>*, const XKeyEvent&) const;
|
||||
void handleWheelEvent (LinuxComponentPeer<::Window>*, const XButtonPressedEvent&, float) const;
|
||||
void handleButtonPressEvent (LinuxComponentPeer<::Window>*, const XButtonPressedEvent&, int) const;
|
||||
void handleButtonPressEvent (LinuxComponentPeer<::Window>*, const XButtonPressedEvent&) const;
|
||||
void handleButtonReleaseEvent (LinuxComponentPeer<::Window>*, const XButtonReleasedEvent&) const;
|
||||
void handleMotionNotifyEvent (LinuxComponentPeer<::Window>*, const XPointerMovedEvent&) const;
|
||||
void handleEnterNotifyEvent (LinuxComponentPeer<::Window>*, const XEnterWindowEvent&) const;
|
||||
void handleLeaveNotifyEvent (LinuxComponentPeer<::Window>*, const XLeaveWindowEvent&) const;
|
||||
void handleFocusInEvent (LinuxComponentPeer<::Window>*) const;
|
||||
void handleFocusOutEvent (LinuxComponentPeer<::Window>*) const;
|
||||
void handleExposeEvent (LinuxComponentPeer<::Window>*, XExposeEvent&) const;
|
||||
void handleConfigureNotifyEvent (LinuxComponentPeer<::Window>*, XConfigureEvent&) const;
|
||||
void handleGravityNotify (LinuxComponentPeer<::Window>*) const;
|
||||
void handleMappingNotify (XMappingEvent&) const;
|
||||
void handleClientMessageEvent (LinuxComponentPeer<::Window>*, XClientMessageEvent&, XEvent&) const;
|
||||
void handleXEmbedMessage (LinuxComponentPeer<::Window>*, XClientMessageEvent&) const;
|
||||
|
||||
//==============================================================================
|
||||
bool xIsAvailable = false;
|
||||
|
||||
std::unique_ptr<XWindowSystemUtilities::Atoms> atoms;
|
||||
::Display* display = nullptr;
|
||||
Colormap colormap = {};
|
||||
Visual* visual = nullptr;
|
||||
|
||||
int depth = 0, shmCompletionEvent = 0;
|
||||
int pointerMap[5] = {};
|
||||
String localClipboardContent;
|
||||
|
||||
Point<int> parentScreenPosition;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XWindowSystem)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -24,17 +24,16 @@ class SystemTrayIconComponent::Pimpl
|
|||
public:
|
||||
Pimpl (const Image& im, Window windowH) : image (im)
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
auto display = xDisplay.display;
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
|
||||
ScopedXLock xlock (display);
|
||||
auto* display = XWindowSystem::getInstance()->getDisplay();
|
||||
|
||||
auto* screen = X11Symbols::getInstance()->xDefaultScreenOfDisplay (display);
|
||||
auto screenNumber = X11Symbols::getInstance()->xScreenNumberOfScreen (screen);
|
||||
|
||||
String screenAtom ("_NET_SYSTEM_TRAY_S");
|
||||
screenAtom << screenNumber;
|
||||
Atom selectionAtom = Atoms::getCreating (display, screenAtom.toUTF8());
|
||||
Atom selectionAtom = XWindowSystemUtilities::Atoms::getCreating (display, screenAtom.toUTF8());
|
||||
|
||||
X11Symbols::getInstance()->xGrabServer (display);
|
||||
auto managerWin = X11Symbols::getInstance()->xGetSelectionOwner (display, selectionAtom);
|
||||
|
|
@ -50,7 +49,7 @@ public:
|
|||
XEvent ev = { 0 };
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = managerWin;
|
||||
ev.xclient.message_type = Atoms::getCreating (display, "_NET_SYSTEM_TRAY_OPCODE");
|
||||
ev.xclient.message_type = XWindowSystemUtilities::Atoms::getCreating (display, "_NET_SYSTEM_TRAY_OPCODE");
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = CurrentTime;
|
||||
ev.xclient.data.l[1] = 0 /*SYSTEM_TRAY_REQUEST_DOCK*/;
|
||||
|
|
@ -64,12 +63,12 @@ public:
|
|||
|
||||
// For older KDE's ...
|
||||
long atomData = 1;
|
||||
Atom trayAtom = Atoms::getCreating (display, "KWM_DOCKWINDOW");
|
||||
Atom trayAtom = XWindowSystemUtilities::Atoms::getCreating (display, "KWM_DOCKWINDOW");
|
||||
X11Symbols::getInstance()->xChangeProperty (display, windowH, trayAtom, trayAtom,
|
||||
32, PropModeReplace, (unsigned char*) &atomData, 1);
|
||||
|
||||
// For more recent KDE's...
|
||||
trayAtom = Atoms::getCreating (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
|
||||
trayAtom = XWindowSystemUtilities::Atoms::getCreating (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
|
||||
X11Symbols::getInstance()->xChangeProperty (display, windowH, trayAtom, XA_WINDOW,
|
||||
32, PropModeReplace, (unsigned char*) &windowH, 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ bool juce_handleXEmbedEvent (ComponentPeer*, void*);
|
|||
Window juce_getCurrentFocusWindow (ComponentPeer*);
|
||||
|
||||
//==============================================================================
|
||||
unsigned long juce_createKeyProxyWindow (ComponentPeer*);
|
||||
void juce_deleteKeyProxyWindow (ComponentPeer*);
|
||||
::Window juce_createKeyProxyWindow (ComponentPeer*);
|
||||
void juce_deleteKeyProxyWindow (::Window);
|
||||
|
||||
//==============================================================================
|
||||
enum
|
||||
|
|
@ -75,7 +75,7 @@ public:
|
|||
|
||||
~SharedKeyWindow()
|
||||
{
|
||||
juce_deleteKeyProxyWindow (keyPeer);
|
||||
juce_deleteKeyProxyWindow (keyProxy);
|
||||
|
||||
auto& keyWindows = getKeyWindows();
|
||||
keyWindows.remove (keyPeer);
|
||||
|
|
@ -130,8 +130,12 @@ public:
|
|||
//==============================================================================
|
||||
Pimpl (XEmbedComponent& parent, Window x11Window,
|
||||
bool wantsKeyboardFocus, bool isClientInitiated, bool shouldAllowResize)
|
||||
: owner (parent), atoms (x11display.display), clientInitiated (isClientInitiated),
|
||||
wantsFocus (wantsKeyboardFocus), allowResize (shouldAllowResize)
|
||||
: owner (parent),
|
||||
infoAtom (XWindowSystem::getInstance()->getAtoms().XembedInfo),
|
||||
messageTypeAtom (XWindowSystem::getInstance()->getAtoms().XembedMsgType),
|
||||
clientInitiated (isClientInitiated),
|
||||
wantsFocus (wantsKeyboardFocus),
|
||||
allowResize (shouldAllowResize)
|
||||
{
|
||||
getWidgets().add (this);
|
||||
|
||||
|
|
@ -254,9 +258,7 @@ private:
|
|||
//==============================================================================
|
||||
XEmbedComponent& owner;
|
||||
Window client = 0, host = 0;
|
||||
|
||||
ScopedXDisplay x11display;
|
||||
Atoms atoms;
|
||||
Atom infoAtom, messageTypeAtom;
|
||||
|
||||
bool clientInitiated;
|
||||
bool wantsFocus = false;
|
||||
|
|
@ -372,12 +374,12 @@ private:
|
|||
return {};
|
||||
}
|
||||
|
||||
Display* getDisplay() { return reinterpret_cast<Display*> (x11display.display); }
|
||||
Display* getDisplay() { return XWindowSystem::getInstance()->getDisplay(); }
|
||||
|
||||
//==============================================================================
|
||||
bool getXEmbedMappedFlag()
|
||||
{
|
||||
GetXProperty embedInfo (x11display.display, client, atoms.XembedInfo, 0, 2, false, atoms.XembedInfo);
|
||||
XWindowSystemUtilities::GetXProperty embedInfo (client, infoAtom, 0, 2, false, infoAtom);
|
||||
|
||||
if (embedInfo.success && embedInfo.actualFormat == 32
|
||||
&& embedInfo.numItems >= 2 && embedInfo.data != nullptr)
|
||||
|
|
@ -405,7 +407,7 @@ private:
|
|||
//==============================================================================
|
||||
void propertyChanged (const Atom& a)
|
||||
{
|
||||
if (a == atoms.XembedInfo)
|
||||
if (a == infoAtom)
|
||||
updateMapping();
|
||||
}
|
||||
|
||||
|
|
@ -561,7 +563,7 @@ private:
|
|||
return true;
|
||||
|
||||
case ClientMessage:
|
||||
if (e.xclient.message_type == atoms.XembedMsgType && e.xclient.format == 32)
|
||||
if (e.xclient.message_type == messageTypeAtom && e.xclient.format == 32)
|
||||
{
|
||||
handleXembedCmd ((::Time) e.xclient.data.l[0], e.xclient.data.l[1],
|
||||
e.xclient.data.l[2], e.xclient.data.l[3],
|
||||
|
|
@ -588,7 +590,7 @@ private:
|
|||
::memset (&msg, 0, sizeof (XClientMessageEvent));
|
||||
msg.window = client;
|
||||
msg.type = ClientMessage;
|
||||
msg.message_type = atoms.XembedMsgType;
|
||||
msg.message_type = messageTypeAtom;
|
||||
msg.format = 32;
|
||||
msg.data.l[0] = (long) xTime;
|
||||
msg.data.l[1] = opcode;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ extern XContext windowHandleXContext;
|
|||
|
||||
//==============================================================================
|
||||
// Defined juce_linux_Windowing.cpp
|
||||
Rectangle<int> juce_LinuxScaledToPhysicalBounds (ComponentPeer*, Rectangle<int>);
|
||||
void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy);
|
||||
void juce_LinuxRemoveRepaintListener (ComponentPeer*, Component* dummy);
|
||||
|
||||
|
|
@ -55,9 +54,10 @@ public:
|
|||
OpenGLVersion)
|
||||
: component (comp), contextToShareWith (shareContext), dummy (*this)
|
||||
{
|
||||
display = XWindowSystem::getInstance()->displayRef();
|
||||
display = XWindowSystem::getInstance()->getDisplay();
|
||||
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
|
||||
ScopedXLock xlock (display);
|
||||
X11Symbols::getInstance()->xSync (display, False);
|
||||
|
||||
GLint attribs[] =
|
||||
|
|
@ -95,7 +95,7 @@ public:
|
|||
auto glBounds = component.getTopLevelComponent()
|
||||
->getLocalArea (&component, component.getLocalBounds());
|
||||
|
||||
glBounds = juce_LinuxScaledToPhysicalBounds (peer, glBounds);
|
||||
glBounds = Desktop::getInstance().getDisplays().logicalToPhysical (glBounds);
|
||||
|
||||
embeddedWindow = X11Symbols::getInstance()->xCreateWindow (display, windowH,
|
||||
glBounds.getX(), glBounds.getY(),
|
||||
|
|
@ -123,20 +123,18 @@ public:
|
|||
|
||||
if (embeddedWindow != 0)
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
X11Symbols::getInstance()->xUnmapWindow (display, embeddedWindow);
|
||||
X11Symbols::getInstance()->xDestroyWindow (display, embeddedWindow);
|
||||
}
|
||||
|
||||
if (bestVisual != nullptr)
|
||||
X11Symbols::getInstance()->xFree (bestVisual);
|
||||
|
||||
XWindowSystem::getInstance()->displayUnref();
|
||||
}
|
||||
|
||||
bool initialiseOnRenderThread (OpenGLContext& c)
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
renderContext = glXCreateContext (display, bestVisual, (GLXContext) contextToShareWith, GL_TRUE);
|
||||
c.makeActive();
|
||||
context = &c;
|
||||
|
|
@ -146,7 +144,7 @@ public:
|
|||
|
||||
void shutdownOnRenderThread()
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
context = nullptr;
|
||||
deactivateCurrentContext();
|
||||
glXDestroyContext (display, renderContext);
|
||||
|
|
@ -155,36 +153,38 @@ public:
|
|||
|
||||
bool makeActive() const noexcept
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
return renderContext != nullptr
|
||||
&& glXMakeCurrent (display, embeddedWindow, renderContext);
|
||||
}
|
||||
|
||||
bool isActive() const noexcept
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
return glXGetCurrentContext() == renderContext && renderContext != nullptr;
|
||||
}
|
||||
|
||||
static void deactivateCurrentContext()
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
ScopedXLock xlock (xDisplay.display);
|
||||
glXMakeCurrent (xDisplay.display, None, nullptr);
|
||||
if (auto* display = XWindowSystem::getInstance()->getDisplay())
|
||||
{
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
glXMakeCurrent (display, None, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void swapBuffers()
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
glXSwapBuffers (display, embeddedWindow);
|
||||
}
|
||||
|
||||
void updateWindowPosition (Rectangle<int> newBounds)
|
||||
{
|
||||
bounds = newBounds;
|
||||
auto physicalBounds = juce_LinuxScaledToPhysicalBounds (component.getPeer(), bounds);
|
||||
auto physicalBounds = Desktop::getInstance().getDisplays().logicalToPhysical (bounds);
|
||||
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
X11Symbols::getInstance()->xMoveResizeWindow (display, embeddedWindow,
|
||||
physicalBounds.getX(), physicalBounds.getY(),
|
||||
(unsigned int) jmax (1, physicalBounds.getWidth()),
|
||||
|
|
@ -199,7 +199,7 @@ public:
|
|||
if (auto GLXSwapIntervalSGI
|
||||
= (PFNGLXSWAPINTERVALSGIPROC) OpenGLHelpers::getExtensionFunction ("glXSwapIntervalSGI"))
|
||||
{
|
||||
ScopedXLock xlock (display);
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
swapFrames = numFramesPerSwap;
|
||||
GLXSwapIntervalSGI (numFramesPerSwap);
|
||||
return true;
|
||||
|
|
@ -228,13 +228,13 @@ private:
|
|||
|
||||
int swapFrames = 0;
|
||||
Rectangle<int> bounds;
|
||||
XVisualInfo* bestVisual = {};
|
||||
XVisualInfo* bestVisual = nullptr;
|
||||
void* contextToShareWith;
|
||||
|
||||
OpenGLContext* context = {};
|
||||
OpenGLContext* context = nullptr;
|
||||
DummyComponent dummy;
|
||||
|
||||
::Display* display = {};
|
||||
::Display* display = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
|
||||
};
|
||||
|
|
@ -242,15 +242,8 @@ private:
|
|||
//==============================================================================
|
||||
bool OpenGLHelpers::isContextActive()
|
||||
{
|
||||
ScopedXDisplay xDisplay;
|
||||
|
||||
if (xDisplay.display)
|
||||
{
|
||||
ScopedXLock xlock (xDisplay.display);
|
||||
return glXGetCurrentContext() != nullptr;
|
||||
}
|
||||
|
||||
return false;
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
return glXGetCurrentContext() != nullptr;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue