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.

876 lines
31KB

  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. class LinuxComponentPeer final : public ComponentPeer,
  22. private XWindowSystemUtilities::XSettings::Listener
  23. {
  24. public:
  25. LinuxComponentPeer (Component& comp, int windowStyleFlags, ::Window parentToAddTo)
  26. : ComponentPeer (comp, windowStyleFlags),
  27. isAlwaysOnTop (comp.isAlwaysOnTop())
  28. {
  29. // it's dangerous to create a window on a thread other than the message thread.
  30. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  31. const auto* instance = XWindowSystem::getInstance();
  32. if (! instance->isX11Available())
  33. return;
  34. if (isAlwaysOnTop)
  35. ++WindowUtilsInternal::numAlwaysOnTopPeers;
  36. repainter = std::make_unique<LinuxRepaintManager> (*this);
  37. windowH = instance->createWindow (parentToAddTo, this);
  38. parentWindow = parentToAddTo;
  39. setTitle (component.getName());
  40. if (auto* xSettings = instance->getXSettings())
  41. xSettings->addListener (this);
  42. getNativeRealtimeModifiers = []() -> ModifierKeys { return XWindowSystem::getInstance()->getNativeRealtimeModifiers(); };
  43. updateVBlankTimer();
  44. }
  45. ~LinuxComponentPeer() override
  46. {
  47. // it's dangerous to delete a window on a thread other than the message thread.
  48. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  49. auto* instance = XWindowSystem::getInstance();
  50. repainter = nullptr;
  51. instance->destroyWindow (windowH);
  52. if (auto* xSettings = instance->getXSettings())
  53. xSettings->removeListener (this);
  54. if (isAlwaysOnTop)
  55. --WindowUtilsInternal::numAlwaysOnTopPeers;
  56. }
  57. ::Window getWindowHandle() const noexcept
  58. {
  59. return windowH;
  60. }
  61. //==============================================================================
  62. void* getNativeHandle() const override
  63. {
  64. return reinterpret_cast<void*> (getWindowHandle());
  65. }
  66. //==============================================================================
  67. void forceSetBounds (const Rectangle<int>& correctedNewBounds, bool isNowFullScreen)
  68. {
  69. bounds = correctedNewBounds;
  70. updateScaleFactorFromNewBounds (bounds, false);
  71. auto physicalBounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds)
  72. : bounds * currentScaleFactor;
  73. WeakReference<Component> deletionChecker (&component);
  74. XWindowSystem::getInstance()->setBounds (windowH, physicalBounds, isNowFullScreen);
  75. fullScreen = isNowFullScreen;
  76. if (deletionChecker != nullptr)
  77. {
  78. updateBorderSize();
  79. handleMovedOrResized();
  80. }
  81. }
  82. void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) override
  83. {
  84. const auto correctedNewBounds = newBounds.withSize (jmax (1, newBounds.getWidth()),
  85. jmax (1, newBounds.getHeight()));
  86. if (bounds != correctedNewBounds || fullScreen != isNowFullScreen)
  87. forceSetBounds (correctedNewBounds, isNowFullScreen);
  88. }
  89. Point<int> getScreenPosition (bool physical) const
  90. {
  91. auto physicalParentPosition = XWindowSystem::getInstance()->getPhysicalParentScreenPosition();
  92. auto parentPosition = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalParentPosition)
  93. : physicalParentPosition / currentScaleFactor;
  94. auto screenBounds = parentWindow == 0 ? bounds
  95. : bounds.translated (parentPosition.x, parentPosition.y);
  96. if (physical)
  97. return parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft())
  98. : screenBounds.getTopLeft() * currentScaleFactor;
  99. return screenBounds.getTopLeft();
  100. }
  101. Rectangle<int> getBounds() const override
  102. {
  103. return bounds;
  104. }
  105. OptionalBorderSize getFrameSizeIfPresent() const override
  106. {
  107. return windowBorder;
  108. }
  109. BorderSize<int> getFrameSize() const override
  110. {
  111. const auto optionalBorderSize = getFrameSizeIfPresent();
  112. return optionalBorderSize ? (*optionalBorderSize) : BorderSize<int>();
  113. }
  114. Point<float> localToGlobal (Point<float> relativePosition) override
  115. {
  116. return localToGlobal (*this, relativePosition);
  117. }
  118. Point<float> globalToLocal (Point<float> screenPosition) override
  119. {
  120. return globalToLocal (*this, screenPosition);
  121. }
  122. using ComponentPeer::localToGlobal;
  123. using ComponentPeer::globalToLocal;
  124. //==============================================================================
  125. StringArray getAvailableRenderingEngines() override
  126. {
  127. return { "Software Renderer" };
  128. }
  129. void setVisible (bool shouldBeVisible) override
  130. {
  131. XWindowSystem::getInstance()->setVisible (windowH, shouldBeVisible);
  132. }
  133. void setTitle (const String& title) override
  134. {
  135. XWindowSystem::getInstance()->setTitle (windowH, title);
  136. }
  137. void setMinimised (bool shouldBeMinimised) override
  138. {
  139. if (shouldBeMinimised)
  140. XWindowSystem::getInstance()->setMinimised (windowH, shouldBeMinimised);
  141. else
  142. setVisible (true);
  143. }
  144. bool isMinimised() const override
  145. {
  146. return XWindowSystem::getInstance()->isMinimised (windowH);
  147. }
  148. void setFullScreen (bool shouldBeFullScreen) override
  149. {
  150. auto r = lastNonFullscreenBounds; // (get a copy of this before de-minimising)
  151. setMinimised (false);
  152. if (fullScreen != shouldBeFullScreen)
  153. {
  154. const auto usingNativeTitleBar = ((styleFlags & windowHasTitleBar) != 0);
  155. if (usingNativeTitleBar)
  156. XWindowSystem::getInstance()->setMaximised (windowH, shouldBeFullScreen);
  157. if (shouldBeFullScreen)
  158. r = usingNativeTitleBar ? XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow)
  159. : Desktop::getInstance().getDisplays().getDisplayForRect (bounds)->userArea;
  160. if (! r.isEmpty())
  161. setBounds (detail::ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
  162. component.repaint();
  163. }
  164. }
  165. bool isFullScreen() const override
  166. {
  167. return fullScreen;
  168. }
  169. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  170. {
  171. if (! bounds.withZeroOrigin().contains (localPos))
  172. return false;
  173. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  174. {
  175. auto* c = Desktop::getInstance().getComponent (i);
  176. if (c == &component)
  177. break;
  178. if (! c->isVisible())
  179. continue;
  180. auto* otherPeer = c->getPeer();
  181. jassert (otherPeer == nullptr || dynamic_cast<LinuxComponentPeer*> (c->getPeer()) != nullptr);
  182. if (auto* peer = static_cast<LinuxComponentPeer*> (otherPeer))
  183. if (peer->contains (globalToLocal (*peer, localToGlobal (*this, localPos.toFloat())).roundToInt(), 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. updateVBlankTimer();
  285. }
  286. void updateBorderSize()
  287. {
  288. if ((styleFlags & windowHasTitleBar) == 0)
  289. {
  290. windowBorder = ComponentPeer::OptionalBorderSize { BorderSize<int>() };
  291. }
  292. else if (! windowBorder
  293. || ((*windowBorder).getTopAndBottom() == 0 && (*windowBorder).getLeftAndRight() == 0))
  294. {
  295. windowBorder = [&]()
  296. {
  297. if (auto unscaledBorderSize = XWindowSystem::getInstance()->getBorderSize (windowH))
  298. return OptionalBorderSize { (*unscaledBorderSize).multipliedBy (1.0 / currentScaleFactor) };
  299. return OptionalBorderSize {};
  300. }();
  301. }
  302. }
  303. bool setWindowAssociation (::Window windowIn)
  304. {
  305. clearWindowAssociation();
  306. association = { this, windowIn };
  307. return association.isValid();
  308. }
  309. void clearWindowAssociation() { association = {}; }
  310. void startHostManagedResize (Point<int> mouseDownPosition,
  311. ResizableBorderComponent::Zone zone) override
  312. {
  313. XWindowSystem::getInstance()->startHostManagedResize (windowH, mouseDownPosition, zone);
  314. }
  315. //==============================================================================
  316. static bool isActiveApplication;
  317. bool focused = false;
  318. private:
  319. //==============================================================================
  320. class LinuxRepaintManager
  321. {
  322. public:
  323. LinuxRepaintManager (LinuxComponentPeer& p)
  324. : peer (p),
  325. isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
  326. {
  327. }
  328. void dispatchDeferredRepaints()
  329. {
  330. XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
  331. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  332. return;
  333. if (! regionsNeedingRepaint.isEmpty())
  334. performAnyPendingRepaintsNow();
  335. else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
  336. image = Image();
  337. }
  338. void repaint (Rectangle<int> area)
  339. {
  340. regionsNeedingRepaint.add (area * peer.currentScaleFactor);
  341. }
  342. void performAnyPendingRepaintsNow()
  343. {
  344. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  345. return;
  346. auto originalRepaintRegion = regionsNeedingRepaint;
  347. regionsNeedingRepaint.clear();
  348. auto totalArea = originalRepaintRegion.getBounds();
  349. if (! totalArea.isEmpty())
  350. {
  351. const auto wasImageNull = image.isNull();
  352. if (wasImageNull || image.getWidth() < totalArea.getWidth()
  353. || image.getHeight() < totalArea.getHeight())
  354. {
  355. image = XWindowSystem::getInstance()->createImage (isSemiTransparentWindow,
  356. totalArea.getWidth(), totalArea.getHeight(),
  357. useARGBImagesForRendering);
  358. if (wasImageNull)
  359. {
  360. // After calling createImage() XWindowSystem::getWindowBounds() will return
  361. // changed coordinates that look like the result of some position
  362. // defaulting mechanism. If we handle a configureNotifyEvent after
  363. // createImage() and before we would issue new, valid coordinates, we will
  364. // apply these default, unwanted coordinates to our window. To avoid that
  365. // we immediately send another positioning message to guarantee that the
  366. // next configureNotifyEvent will read valid values.
  367. //
  368. // This issue only occurs right after peer creation, when the image is
  369. // null. Updating when only the width or height is changed would lead to
  370. // incorrect behaviour.
  371. peer.forceSetBounds (detail::ScalingHelpers::scaledScreenPosToUnscaled (peer.component, peer.component.getBoundsInParent()),
  372. peer.isFullScreen());
  373. }
  374. }
  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. }
  391. private:
  392. LinuxComponentPeer& peer;
  393. const bool isSemiTransparentWindow;
  394. Image image;
  395. uint32 lastTimeImageUsed = 0;
  396. RectangleList<int> regionsNeedingRepaint;
  397. bool useARGBImagesForRendering = XWindowSystem::getInstance()->canUseARGBImages();
  398. JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
  399. };
  400. class LinuxVBlankManager final : public Timer
  401. {
  402. public:
  403. explicit LinuxVBlankManager (std::function<void()> cb) : callback (std::move (cb))
  404. {
  405. jassert (callback);
  406. }
  407. ~LinuxVBlankManager() override { stopTimer(); }
  408. //==============================================================================
  409. void timerCallback() override { callback(); }
  410. private:
  411. std::function<void()> callback;
  412. JUCE_DECLARE_NON_COPYABLE (LinuxVBlankManager)
  413. JUCE_DECLARE_NON_MOVEABLE (LinuxVBlankManager)
  414. };
  415. //==============================================================================
  416. template <typename This>
  417. static Point<float> localToGlobal (This& t, Point<float> relativePosition)
  418. {
  419. return relativePosition + t.getScreenPosition (false).toFloat();
  420. }
  421. template <typename This>
  422. static Point<float> globalToLocal (This& t, Point<float> screenPosition)
  423. {
  424. return screenPosition - t.getScreenPosition (false).toFloat();
  425. }
  426. //==============================================================================
  427. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  428. {
  429. static StringArray possibleSettings { XWindowSystem::getWindowScalingFactorSettingName(),
  430. "Gdk/UnscaledDPI",
  431. "Xft/DPI" };
  432. if (possibleSettings.contains (settingThatHasChanged.name))
  433. forceDisplayUpdate();
  434. }
  435. void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
  436. {
  437. Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
  438. const auto& desktop = Desktop::getInstance();
  439. if (auto* display = desktop.getDisplays().getDisplayForRect (newBounds.translated (translation.x, translation.y),
  440. isPhysical))
  441. {
  442. auto newScaleFactor = display->scale / desktop.getGlobalScaleFactor();
  443. if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
  444. {
  445. currentScaleFactor = newScaleFactor;
  446. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
  447. }
  448. }
  449. }
  450. void onVBlank()
  451. {
  452. vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
  453. if (repainter != nullptr)
  454. repainter->dispatchDeferredRepaints();
  455. }
  456. void updateVBlankTimer()
  457. {
  458. if (auto* display = Desktop::getInstance().getDisplays().getDisplayForRect (bounds))
  459. {
  460. // Some systems fail to set an explicit refresh rate, or ask for a refresh rate of 0
  461. // (observed on Raspbian Bullseye over VNC). In these situations, use a fallback value.
  462. const auto newIntFrequencyHz = roundToInt (display->verticalFrequencyHz.value_or (0.0));
  463. const auto frequencyToUse = newIntFrequencyHz != 0 ? newIntFrequencyHz : 100;
  464. if (vBlankManager.getTimerInterval() != frequencyToUse)
  465. vBlankManager.startTimerHz (frequencyToUse);
  466. }
  467. }
  468. //==============================================================================
  469. std::unique_ptr<LinuxRepaintManager> repainter;
  470. LinuxVBlankManager vBlankManager { [this]() { onVBlank(); } };
  471. ::Window windowH = {}, parentWindow = {};
  472. Rectangle<int> bounds;
  473. ComponentPeer::OptionalBorderSize windowBorder;
  474. bool fullScreen = false, isAlwaysOnTop = false;
  475. double currentScaleFactor = 1.0;
  476. Array<Component*> glRepaintListeners;
  477. ScopedWindowAssociation association;
  478. //==============================================================================
  479. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
  480. };
  481. bool LinuxComponentPeer::isActiveApplication = false;
  482. //==============================================================================
  483. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  484. {
  485. return new LinuxComponentPeer (*this, styleFlags, (::Window) nativeWindowToAttachTo);
  486. }
  487. //==============================================================================
  488. JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return LinuxComponentPeer::isActiveApplication; }
  489. JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  490. JUCE_API void JUCE_CALLTYPE Process::hide() {}
  491. //==============================================================================
  492. void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
  493. {
  494. if (enableOrDisable)
  495. comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea);
  496. }
  497. void Displays::findDisplays (float masterScale)
  498. {
  499. if (XWindowSystem::getInstance()->getDisplay() != nullptr)
  500. {
  501. displays = XWindowSystem::getInstance()->findDisplays (masterScale);
  502. if (! displays.isEmpty())
  503. updateToLogical();
  504. }
  505. }
  506. bool Desktop::canUseSemiTransparentWindows() noexcept
  507. {
  508. return XWindowSystem::getInstance()->canUseSemiTransparentWindows();
  509. }
  510. class Desktop::NativeDarkModeChangeDetectorImpl : private XWindowSystemUtilities::XSettings::Listener
  511. {
  512. public:
  513. NativeDarkModeChangeDetectorImpl()
  514. {
  515. const auto* windowSystem = XWindowSystem::getInstance();
  516. if (auto* xSettings = windowSystem->getXSettings())
  517. xSettings->addListener (this);
  518. darkModeEnabled = windowSystem->isDarkModeActive();
  519. }
  520. ~NativeDarkModeChangeDetectorImpl() override
  521. {
  522. if (auto* windowSystem = XWindowSystem::getInstanceWithoutCreating())
  523. if (auto* xSettings = windowSystem->getXSettings())
  524. xSettings->removeListener (this);
  525. }
  526. bool isDarkModeEnabled() const noexcept { return darkModeEnabled; }
  527. private:
  528. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  529. {
  530. if (settingThatHasChanged.name == XWindowSystem::getThemeNameSettingName())
  531. {
  532. const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, XWindowSystem::getInstance()->isDarkModeActive());
  533. if (darkModeEnabled != wasDarkModeEnabled)
  534. Desktop::getInstance().darkModeChanged();
  535. }
  536. }
  537. bool darkModeEnabled = false;
  538. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  539. };
  540. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  541. {
  542. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  543. }
  544. bool Desktop::isDarkModeActive() const
  545. {
  546. return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled();
  547. }
  548. static bool screenSaverAllowed = true;
  549. void Desktop::setScreenSaverEnabled (bool isEnabled)
  550. {
  551. if (screenSaverAllowed != isEnabled)
  552. {
  553. screenSaverAllowed = isEnabled;
  554. XWindowSystem::getInstance()->setScreenSaverEnabled (screenSaverAllowed);
  555. }
  556. }
  557. bool Desktop::isScreenSaverEnabled()
  558. {
  559. return screenSaverAllowed;
  560. }
  561. double Desktop::getDefaultMasterScale() { return 1.0; }
  562. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; }
  563. void Desktop::allowedOrientationsChanged() {}
  564. //==============================================================================
  565. bool detail::MouseInputSourceList::addSource()
  566. {
  567. if (sources.isEmpty())
  568. {
  569. addSource (0, MouseInputSource::InputSourceType::mouse);
  570. return true;
  571. }
  572. return false;
  573. }
  574. bool detail::MouseInputSourceList::canUseTouch() const
  575. {
  576. return false;
  577. }
  578. Point<float> MouseInputSource::getCurrentRawMousePosition()
  579. {
  580. return Desktop::getInstance().getDisplays().physicalToLogical (XWindowSystem::getInstance()->getCurrentMousePosition());
  581. }
  582. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  583. {
  584. XWindowSystem::getInstance()->setMousePosition (Desktop::getInstance().getDisplays().logicalToPhysical (newPosition));
  585. }
  586. //==============================================================================
  587. class MouseCursor::PlatformSpecificHandle
  588. {
  589. public:
  590. explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type)
  591. : cursorHandle (makeHandle (type)) {}
  592. explicit PlatformSpecificHandle (const detail::CustomMouseCursorInfo& info)
  593. : cursorHandle (makeHandle (info)) {}
  594. ~PlatformSpecificHandle()
  595. {
  596. if (cursorHandle != Cursor{})
  597. XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle);
  598. }
  599. static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer)
  600. {
  601. const auto cursor = handle != nullptr ? handle->cursorHandle : Cursor{};
  602. if (peer != nullptr)
  603. XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), cursor);
  604. }
  605. private:
  606. static Cursor makeHandle (const detail::CustomMouseCursorInfo& info)
  607. {
  608. const auto image = info.image.getImage();
  609. return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image.rescaled ((int) (image.getWidth() / info.image.getScale()),
  610. (int) (image.getHeight() / info.image.getScale())), info.hotspot);
  611. }
  612. static Cursor makeHandle (MouseCursor::StandardCursorType type)
  613. {
  614. return XWindowSystem::getInstance()->createStandardMouseCursor (type);
  615. }
  616. Cursor cursorHandle;
  617. //==============================================================================
  618. JUCE_DECLARE_NON_COPYABLE (PlatformSpecificHandle)
  619. JUCE_DECLARE_NON_MOVEABLE (PlatformSpecificHandle)
  620. };
  621. //==============================================================================
  622. static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
  623. {
  624. if (sourceComp == nullptr)
  625. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  626. sourceComp = draggingSource->getComponentUnderMouse();
  627. if (sourceComp != nullptr)
  628. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (sourceComp->getPeer()))
  629. return lp;
  630. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  631. return nullptr;
  632. }
  633. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  634. Component* sourceComp, std::function<void()> callback)
  635. {
  636. if (files.isEmpty())
  637. return false;
  638. if (auto* peer = getPeerForDragEvent (sourceComp))
  639. return XWindowSystem::getInstance()->externalDragFileInit (peer, files, canMoveFiles, std::move (callback));
  640. // This method must be called in response to a component's mouseDown or mouseDrag event!
  641. jassertfalse;
  642. return false;
  643. }
  644. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
  645. std::function<void()> callback)
  646. {
  647. if (text.isEmpty())
  648. return false;
  649. if (auto* peer = getPeerForDragEvent (sourceComp))
  650. return XWindowSystem::getInstance()->externalDragTextInit (peer, text, std::move (callback));
  651. // This method must be called in response to a component's mouseDown or mouseDrag event!
  652. jassertfalse;
  653. return false;
  654. }
  655. //==============================================================================
  656. void SystemClipboard::copyTextToClipboard (const String& clipText)
  657. {
  658. XWindowSystem::getInstance()->copyTextToClipboard (clipText);
  659. }
  660. String SystemClipboard::getTextFromClipboard()
  661. {
  662. return XWindowSystem::getInstance()->getTextFromClipboard();
  663. }
  664. //==============================================================================
  665. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  666. {
  667. return XWindowSystem::getInstance()->isKeyCurrentlyDown (keyCode);
  668. }
  669. void LookAndFeel::playAlertSound()
  670. {
  671. std::cout << "\a" << std::flush;
  672. }
  673. //==============================================================================
  674. Image detail::WindowingHelpers::createIconForFile (const File&)
  675. {
  676. return {};
  677. }
  678. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy);
  679. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
  680. {
  681. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  682. linuxPeer->addOpenGLRepaintListener (dummy);
  683. }
  684. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy);
  685. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
  686. {
  687. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  688. linuxPeer->removeOpenGLRepaintListener (dummy);
  689. }
  690. } // namespace juce