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.

1001 lines
37KB

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