| @@ -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 | |||
| @@ -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 | |||
| @@ -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, | |||
| @@ -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 | |||