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.

999 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. }
  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 localToGlobal (*this, relativePosition);
  119. }
  120. Point<float> globalToLocal (Point<float> screenPosition) override
  121. {
  122. return globalToLocal (*this, screenPosition);
  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. auto* otherPeer = c->getPeer();
  183. jassert (otherPeer == nullptr || dynamic_cast<LinuxComponentPeer*> (c->getPeer()) != nullptr);
  184. if (auto* peer = static_cast<LinuxComponentPeer*> (otherPeer))
  185. if (peer->contains (globalToLocal (*peer, localToGlobal (*this, localPos.toFloat())).roundToInt(), true))
  186. return false;
  187. }
  188. if (trueIfInAChildWindow)
  189. return true;
  190. return XWindowSystem::getInstance()->contains (windowH, localPos * currentScaleFactor);
  191. }
  192. void toFront (bool makeActive) override
  193. {
  194. if (makeActive)
  195. {
  196. setVisible (true);
  197. grabFocus();
  198. }
  199. XWindowSystem::getInstance()->toFront (windowH, makeActive);
  200. handleBroughtToFront();
  201. }
  202. void toBehind (ComponentPeer* other) override
  203. {
  204. if (auto* otherPeer = dynamic_cast<LinuxComponentPeer*> (other))
  205. {
  206. if (otherPeer->styleFlags & windowIsTemporary)
  207. return;
  208. setMinimised (false);
  209. XWindowSystem::getInstance()->toBehind (windowH, otherPeer->windowH);
  210. }
  211. else
  212. {
  213. jassertfalse; // wrong type of window?
  214. }
  215. }
  216. bool isFocused() const override
  217. {
  218. return XWindowSystem::getInstance()->isFocused (windowH);
  219. }
  220. void grabFocus() override
  221. {
  222. if (XWindowSystem::getInstance()->grabFocus (windowH))
  223. isActiveApplication = true;
  224. }
  225. //==============================================================================
  226. void repaint (const Rectangle<int>& area) override
  227. {
  228. if (repainter != nullptr)
  229. repainter->repaint (area.getIntersection (bounds.withZeroOrigin()));
  230. }
  231. void performAnyPendingRepaintsNow() override
  232. {
  233. if (repainter != nullptr)
  234. repainter->performAnyPendingRepaintsNow();
  235. }
  236. void setIcon (const Image& newIcon) override
  237. {
  238. XWindowSystem::getInstance()->setIcon (windowH, newIcon);
  239. }
  240. double getPlatformScaleFactor() const noexcept override
  241. {
  242. return currentScaleFactor;
  243. }
  244. void setAlpha (float) override {}
  245. bool setAlwaysOnTop (bool) override { return false; }
  246. void textInputRequired (Point<int>, TextInputTarget&) override {}
  247. //==============================================================================
  248. void addOpenGLRepaintListener (Component* dummy)
  249. {
  250. if (dummy != nullptr)
  251. glRepaintListeners.addIfNotAlreadyThere (dummy);
  252. }
  253. void removeOpenGLRepaintListener (Component* dummy)
  254. {
  255. if (dummy != nullptr)
  256. glRepaintListeners.removeAllInstancesOf (dummy);
  257. }
  258. void repaintOpenGLContexts()
  259. {
  260. for (auto* c : glRepaintListeners)
  261. c->handleCommandMessage (0);
  262. }
  263. //==============================================================================
  264. ::Window getParentWindow() { return parentWindow; }
  265. void setParentWindow (::Window newParent) { parentWindow = newParent; }
  266. //==============================================================================
  267. bool isConstrainedNativeWindow() const
  268. {
  269. return constrainer != nullptr
  270. && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable)
  271. && ! isKioskMode();
  272. }
  273. void updateWindowBounds()
  274. {
  275. if (windowH == 0)
  276. {
  277. jassertfalse;
  278. return;
  279. }
  280. if (isConstrainedNativeWindow())
  281. XWindowSystem::getInstance()->updateConstraints (windowH);
  282. auto physicalBounds = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow);
  283. updateScaleFactorFromNewBounds (physicalBounds, true);
  284. bounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds)
  285. : physicalBounds / currentScaleFactor;
  286. updateVBlankTimer();
  287. }
  288. void updateBorderSize()
  289. {
  290. if ((styleFlags & windowHasTitleBar) == 0)
  291. {
  292. windowBorder = ComponentPeer::OptionalBorderSize { BorderSize<int>() };
  293. }
  294. else if (! windowBorder
  295. || ((*windowBorder).getTopAndBottom() == 0 && (*windowBorder).getLeftAndRight() == 0))
  296. {
  297. windowBorder = [&]()
  298. {
  299. if (auto unscaledBorderSize = XWindowSystem::getInstance()->getBorderSize (windowH))
  300. return OptionalBorderSize { (*unscaledBorderSize).multipliedBy (1.0 / currentScaleFactor) };
  301. return OptionalBorderSize {};
  302. }();
  303. }
  304. }
  305. bool setWindowAssociation (::Window windowIn)
  306. {
  307. clearWindowAssociation();
  308. association = { this, windowIn };
  309. return association.isValid();
  310. }
  311. void clearWindowAssociation() { association = {}; }
  312. //==============================================================================
  313. static bool isActiveApplication;
  314. bool focused = false;
  315. private:
  316. //==============================================================================
  317. class LinuxRepaintManager
  318. {
  319. public:
  320. LinuxRepaintManager (LinuxComponentPeer& p)
  321. : peer (p),
  322. isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
  323. {
  324. }
  325. void dispatchDeferredRepaints()
  326. {
  327. XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
  328. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  329. return;
  330. if (! regionsNeedingRepaint.isEmpty())
  331. performAnyPendingRepaintsNow();
  332. else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
  333. image = Image();
  334. }
  335. void repaint (Rectangle<int> area)
  336. {
  337. regionsNeedingRepaint.add (area * peer.currentScaleFactor);
  338. }
  339. void performAnyPendingRepaintsNow()
  340. {
  341. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  342. return;
  343. auto originalRepaintRegion = regionsNeedingRepaint;
  344. regionsNeedingRepaint.clear();
  345. auto totalArea = originalRepaintRegion.getBounds();
  346. if (! totalArea.isEmpty())
  347. {
  348. const auto wasImageNull = image.isNull();
  349. if (wasImageNull || image.getWidth() < totalArea.getWidth()
  350. || image.getHeight() < totalArea.getHeight())
  351. {
  352. image = XWindowSystem::getInstance()->createImage (isSemiTransparentWindow,
  353. totalArea.getWidth(), totalArea.getHeight(),
  354. useARGBImagesForRendering);
  355. if (wasImageNull)
  356. {
  357. // After calling createImage() XWindowSystem::getWindowBounds() will return
  358. // changed coordinates that look like the result of some position
  359. // defaulting mechanism. If we handle a configureNotifyEvent after
  360. // createImage() and before we would issue new, valid coordinates, we will
  361. // apply these default, unwanted coordinates to our window. To avoid that
  362. // we immediately send another positioning message to guarantee that the
  363. // next configureNotifyEvent will read valid values.
  364. //
  365. // This issue only occurs right after peer creation, when the image is
  366. // null. Updating when only the width or height is changed would lead to
  367. // incorrect behaviour.
  368. peer.forceSetBounds (ScalingHelpers::scaledScreenPosToUnscaled (peer.component,
  369. peer.component.getBoundsInParent()),
  370. peer.isFullScreen());
  371. }
  372. }
  373. RectangleList<int> adjustedList (originalRepaintRegion);
  374. adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY());
  375. if (XWindowSystem::getInstance()->canUseARGBImages())
  376. for (auto& i : originalRepaintRegion)
  377. image.clear (i - totalArea.getPosition());
  378. {
  379. auto context = peer.getComponent().getLookAndFeel()
  380. .createGraphicsContext (image, -totalArea.getPosition(), adjustedList);
  381. context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor));
  382. peer.handlePaint (*context);
  383. }
  384. for (auto& i : originalRepaintRegion)
  385. XWindowSystem::getInstance()->blitToWindow (peer.windowH, image, i, totalArea);
  386. }
  387. lastTimeImageUsed = Time::getApproximateMillisecondCounter();
  388. }
  389. private:
  390. LinuxComponentPeer& peer;
  391. const bool isSemiTransparentWindow;
  392. Image image;
  393. uint32 lastTimeImageUsed = 0;
  394. RectangleList<int> regionsNeedingRepaint;
  395. bool useARGBImagesForRendering = XWindowSystem::getInstance()->canUseARGBImages();
  396. JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
  397. };
  398. class LinuxVBlankManager : public Timer
  399. {
  400. public:
  401. explicit LinuxVBlankManager (std::function<void()> cb) : callback (std::move (cb))
  402. {
  403. jassert (callback);
  404. }
  405. ~LinuxVBlankManager() override { stopTimer(); }
  406. //==============================================================================
  407. void timerCallback() override { callback(); }
  408. private:
  409. std::function<void()> callback;
  410. JUCE_DECLARE_NON_COPYABLE (LinuxVBlankManager)
  411. JUCE_DECLARE_NON_MOVEABLE (LinuxVBlankManager)
  412. };
  413. //==============================================================================
  414. template <typename This>
  415. static Point<float> localToGlobal (This& t, Point<float> relativePosition)
  416. {
  417. return relativePosition + t.getScreenPosition (false).toFloat();
  418. }
  419. template <typename This>
  420. static Point<float> globalToLocal (This& t, Point<float> screenPosition)
  421. {
  422. return screenPosition - t.getScreenPosition (false).toFloat();
  423. }
  424. //==============================================================================
  425. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  426. {
  427. static StringArray possibleSettings { XWindowSystem::getWindowScalingFactorSettingName(),
  428. "Gdk/UnscaledDPI",
  429. "Xft/DPI" };
  430. if (possibleSettings.contains (settingThatHasChanged.name))
  431. forceDisplayUpdate();
  432. }
  433. void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
  434. {
  435. Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
  436. const auto& desktop = Desktop::getInstance();
  437. if (auto* display = desktop.getDisplays().getDisplayForRect (newBounds.translated (translation.x, translation.y),
  438. isPhysical))
  439. {
  440. auto newScaleFactor = display->scale / desktop.getGlobalScaleFactor();
  441. if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
  442. {
  443. currentScaleFactor = newScaleFactor;
  444. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
  445. }
  446. }
  447. }
  448. void onVBlank()
  449. {
  450. vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
  451. if (repainter != nullptr)
  452. repainter->dispatchDeferredRepaints();
  453. }
  454. void updateVBlankTimer()
  455. {
  456. if (auto* display = Desktop::getInstance().getDisplays().getDisplayForRect (bounds))
  457. {
  458. // Some systems fail to set an explicit refresh rate, or ask for a refresh rate of 0
  459. // (observed on Raspbian Bullseye over VNC). In these situations, use a fallback value.
  460. const auto newIntFrequencyHz = roundToInt (display->verticalFrequencyHz.value_or (0.0));
  461. const auto frequencyToUse = newIntFrequencyHz != 0 ? newIntFrequencyHz : 100;
  462. if (vBlankManager.getTimerInterval() != frequencyToUse)
  463. vBlankManager.startTimerHz (frequencyToUse);
  464. }
  465. }
  466. //==============================================================================
  467. std::unique_ptr<LinuxRepaintManager> repainter;
  468. LinuxVBlankManager vBlankManager { [this]() { onVBlank(); } };
  469. ::Window windowH = {}, parentWindow = {};
  470. Rectangle<int> bounds;
  471. ComponentPeer::OptionalBorderSize windowBorder;
  472. bool fullScreen = false, isAlwaysOnTop = false;
  473. double currentScaleFactor = 1.0;
  474. Array<Component*> glRepaintListeners;
  475. ScopedWindowAssociation association;
  476. //==============================================================================
  477. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
  478. };
  479. bool LinuxComponentPeer::isActiveApplication = false;
  480. //==============================================================================
  481. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  482. {
  483. return new LinuxComponentPeer (*this, styleFlags, (::Window) nativeWindowToAttachTo);
  484. }
  485. //==============================================================================
  486. JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return LinuxComponentPeer::isActiveApplication; }
  487. JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  488. JUCE_API void JUCE_CALLTYPE Process::hide() {}
  489. //==============================================================================
  490. void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
  491. {
  492. if (enableOrDisable)
  493. comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea);
  494. }
  495. void Displays::findDisplays (float masterScale)
  496. {
  497. if (XWindowSystem::getInstance()->getDisplay() != nullptr)
  498. {
  499. displays = XWindowSystem::getInstance()->findDisplays (masterScale);
  500. if (! displays.isEmpty())
  501. updateToLogical();
  502. }
  503. }
  504. bool Desktop::canUseSemiTransparentWindows() noexcept
  505. {
  506. return XWindowSystem::getInstance()->canUseSemiTransparentWindows();
  507. }
  508. class Desktop::NativeDarkModeChangeDetectorImpl : private XWindowSystemUtilities::XSettings::Listener
  509. {
  510. public:
  511. NativeDarkModeChangeDetectorImpl()
  512. {
  513. const auto* windowSystem = XWindowSystem::getInstance();
  514. if (auto* xSettings = windowSystem->getXSettings())
  515. xSettings->addListener (this);
  516. darkModeEnabled = windowSystem->isDarkModeActive();
  517. }
  518. ~NativeDarkModeChangeDetectorImpl() override
  519. {
  520. if (auto* windowSystem = XWindowSystem::getInstanceWithoutCreating())
  521. if (auto* xSettings = windowSystem->getXSettings())
  522. xSettings->removeListener (this);
  523. }
  524. bool isDarkModeEnabled() const noexcept { return darkModeEnabled; }
  525. private:
  526. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  527. {
  528. if (settingThatHasChanged.name == XWindowSystem::getThemeNameSettingName())
  529. {
  530. const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, XWindowSystem::getInstance()->isDarkModeActive());
  531. if (darkModeEnabled != wasDarkModeEnabled)
  532. Desktop::getInstance().darkModeChanged();
  533. }
  534. }
  535. bool darkModeEnabled = false;
  536. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  537. };
  538. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  539. {
  540. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  541. }
  542. bool Desktop::isDarkModeActive() const
  543. {
  544. return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled();
  545. }
  546. static bool screenSaverAllowed = true;
  547. void Desktop::setScreenSaverEnabled (bool isEnabled)
  548. {
  549. if (screenSaverAllowed != isEnabled)
  550. {
  551. screenSaverAllowed = isEnabled;
  552. XWindowSystem::getInstance()->setScreenSaverEnabled (screenSaverAllowed);
  553. }
  554. }
  555. bool Desktop::isScreenSaverEnabled()
  556. {
  557. return screenSaverAllowed;
  558. }
  559. double Desktop::getDefaultMasterScale() { return 1.0; }
  560. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; }
  561. void Desktop::allowedOrientationsChanged() {}
  562. //==============================================================================
  563. bool MouseInputSource::SourceList::addSource()
  564. {
  565. if (sources.isEmpty())
  566. {
  567. addSource (0, MouseInputSource::InputSourceType::mouse);
  568. return true;
  569. }
  570. return false;
  571. }
  572. bool MouseInputSource::SourceList::canUseTouch()
  573. {
  574. return false;
  575. }
  576. Point<float> MouseInputSource::getCurrentRawMousePosition()
  577. {
  578. return Desktop::getInstance().getDisplays().physicalToLogical (XWindowSystem::getInstance()->getCurrentMousePosition());
  579. }
  580. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  581. {
  582. XWindowSystem::getInstance()->setMousePosition (Desktop::getInstance().getDisplays().logicalToPhysical (newPosition));
  583. }
  584. //==============================================================================
  585. class MouseCursor::PlatformSpecificHandle
  586. {
  587. public:
  588. explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type)
  589. : cursorHandle (makeHandle (type)) {}
  590. explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info)
  591. : cursorHandle (makeHandle (info)) {}
  592. ~PlatformSpecificHandle()
  593. {
  594. if (cursorHandle != Cursor{})
  595. XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle);
  596. }
  597. static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer)
  598. {
  599. const auto cursor = handle != nullptr ? handle->cursorHandle : Cursor{};
  600. if (peer != nullptr)
  601. XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), cursor);
  602. }
  603. private:
  604. static Cursor makeHandle (const CustomMouseCursorInfo& info)
  605. {
  606. const auto image = info.image.getImage();
  607. return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image.rescaled ((int) (image.getWidth() / info.image.getScale()),
  608. (int) (image.getHeight() / info.image.getScale())), info.hotspot);
  609. }
  610. static Cursor makeHandle (MouseCursor::StandardCursorType type)
  611. {
  612. return XWindowSystem::getInstance()->createStandardMouseCursor (type);
  613. }
  614. Cursor cursorHandle;
  615. //==============================================================================
  616. JUCE_DECLARE_NON_COPYABLE (PlatformSpecificHandle)
  617. JUCE_DECLARE_NON_MOVEABLE (PlatformSpecificHandle)
  618. };
  619. //==============================================================================
  620. static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
  621. {
  622. if (sourceComp == nullptr)
  623. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  624. sourceComp = draggingSource->getComponentUnderMouse();
  625. if (sourceComp != nullptr)
  626. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (sourceComp->getPeer()))
  627. return lp;
  628. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  629. return nullptr;
  630. }
  631. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  632. Component* sourceComp, std::function<void()> callback)
  633. {
  634. if (files.isEmpty())
  635. return false;
  636. if (auto* peer = getPeerForDragEvent (sourceComp))
  637. return XWindowSystem::getInstance()->externalDragFileInit (peer, files, canMoveFiles, std::move (callback));
  638. // This method must be called in response to a component's mouseDown or mouseDrag event!
  639. jassertfalse;
  640. return false;
  641. }
  642. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
  643. std::function<void()> callback)
  644. {
  645. if (text.isEmpty())
  646. return false;
  647. if (auto* peer = getPeerForDragEvent (sourceComp))
  648. return XWindowSystem::getInstance()->externalDragTextInit (peer, text, std::move (callback));
  649. // This method must be called in response to a component's mouseDown or mouseDrag event!
  650. jassertfalse;
  651. return false;
  652. }
  653. //==============================================================================
  654. void SystemClipboard::copyTextToClipboard (const String& clipText)
  655. {
  656. XWindowSystem::getInstance()->copyTextToClipboard (clipText);
  657. }
  658. String SystemClipboard::getTextFromClipboard()
  659. {
  660. return XWindowSystem::getInstance()->getTextFromClipboard();
  661. }
  662. //==============================================================================
  663. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  664. {
  665. return XWindowSystem::getInstance()->isKeyCurrentlyDown (keyCode);
  666. }
  667. void LookAndFeel::playAlertSound()
  668. {
  669. std::cout << "\a" << std::flush;
  670. }
  671. //==============================================================================
  672. static int showDialog (const MessageBoxOptions& options,
  673. ModalComponentManager::Callback* callback,
  674. Async async)
  675. {
  676. const auto dummyCallback = [] (int) {};
  677. switch (options.getNumButtons())
  678. {
  679. case 2:
  680. {
  681. if (async == Async::yes && callback == nullptr)
  682. callback = ModalCallbackFunction::create (dummyCallback);
  683. return AlertWindow::showOkCancelBox (options.getIconType(),
  684. options.getTitle(),
  685. options.getMessage(),
  686. options.getButtonText (0),
  687. options.getButtonText (1),
  688. options.getAssociatedComponent(),
  689. callback) ? 1 : 0;
  690. }
  691. case 3:
  692. {
  693. if (async == Async::yes && callback == nullptr)
  694. callback = ModalCallbackFunction::create (dummyCallback);
  695. return AlertWindow::showYesNoCancelBox (options.getIconType(),
  696. options.getTitle(),
  697. options.getMessage(),
  698. options.getButtonText (0),
  699. options.getButtonText (1),
  700. options.getButtonText (2),
  701. options.getAssociatedComponent(),
  702. callback);
  703. }
  704. case 1:
  705. default:
  706. break;
  707. }
  708. #if JUCE_MODAL_LOOPS_PERMITTED
  709. if (async == Async::no)
  710. {
  711. AlertWindow::showMessageBox (options.getIconType(),
  712. options.getTitle(),
  713. options.getMessage(),
  714. options.getButtonText (0),
  715. options.getAssociatedComponent());
  716. }
  717. else
  718. #endif
  719. {
  720. AlertWindow::showMessageBoxAsync (options.getIconType(),
  721. options.getTitle(),
  722. options.getMessage(),
  723. options.getButtonText (0),
  724. options.getAssociatedComponent(),
  725. callback);
  726. }
  727. return 0;
  728. }
  729. #if JUCE_MODAL_LOOPS_PERMITTED
  730. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  731. const String& title, const String& message,
  732. Component* /*associatedComponent*/)
  733. {
  734. AlertWindow::showMessageBox (iconType, title, message);
  735. }
  736. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  737. {
  738. return showDialog (options, nullptr, Async::no);
  739. }
  740. #endif
  741. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  742. const String& title, const String& message,
  743. Component* associatedComponent,
  744. ModalComponentManager::Callback* callback)
  745. {
  746. AlertWindow::showMessageBoxAsync (iconType, title, message, {}, associatedComponent, callback);
  747. }
  748. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  749. const String& title, const String& message,
  750. Component* associatedComponent,
  751. ModalComponentManager::Callback* callback)
  752. {
  753. return AlertWindow::showOkCancelBox (iconType, title, message, {}, {}, associatedComponent, callback);
  754. }
  755. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  756. const String& title, const String& message,
  757. Component* associatedComponent,
  758. ModalComponentManager::Callback* callback)
  759. {
  760. return AlertWindow::showYesNoCancelBox (iconType, title, message, {}, {}, {},
  761. associatedComponent, callback);
  762. }
  763. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  764. const String& title, const String& message,
  765. Component* associatedComponent,
  766. ModalComponentManager::Callback* callback)
  767. {
  768. return AlertWindow::showOkCancelBox (iconType, title, message, TRANS("Yes"), TRANS("No"),
  769. associatedComponent, callback);
  770. }
  771. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  772. ModalComponentManager::Callback* callback)
  773. {
  774. showDialog (options, callback, Async::yes);
  775. }
  776. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  777. std::function<void (int)> callback)
  778. {
  779. showAsync (options, ModalCallbackFunction::create (callback));
  780. }
  781. //==============================================================================
  782. Image juce_createIconForFile (const File&)
  783. {
  784. return {};
  785. }
  786. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy);
  787. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
  788. {
  789. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  790. linuxPeer->addOpenGLRepaintListener (dummy);
  791. }
  792. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy);
  793. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
  794. {
  795. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  796. linuxPeer->removeOpenGLRepaintListener (dummy);
  797. }
  798. } // namespace juce