The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

943 lines
34KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. //==============================================================================
  21. static int numAlwaysOnTopPeers = 0;
  22. bool juce_areThereAnyAlwaysOnTopWindows() { return numAlwaysOnTopPeers > 0; }
  23. //==============================================================================
  24. class LinuxComponentPeer : public ComponentPeer,
  25. private XWindowSystemUtilities::XSettings::Listener
  26. {
  27. public:
  28. LinuxComponentPeer (Component& comp, int windowStyleFlags, ::Window parentToAddTo)
  29. : ComponentPeer (comp, windowStyleFlags),
  30. isAlwaysOnTop (comp.isAlwaysOnTop())
  31. {
  32. // it's dangerous to create a window on a thread other than the message thread.
  33. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  34. const auto* instance = XWindowSystem::getInstance();
  35. if (! instance->isX11Available())
  36. return;
  37. if (isAlwaysOnTop)
  38. ++numAlwaysOnTopPeers;
  39. repainter = std::make_unique<LinuxRepaintManager> (*this);
  40. windowH = instance->createWindow (parentToAddTo, this);
  41. parentWindow = parentToAddTo;
  42. setTitle (component.getName());
  43. if (auto* xSettings = instance->getXSettings())
  44. xSettings->addListener (this);
  45. getNativeRealtimeModifiers = []() -> ModifierKeys { return XWindowSystem::getInstance()->getNativeRealtimeModifiers(); };
  46. }
  47. ~LinuxComponentPeer() override
  48. {
  49. // it's dangerous to delete a window on a thread other than the message thread.
  50. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  51. auto* instance = XWindowSystem::getInstance();
  52. repainter = nullptr;
  53. instance->destroyWindow (windowH);
  54. if (auto* xSettings = instance->getXSettings())
  55. xSettings->removeListener (this);
  56. if (isAlwaysOnTop)
  57. --numAlwaysOnTopPeers;
  58. }
  59. ::Window getWindowHandle() const noexcept
  60. {
  61. return windowH;
  62. }
  63. //==============================================================================
  64. void* getNativeHandle() const override
  65. {
  66. return reinterpret_cast<void*> (getWindowHandle());
  67. }
  68. //==============================================================================
  69. void forceSetBounds (const Rectangle<int>& correctedNewBounds, bool isNowFullScreen)
  70. {
  71. bounds = correctedNewBounds;
  72. updateScaleFactorFromNewBounds (bounds, false);
  73. auto physicalBounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds)
  74. : bounds * currentScaleFactor;
  75. WeakReference<Component> deletionChecker (&component);
  76. XWindowSystem::getInstance()->setBounds (windowH, physicalBounds, isNowFullScreen);
  77. fullScreen = isNowFullScreen;
  78. if (deletionChecker != nullptr)
  79. {
  80. updateBorderSize();
  81. handleMovedOrResized();
  82. }
  83. }
  84. void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) override
  85. {
  86. const auto correctedNewBounds = newBounds.withSize (jmax (1, newBounds.getWidth()),
  87. jmax (1, newBounds.getHeight()));
  88. if (bounds != correctedNewBounds || fullScreen != isNowFullScreen)
  89. forceSetBounds (correctedNewBounds, isNowFullScreen);
  90. }
  91. Point<int> getScreenPosition (bool physical) const
  92. {
  93. auto physicalParentPosition = XWindowSystem::getInstance()->getPhysicalParentScreenPosition();
  94. auto parentPosition = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalParentPosition)
  95. : physicalParentPosition / currentScaleFactor;
  96. auto screenBounds = parentWindow == 0 ? bounds
  97. : bounds.translated (parentPosition.x, parentPosition.y);
  98. if (physical)
  99. return parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft())
  100. : screenBounds.getTopLeft() * currentScaleFactor;
  101. return screenBounds.getTopLeft();
  102. }
  103. Rectangle<int> getBounds() const override
  104. {
  105. return bounds;
  106. }
  107. OptionalBorderSize getFrameSizeIfPresent() const override
  108. {
  109. return windowBorder;
  110. }
  111. BorderSize<int> getFrameSize() const override
  112. {
  113. const auto optionalBorderSize = getFrameSizeIfPresent();
  114. return optionalBorderSize ? (*optionalBorderSize) : BorderSize<int>();
  115. }
  116. Point<float> localToGlobal (Point<float> relativePosition) override
  117. {
  118. return relativePosition + getScreenPosition (false).toFloat();
  119. }
  120. Point<float> globalToLocal (Point<float> screenPosition) override
  121. {
  122. return screenPosition - getScreenPosition (false).toFloat();
  123. }
  124. using ComponentPeer::localToGlobal;
  125. using ComponentPeer::globalToLocal;
  126. //==============================================================================
  127. StringArray getAvailableRenderingEngines() override
  128. {
  129. return { "Software Renderer" };
  130. }
  131. void setVisible (bool shouldBeVisible) override
  132. {
  133. XWindowSystem::getInstance()->setVisible (windowH, shouldBeVisible);
  134. }
  135. void setTitle (const String& title) override
  136. {
  137. XWindowSystem::getInstance()->setTitle (windowH, title);
  138. }
  139. void setMinimised (bool shouldBeMinimised) override
  140. {
  141. if (shouldBeMinimised)
  142. XWindowSystem::getInstance()->setMinimised (windowH, shouldBeMinimised);
  143. else
  144. setVisible (true);
  145. }
  146. bool isMinimised() const override
  147. {
  148. return XWindowSystem::getInstance()->isMinimised (windowH);
  149. }
  150. void setFullScreen (bool shouldBeFullScreen) override
  151. {
  152. auto r = lastNonFullscreenBounds; // (get a copy of this before de-minimising)
  153. setMinimised (false);
  154. if (fullScreen != shouldBeFullScreen)
  155. {
  156. const auto usingNativeTitleBar = ((styleFlags & windowHasTitleBar) != 0);
  157. if (usingNativeTitleBar)
  158. XWindowSystem::getInstance()->setMaximised (windowH, shouldBeFullScreen);
  159. if (shouldBeFullScreen)
  160. r = usingNativeTitleBar ? XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow)
  161. : Desktop::getInstance().getDisplays().getDisplayForRect (bounds)->userArea;
  162. if (! r.isEmpty())
  163. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
  164. component.repaint();
  165. }
  166. }
  167. bool isFullScreen() const override
  168. {
  169. return fullScreen;
  170. }
  171. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  172. {
  173. if (! bounds.withZeroOrigin().contains (localPos))
  174. return false;
  175. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  176. {
  177. auto* c = Desktop::getInstance().getComponent (i);
  178. if (c == &component)
  179. break;
  180. if (! c->isVisible())
  181. continue;
  182. if (auto* peer = c->getPeer())
  183. if (peer->contains (localPos + bounds.getPosition() - peer->getBounds().getPosition(), true))
  184. return false;
  185. }
  186. if (trueIfInAChildWindow)
  187. return true;
  188. return XWindowSystem::getInstance()->contains (windowH, localPos * currentScaleFactor);
  189. }
  190. void toFront (bool makeActive) override
  191. {
  192. if (makeActive)
  193. {
  194. setVisible (true);
  195. grabFocus();
  196. }
  197. XWindowSystem::getInstance()->toFront (windowH, makeActive);
  198. handleBroughtToFront();
  199. }
  200. void toBehind (ComponentPeer* other) override
  201. {
  202. if (auto* otherPeer = dynamic_cast<LinuxComponentPeer*> (other))
  203. {
  204. if (otherPeer->styleFlags & windowIsTemporary)
  205. return;
  206. setMinimised (false);
  207. XWindowSystem::getInstance()->toBehind (windowH, otherPeer->windowH);
  208. }
  209. else
  210. {
  211. jassertfalse; // wrong type of window?
  212. }
  213. }
  214. bool isFocused() const override
  215. {
  216. return XWindowSystem::getInstance()->isFocused (windowH);
  217. }
  218. void grabFocus() override
  219. {
  220. if (XWindowSystem::getInstance()->grabFocus (windowH))
  221. isActiveApplication = true;
  222. }
  223. //==============================================================================
  224. void repaint (const Rectangle<int>& area) override
  225. {
  226. if (repainter != nullptr)
  227. repainter->repaint (area.getIntersection (bounds.withZeroOrigin()));
  228. }
  229. void performAnyPendingRepaintsNow() override
  230. {
  231. if (repainter != nullptr)
  232. repainter->performAnyPendingRepaintsNow();
  233. }
  234. void setIcon (const Image& newIcon) override
  235. {
  236. XWindowSystem::getInstance()->setIcon (windowH, newIcon);
  237. }
  238. double getPlatformScaleFactor() const noexcept override
  239. {
  240. return currentScaleFactor;
  241. }
  242. void setAlpha (float) override {}
  243. bool setAlwaysOnTop (bool) override { return false; }
  244. void textInputRequired (Point<int>, TextInputTarget&) override {}
  245. //==============================================================================
  246. void addOpenGLRepaintListener (Component* dummy)
  247. {
  248. if (dummy != nullptr)
  249. glRepaintListeners.addIfNotAlreadyThere (dummy);
  250. }
  251. void removeOpenGLRepaintListener (Component* dummy)
  252. {
  253. if (dummy != nullptr)
  254. glRepaintListeners.removeAllInstancesOf (dummy);
  255. }
  256. void repaintOpenGLContexts()
  257. {
  258. for (auto* c : glRepaintListeners)
  259. c->handleCommandMessage (0);
  260. }
  261. //==============================================================================
  262. ::Window getParentWindow() { return parentWindow; }
  263. void setParentWindow (::Window newParent) { parentWindow = newParent; }
  264. //==============================================================================
  265. bool isConstrainedNativeWindow() const
  266. {
  267. return constrainer != nullptr
  268. && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable)
  269. && ! isKioskMode();
  270. }
  271. void updateWindowBounds()
  272. {
  273. if (windowH == 0)
  274. {
  275. jassertfalse;
  276. return;
  277. }
  278. if (isConstrainedNativeWindow())
  279. XWindowSystem::getInstance()->updateConstraints (windowH);
  280. auto physicalBounds = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow);
  281. updateScaleFactorFromNewBounds (physicalBounds, true);
  282. bounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds)
  283. : physicalBounds / currentScaleFactor;
  284. }
  285. void updateBorderSize()
  286. {
  287. if ((styleFlags & windowHasTitleBar) == 0)
  288. {
  289. windowBorder = ComponentPeer::OptionalBorderSize { BorderSize<int>() };
  290. }
  291. else if (! windowBorder
  292. || ((*windowBorder).getTopAndBottom() == 0 && (*windowBorder).getLeftAndRight() == 0))
  293. {
  294. windowBorder = [&]()
  295. {
  296. if (auto unscaledBorderSize = XWindowSystem::getInstance()->getBorderSize (windowH))
  297. return OptionalBorderSize { (*unscaledBorderSize).multipliedBy (1.0 / currentScaleFactor) };
  298. return OptionalBorderSize {};
  299. }();
  300. }
  301. }
  302. //==============================================================================
  303. static bool isActiveApplication;
  304. bool focused = false;
  305. private:
  306. //==============================================================================
  307. class LinuxRepaintManager : public Timer
  308. {
  309. public:
  310. LinuxRepaintManager (LinuxComponentPeer& p)
  311. : peer (p),
  312. isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
  313. {
  314. }
  315. void timerCallback() override
  316. {
  317. XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
  318. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  319. return;
  320. if (! regionsNeedingRepaint.isEmpty())
  321. {
  322. stopTimer();
  323. performAnyPendingRepaintsNow();
  324. }
  325. else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
  326. {
  327. stopTimer();
  328. image = Image();
  329. }
  330. }
  331. void repaint (Rectangle<int> area)
  332. {
  333. if (! isTimerRunning())
  334. startTimer (repaintTimerPeriod);
  335. regionsNeedingRepaint.add (area * peer.currentScaleFactor);
  336. }
  337. void performAnyPendingRepaintsNow()
  338. {
  339. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  340. {
  341. startTimer (repaintTimerPeriod);
  342. return;
  343. }
  344. auto originalRepaintRegion = regionsNeedingRepaint;
  345. regionsNeedingRepaint.clear();
  346. auto totalArea = originalRepaintRegion.getBounds();
  347. if (! totalArea.isEmpty())
  348. {
  349. const auto wasImageNull = image.isNull();
  350. if (wasImageNull || image.getWidth() < totalArea.getWidth()
  351. || image.getHeight() < totalArea.getHeight())
  352. {
  353. image = XWindowSystem::getInstance()->createImage (isSemiTransparentWindow,
  354. totalArea.getWidth(), totalArea.getHeight(),
  355. useARGBImagesForRendering);
  356. if (wasImageNull)
  357. {
  358. // After calling createImage() XWindowSystem::getWindowBounds() will return
  359. // changed coordinates that look like the result of some position
  360. // defaulting mechanism. If we handle a configureNotifyEvent after
  361. // createImage() and before we would issue new, valid coordinates, we will
  362. // apply these default, unwanted coordinates to our window. To avoid that
  363. // we immediately send another positioning message to guarantee that the
  364. // next configureNotifyEvent will read valid values.
  365. //
  366. // This issue only occurs right after peer creation, when the image is
  367. // null. Updating when only the width or height is changed would lead to
  368. // incorrect behaviour.
  369. peer.forceSetBounds (ScalingHelpers::scaledScreenPosToUnscaled (peer.component,
  370. peer.component.getBoundsInParent()),
  371. peer.isFullScreen());
  372. }
  373. }
  374. startTimer (repaintTimerPeriod);
  375. RectangleList<int> adjustedList (originalRepaintRegion);
  376. adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY());
  377. if (XWindowSystem::getInstance()->canUseARGBImages())
  378. for (auto& i : originalRepaintRegion)
  379. image.clear (i - totalArea.getPosition());
  380. {
  381. auto context = peer.getComponent().getLookAndFeel()
  382. .createGraphicsContext (image, -totalArea.getPosition(), adjustedList);
  383. context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor));
  384. peer.handlePaint (*context);
  385. }
  386. for (auto& i : originalRepaintRegion)
  387. XWindowSystem::getInstance()->blitToWindow (peer.windowH, image, i, totalArea);
  388. }
  389. lastTimeImageUsed = Time::getApproximateMillisecondCounter();
  390. startTimer (repaintTimerPeriod);
  391. }
  392. private:
  393. enum { repaintTimerPeriod = 1000 / 100 };
  394. LinuxComponentPeer& peer;
  395. const bool isSemiTransparentWindow;
  396. Image image;
  397. uint32 lastTimeImageUsed = 0;
  398. RectangleList<int> regionsNeedingRepaint;
  399. bool useARGBImagesForRendering = XWindowSystem::getInstance()->canUseARGBImages();
  400. JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
  401. };
  402. //==============================================================================
  403. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  404. {
  405. static StringArray possibleSettings { XWindowSystem::getWindowScalingFactorSettingName(),
  406. "Gdk/UnscaledDPI",
  407. "Xft/DPI" };
  408. if (possibleSettings.contains (settingThatHasChanged.name))
  409. forceDisplayUpdate();
  410. }
  411. void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
  412. {
  413. Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
  414. const auto& desktop = Desktop::getInstance();
  415. if (auto* display = desktop.getDisplays().getDisplayForRect (newBounds.translated (translation.x, translation.y),
  416. isPhysical))
  417. {
  418. auto newScaleFactor = display->scale / desktop.getGlobalScaleFactor();
  419. if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
  420. {
  421. currentScaleFactor = newScaleFactor;
  422. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
  423. }
  424. }
  425. }
  426. //==============================================================================
  427. std::unique_ptr<LinuxRepaintManager> repainter;
  428. ::Window windowH = {}, parentWindow = {};
  429. Rectangle<int> bounds;
  430. ComponentPeer::OptionalBorderSize windowBorder;
  431. bool fullScreen = false, isAlwaysOnTop = false;
  432. double currentScaleFactor = 1.0;
  433. Array<Component*> glRepaintListeners;
  434. //==============================================================================
  435. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
  436. };
  437. bool LinuxComponentPeer::isActiveApplication = false;
  438. //==============================================================================
  439. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  440. {
  441. return new LinuxComponentPeer (*this, styleFlags, (::Window) nativeWindowToAttachTo);
  442. }
  443. //==============================================================================
  444. JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return LinuxComponentPeer::isActiveApplication; }
  445. JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  446. JUCE_API void JUCE_CALLTYPE Process::hide() {}
  447. //==============================================================================
  448. void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
  449. {
  450. if (enableOrDisable)
  451. comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea);
  452. }
  453. void Displays::findDisplays (float masterScale)
  454. {
  455. if (XWindowSystem::getInstance()->getDisplay() != nullptr)
  456. {
  457. displays = XWindowSystem::getInstance()->findDisplays (masterScale);
  458. if (! displays.isEmpty())
  459. updateToLogical();
  460. }
  461. }
  462. bool Desktop::canUseSemiTransparentWindows() noexcept
  463. {
  464. return XWindowSystem::getInstance()->canUseSemiTransparentWindows();
  465. }
  466. class Desktop::NativeDarkModeChangeDetectorImpl : private XWindowSystemUtilities::XSettings::Listener
  467. {
  468. public:
  469. NativeDarkModeChangeDetectorImpl()
  470. {
  471. const auto* windowSystem = XWindowSystem::getInstance();
  472. if (auto* xSettings = windowSystem->getXSettings())
  473. xSettings->addListener (this);
  474. darkModeEnabled = windowSystem->isDarkModeActive();
  475. }
  476. ~NativeDarkModeChangeDetectorImpl() override
  477. {
  478. if (auto* windowSystem = XWindowSystem::getInstanceWithoutCreating())
  479. if (auto* xSettings = windowSystem->getXSettings())
  480. xSettings->removeListener (this);
  481. }
  482. bool isDarkModeEnabled() const noexcept { return darkModeEnabled; }
  483. private:
  484. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  485. {
  486. if (settingThatHasChanged.name == XWindowSystem::getThemeNameSettingName())
  487. {
  488. const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, XWindowSystem::getInstance()->isDarkModeActive());
  489. if (darkModeEnabled != wasDarkModeEnabled)
  490. Desktop::getInstance().darkModeChanged();
  491. }
  492. }
  493. bool darkModeEnabled = false;
  494. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  495. };
  496. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  497. {
  498. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  499. }
  500. bool Desktop::isDarkModeActive() const
  501. {
  502. return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled();
  503. }
  504. static bool screenSaverAllowed = true;
  505. void Desktop::setScreenSaverEnabled (bool isEnabled)
  506. {
  507. if (screenSaverAllowed != isEnabled)
  508. {
  509. screenSaverAllowed = isEnabled;
  510. XWindowSystem::getInstance()->setScreenSaverEnabled (screenSaverAllowed);
  511. }
  512. }
  513. bool Desktop::isScreenSaverEnabled()
  514. {
  515. return screenSaverAllowed;
  516. }
  517. double Desktop::getDefaultMasterScale() { return 1.0; }
  518. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; }
  519. void Desktop::allowedOrientationsChanged() {}
  520. //==============================================================================
  521. bool MouseInputSource::SourceList::addSource()
  522. {
  523. if (sources.isEmpty())
  524. {
  525. addSource (0, MouseInputSource::InputSourceType::mouse);
  526. return true;
  527. }
  528. return false;
  529. }
  530. bool MouseInputSource::SourceList::canUseTouch()
  531. {
  532. return false;
  533. }
  534. Point<float> MouseInputSource::getCurrentRawMousePosition()
  535. {
  536. return Desktop::getInstance().getDisplays().physicalToLogical (XWindowSystem::getInstance()->getCurrentMousePosition());
  537. }
  538. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  539. {
  540. XWindowSystem::getInstance()->setMousePosition (Desktop::getInstance().getDisplays().logicalToPhysical (newPosition));
  541. }
  542. //==============================================================================
  543. class MouseCursor::PlatformSpecificHandle
  544. {
  545. public:
  546. explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type)
  547. : cursorHandle (makeHandle (type)) {}
  548. explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info)
  549. : cursorHandle (makeHandle (info)) {}
  550. ~PlatformSpecificHandle()
  551. {
  552. if (cursorHandle != Cursor{})
  553. XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle);
  554. }
  555. static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer)
  556. {
  557. const auto cursor = handle != nullptr ? handle->cursorHandle : Cursor{};
  558. if (peer != nullptr)
  559. XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), cursor);
  560. }
  561. private:
  562. static Cursor makeHandle (const CustomMouseCursorInfo& info)
  563. {
  564. const auto image = info.image.getImage();
  565. return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image.rescaled ((int) (image.getWidth() / info.image.getScale()),
  566. (int) (image.getHeight() / info.image.getScale())), info.hotspot);
  567. }
  568. static Cursor makeHandle (MouseCursor::StandardCursorType type)
  569. {
  570. return XWindowSystem::getInstance()->createStandardMouseCursor (type);
  571. }
  572. Cursor cursorHandle;
  573. //==============================================================================
  574. JUCE_DECLARE_NON_COPYABLE (PlatformSpecificHandle)
  575. JUCE_DECLARE_NON_MOVEABLE (PlatformSpecificHandle)
  576. };
  577. //==============================================================================
  578. static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
  579. {
  580. if (sourceComp == nullptr)
  581. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  582. sourceComp = draggingSource->getComponentUnderMouse();
  583. if (sourceComp != nullptr)
  584. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (sourceComp->getPeer()))
  585. return lp;
  586. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  587. return nullptr;
  588. }
  589. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  590. Component* sourceComp, std::function<void()> callback)
  591. {
  592. if (files.isEmpty())
  593. return false;
  594. if (auto* peer = getPeerForDragEvent (sourceComp))
  595. return XWindowSystem::getInstance()->externalDragFileInit (peer, files, canMoveFiles, std::move (callback));
  596. // This method must be called in response to a component's mouseDown or mouseDrag event!
  597. jassertfalse;
  598. return false;
  599. }
  600. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
  601. std::function<void()> callback)
  602. {
  603. if (text.isEmpty())
  604. return false;
  605. if (auto* peer = getPeerForDragEvent (sourceComp))
  606. return XWindowSystem::getInstance()->externalDragTextInit (peer, text, std::move (callback));
  607. // This method must be called in response to a component's mouseDown or mouseDrag event!
  608. jassertfalse;
  609. return false;
  610. }
  611. //==============================================================================
  612. void SystemClipboard::copyTextToClipboard (const String& clipText)
  613. {
  614. XWindowSystem::getInstance()->copyTextToClipboard (clipText);
  615. }
  616. String SystemClipboard::getTextFromClipboard()
  617. {
  618. return XWindowSystem::getInstance()->getTextFromClipboard();
  619. }
  620. //==============================================================================
  621. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  622. {
  623. return XWindowSystem::getInstance()->isKeyCurrentlyDown (keyCode);
  624. }
  625. void LookAndFeel::playAlertSound()
  626. {
  627. std::cout << "\a" << std::flush;
  628. }
  629. //==============================================================================
  630. static int showDialog (const MessageBoxOptions& options,
  631. ModalComponentManager::Callback* callback,
  632. Async async)
  633. {
  634. const auto dummyCallback = [] (int) {};
  635. switch (options.getNumButtons())
  636. {
  637. case 2:
  638. {
  639. if (async == Async::yes && callback == nullptr)
  640. callback = ModalCallbackFunction::create (dummyCallback);
  641. return AlertWindow::showOkCancelBox (options.getIconType(),
  642. options.getTitle(),
  643. options.getMessage(),
  644. options.getButtonText (0),
  645. options.getButtonText (1),
  646. options.getAssociatedComponent(),
  647. callback) ? 1 : 0;
  648. }
  649. case 3:
  650. {
  651. if (async == Async::yes && callback == nullptr)
  652. callback = ModalCallbackFunction::create (dummyCallback);
  653. return AlertWindow::showYesNoCancelBox (options.getIconType(),
  654. options.getTitle(),
  655. options.getMessage(),
  656. options.getButtonText (0),
  657. options.getButtonText (1),
  658. options.getButtonText (2),
  659. options.getAssociatedComponent(),
  660. callback);
  661. }
  662. case 1:
  663. default:
  664. break;
  665. }
  666. #if JUCE_MODAL_LOOPS_PERMITTED
  667. if (async == Async::no)
  668. {
  669. AlertWindow::showMessageBox (options.getIconType(),
  670. options.getTitle(),
  671. options.getMessage(),
  672. options.getButtonText (0),
  673. options.getAssociatedComponent());
  674. }
  675. else
  676. #endif
  677. {
  678. AlertWindow::showMessageBoxAsync (options.getIconType(),
  679. options.getTitle(),
  680. options.getMessage(),
  681. options.getButtonText (0),
  682. options.getAssociatedComponent(),
  683. callback);
  684. }
  685. return 0;
  686. }
  687. #if JUCE_MODAL_LOOPS_PERMITTED
  688. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  689. const String& title, const String& message,
  690. Component* /*associatedComponent*/)
  691. {
  692. AlertWindow::showMessageBox (iconType, title, message);
  693. }
  694. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  695. {
  696. return showDialog (options, nullptr, Async::no);
  697. }
  698. #endif
  699. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  700. const String& title, const String& message,
  701. Component* associatedComponent,
  702. ModalComponentManager::Callback* callback)
  703. {
  704. AlertWindow::showMessageBoxAsync (iconType, title, message, {}, associatedComponent, callback);
  705. }
  706. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  707. const String& title, const String& message,
  708. Component* associatedComponent,
  709. ModalComponentManager::Callback* callback)
  710. {
  711. return AlertWindow::showOkCancelBox (iconType, title, message, {}, {}, associatedComponent, callback);
  712. }
  713. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  714. const String& title, const String& message,
  715. Component* associatedComponent,
  716. ModalComponentManager::Callback* callback)
  717. {
  718. return AlertWindow::showYesNoCancelBox (iconType, title, message, {}, {}, {},
  719. associatedComponent, callback);
  720. }
  721. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  722. const String& title, const String& message,
  723. Component* associatedComponent,
  724. ModalComponentManager::Callback* callback)
  725. {
  726. return AlertWindow::showOkCancelBox (iconType, title, message, TRANS("Yes"), TRANS("No"),
  727. associatedComponent, callback);
  728. }
  729. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  730. ModalComponentManager::Callback* callback)
  731. {
  732. showDialog (options, callback, Async::yes);
  733. }
  734. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  735. std::function<void (int)> callback)
  736. {
  737. showAsync (options, ModalCallbackFunction::create (callback));
  738. }
  739. //==============================================================================
  740. Image juce_createIconForFile (const File&)
  741. {
  742. return {};
  743. }
  744. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
  745. {
  746. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  747. linuxPeer->addOpenGLRepaintListener (dummy);
  748. }
  749. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
  750. {
  751. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  752. linuxPeer->removeOpenGLRepaintListener (dummy);
  753. }
  754. } // namespace juce