Audio plugin host https://kx.studio/carla
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.

juce_linux_Windowing.cpp 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  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. }
  287. void updateBorderSize()
  288. {
  289. if ((styleFlags & windowHasTitleBar) == 0)
  290. {
  291. windowBorder = ComponentPeer::OptionalBorderSize { BorderSize<int>() };
  292. }
  293. else if (! windowBorder
  294. || ((*windowBorder).getTopAndBottom() == 0 && (*windowBorder).getLeftAndRight() == 0))
  295. {
  296. windowBorder = [&]()
  297. {
  298. if (auto unscaledBorderSize = XWindowSystem::getInstance()->getBorderSize (windowH))
  299. return OptionalBorderSize { (*unscaledBorderSize).multipliedBy (1.0 / currentScaleFactor) };
  300. return OptionalBorderSize {};
  301. }();
  302. }
  303. }
  304. //==============================================================================
  305. static bool isActiveApplication;
  306. bool focused = false;
  307. private:
  308. //==============================================================================
  309. class LinuxRepaintManager : public Timer
  310. {
  311. public:
  312. LinuxRepaintManager (LinuxComponentPeer& p)
  313. : peer (p),
  314. isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
  315. {
  316. }
  317. void timerCallback() override
  318. {
  319. XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
  320. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  321. return;
  322. if (! regionsNeedingRepaint.isEmpty())
  323. {
  324. stopTimer();
  325. performAnyPendingRepaintsNow();
  326. }
  327. else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
  328. {
  329. stopTimer();
  330. image = Image();
  331. }
  332. }
  333. void repaint (Rectangle<int> area)
  334. {
  335. if (! isTimerRunning())
  336. startTimer (repaintTimerPeriod);
  337. regionsNeedingRepaint.add (area * peer.currentScaleFactor);
  338. }
  339. void performAnyPendingRepaintsNow()
  340. {
  341. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  342. {
  343. startTimer (repaintTimerPeriod);
  344. return;
  345. }
  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 (ScalingHelpers::scaledScreenPosToUnscaled (peer.component,
  372. peer.component.getBoundsInParent()),
  373. peer.isFullScreen());
  374. }
  375. }
  376. startTimer (repaintTimerPeriod);
  377. RectangleList<int> adjustedList (originalRepaintRegion);
  378. adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY());
  379. if (XWindowSystem::getInstance()->canUseARGBImages())
  380. for (auto& i : originalRepaintRegion)
  381. image.clear (i - totalArea.getPosition());
  382. {
  383. auto context = peer.getComponent().getLookAndFeel()
  384. .createGraphicsContext (image, -totalArea.getPosition(), adjustedList);
  385. context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor));
  386. peer.handlePaint (*context);
  387. }
  388. for (auto& i : originalRepaintRegion)
  389. XWindowSystem::getInstance()->blitToWindow (peer.windowH, image, i, totalArea);
  390. }
  391. lastTimeImageUsed = Time::getApproximateMillisecondCounter();
  392. startTimer (repaintTimerPeriod);
  393. }
  394. private:
  395. enum { repaintTimerPeriod = 1000 / 100 };
  396. LinuxComponentPeer& peer;
  397. const bool isSemiTransparentWindow;
  398. Image image;
  399. uint32 lastTimeImageUsed = 0;
  400. RectangleList<int> regionsNeedingRepaint;
  401. bool useARGBImagesForRendering = XWindowSystem::getInstance()->canUseARGBImages();
  402. JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
  403. };
  404. //==============================================================================
  405. template <typename This>
  406. static Point<float> localToGlobal (This& t, Point<float> relativePosition)
  407. {
  408. return relativePosition + t.getScreenPosition (false).toFloat();
  409. }
  410. template <typename This>
  411. static Point<float> globalToLocal (This& t, Point<float> screenPosition)
  412. {
  413. return screenPosition - t.getScreenPosition (false).toFloat();
  414. }
  415. //==============================================================================
  416. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  417. {
  418. static StringArray possibleSettings { XWindowSystem::getWindowScalingFactorSettingName(),
  419. "Gdk/UnscaledDPI",
  420. "Xft/DPI" };
  421. if (possibleSettings.contains (settingThatHasChanged.name))
  422. forceDisplayUpdate();
  423. }
  424. void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
  425. {
  426. Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
  427. const auto& desktop = Desktop::getInstance();
  428. if (auto* display = desktop.getDisplays().getDisplayForRect (newBounds.translated (translation.x, translation.y),
  429. isPhysical))
  430. {
  431. auto newScaleFactor = display->scale / desktop.getGlobalScaleFactor();
  432. if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
  433. {
  434. currentScaleFactor = newScaleFactor;
  435. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
  436. }
  437. }
  438. }
  439. //==============================================================================
  440. std::unique_ptr<LinuxRepaintManager> repainter;
  441. ::Window windowH = {}, parentWindow = {};
  442. Rectangle<int> bounds;
  443. ComponentPeer::OptionalBorderSize windowBorder;
  444. bool fullScreen = false, isAlwaysOnTop = false;
  445. double currentScaleFactor = 1.0;
  446. Array<Component*> glRepaintListeners;
  447. //==============================================================================
  448. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
  449. };
  450. bool LinuxComponentPeer::isActiveApplication = false;
  451. //==============================================================================
  452. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  453. {
  454. return new LinuxComponentPeer (*this, styleFlags, (::Window) nativeWindowToAttachTo);
  455. }
  456. //==============================================================================
  457. JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return LinuxComponentPeer::isActiveApplication; }
  458. JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  459. JUCE_API void JUCE_CALLTYPE Process::hide() {}
  460. //==============================================================================
  461. void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
  462. {
  463. if (enableOrDisable)
  464. comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea);
  465. }
  466. void Displays::findDisplays (float masterScale)
  467. {
  468. if (XWindowSystem::getInstance()->getDisplay() != nullptr)
  469. {
  470. displays = XWindowSystem::getInstance()->findDisplays (masterScale);
  471. if (! displays.isEmpty())
  472. updateToLogical();
  473. }
  474. }
  475. bool Desktop::canUseSemiTransparentWindows() noexcept
  476. {
  477. return XWindowSystem::getInstance()->canUseSemiTransparentWindows();
  478. }
  479. class Desktop::NativeDarkModeChangeDetectorImpl : private XWindowSystemUtilities::XSettings::Listener
  480. {
  481. public:
  482. NativeDarkModeChangeDetectorImpl()
  483. {
  484. const auto* windowSystem = XWindowSystem::getInstance();
  485. if (auto* xSettings = windowSystem->getXSettings())
  486. xSettings->addListener (this);
  487. darkModeEnabled = windowSystem->isDarkModeActive();
  488. }
  489. ~NativeDarkModeChangeDetectorImpl() override
  490. {
  491. if (auto* windowSystem = XWindowSystem::getInstanceWithoutCreating())
  492. if (auto* xSettings = windowSystem->getXSettings())
  493. xSettings->removeListener (this);
  494. }
  495. bool isDarkModeEnabled() const noexcept { return darkModeEnabled; }
  496. private:
  497. void settingChanged (const XWindowSystemUtilities::XSetting& settingThatHasChanged) override
  498. {
  499. if (settingThatHasChanged.name == XWindowSystem::getThemeNameSettingName())
  500. {
  501. const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, XWindowSystem::getInstance()->isDarkModeActive());
  502. if (darkModeEnabled != wasDarkModeEnabled)
  503. Desktop::getInstance().darkModeChanged();
  504. }
  505. }
  506. bool darkModeEnabled = false;
  507. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  508. };
  509. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  510. {
  511. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  512. }
  513. bool Desktop::isDarkModeActive() const
  514. {
  515. return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled();
  516. }
  517. static bool screenSaverAllowed = true;
  518. void Desktop::setScreenSaverEnabled (bool isEnabled)
  519. {
  520. if (screenSaverAllowed != isEnabled)
  521. {
  522. screenSaverAllowed = isEnabled;
  523. XWindowSystem::getInstance()->setScreenSaverEnabled (screenSaverAllowed);
  524. }
  525. }
  526. bool Desktop::isScreenSaverEnabled()
  527. {
  528. return screenSaverAllowed;
  529. }
  530. double Desktop::getDefaultMasterScale() { return 1.0; }
  531. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; }
  532. void Desktop::allowedOrientationsChanged() {}
  533. //==============================================================================
  534. bool MouseInputSource::SourceList::addSource()
  535. {
  536. if (sources.isEmpty())
  537. {
  538. addSource (0, MouseInputSource::InputSourceType::mouse);
  539. return true;
  540. }
  541. return false;
  542. }
  543. bool MouseInputSource::SourceList::canUseTouch()
  544. {
  545. return false;
  546. }
  547. Point<float> MouseInputSource::getCurrentRawMousePosition()
  548. {
  549. return Desktop::getInstance().getDisplays().physicalToLogical (XWindowSystem::getInstance()->getCurrentMousePosition());
  550. }
  551. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  552. {
  553. XWindowSystem::getInstance()->setMousePosition (Desktop::getInstance().getDisplays().logicalToPhysical (newPosition));
  554. }
  555. //==============================================================================
  556. class MouseCursor::PlatformSpecificHandle
  557. {
  558. public:
  559. explicit PlatformSpecificHandle (const MouseCursor::StandardCursorType type)
  560. : cursorHandle (makeHandle (type)) {}
  561. explicit PlatformSpecificHandle (const CustomMouseCursorInfo& info)
  562. : cursorHandle (makeHandle (info)) {}
  563. ~PlatformSpecificHandle()
  564. {
  565. if (cursorHandle != Cursor{})
  566. XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle);
  567. }
  568. static void showInWindow (PlatformSpecificHandle* handle, ComponentPeer* peer)
  569. {
  570. const auto cursor = handle != nullptr ? handle->cursorHandle : Cursor{};
  571. if (peer != nullptr)
  572. XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), cursor);
  573. }
  574. private:
  575. static Cursor makeHandle (const CustomMouseCursorInfo& info)
  576. {
  577. const auto image = info.image.getImage();
  578. return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image.rescaled ((int) (image.getWidth() / info.image.getScale()),
  579. (int) (image.getHeight() / info.image.getScale())), info.hotspot);
  580. }
  581. static Cursor makeHandle (MouseCursor::StandardCursorType type)
  582. {
  583. return XWindowSystem::getInstance()->createStandardMouseCursor (type);
  584. }
  585. Cursor cursorHandle;
  586. //==============================================================================
  587. JUCE_DECLARE_NON_COPYABLE (PlatformSpecificHandle)
  588. JUCE_DECLARE_NON_MOVEABLE (PlatformSpecificHandle)
  589. };
  590. //==============================================================================
  591. static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
  592. {
  593. if (sourceComp == nullptr)
  594. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  595. sourceComp = draggingSource->getComponentUnderMouse();
  596. if (sourceComp != nullptr)
  597. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (sourceComp->getPeer()))
  598. return lp;
  599. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  600. return nullptr;
  601. }
  602. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  603. Component* sourceComp, std::function<void()> callback)
  604. {
  605. if (files.isEmpty())
  606. return false;
  607. if (auto* peer = getPeerForDragEvent (sourceComp))
  608. return XWindowSystem::getInstance()->externalDragFileInit (peer, files, canMoveFiles, std::move (callback));
  609. // This method must be called in response to a component's mouseDown or mouseDrag event!
  610. jassertfalse;
  611. return false;
  612. }
  613. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
  614. std::function<void()> callback)
  615. {
  616. if (text.isEmpty())
  617. return false;
  618. if (auto* peer = getPeerForDragEvent (sourceComp))
  619. return XWindowSystem::getInstance()->externalDragTextInit (peer, text, std::move (callback));
  620. // This method must be called in response to a component's mouseDown or mouseDrag event!
  621. jassertfalse;
  622. return false;
  623. }
  624. //==============================================================================
  625. void SystemClipboard::copyTextToClipboard (const String& clipText)
  626. {
  627. XWindowSystem::getInstance()->copyTextToClipboard (clipText);
  628. }
  629. String SystemClipboard::getTextFromClipboard()
  630. {
  631. return XWindowSystem::getInstance()->getTextFromClipboard();
  632. }
  633. //==============================================================================
  634. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  635. {
  636. return XWindowSystem::getInstance()->isKeyCurrentlyDown (keyCode);
  637. }
  638. void LookAndFeel::playAlertSound()
  639. {
  640. std::cout << "\a" << std::flush;
  641. }
  642. //==============================================================================
  643. static int showDialog (const MessageBoxOptions& options,
  644. ModalComponentManager::Callback* callback,
  645. Async async)
  646. {
  647. const auto dummyCallback = [] (int) {};
  648. switch (options.getNumButtons())
  649. {
  650. case 2:
  651. {
  652. if (async == Async::yes && callback == nullptr)
  653. callback = ModalCallbackFunction::create (dummyCallback);
  654. return AlertWindow::showOkCancelBox (options.getIconType(),
  655. options.getTitle(),
  656. options.getMessage(),
  657. options.getButtonText (0),
  658. options.getButtonText (1),
  659. options.getAssociatedComponent(),
  660. callback) ? 1 : 0;
  661. }
  662. case 3:
  663. {
  664. if (async == Async::yes && callback == nullptr)
  665. callback = ModalCallbackFunction::create (dummyCallback);
  666. return AlertWindow::showYesNoCancelBox (options.getIconType(),
  667. options.getTitle(),
  668. options.getMessage(),
  669. options.getButtonText (0),
  670. options.getButtonText (1),
  671. options.getButtonText (2),
  672. options.getAssociatedComponent(),
  673. callback);
  674. }
  675. case 1:
  676. default:
  677. break;
  678. }
  679. #if JUCE_MODAL_LOOPS_PERMITTED
  680. if (async == Async::no)
  681. {
  682. AlertWindow::showMessageBox (options.getIconType(),
  683. options.getTitle(),
  684. options.getMessage(),
  685. options.getButtonText (0),
  686. options.getAssociatedComponent());
  687. }
  688. else
  689. #endif
  690. {
  691. AlertWindow::showMessageBoxAsync (options.getIconType(),
  692. options.getTitle(),
  693. options.getMessage(),
  694. options.getButtonText (0),
  695. options.getAssociatedComponent(),
  696. callback);
  697. }
  698. return 0;
  699. }
  700. #if JUCE_MODAL_LOOPS_PERMITTED
  701. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  702. const String& title, const String& message,
  703. Component* /*associatedComponent*/)
  704. {
  705. AlertWindow::showMessageBox (iconType, title, message);
  706. }
  707. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  708. {
  709. return showDialog (options, nullptr, Async::no);
  710. }
  711. #endif
  712. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  713. const String& title, const String& message,
  714. Component* associatedComponent,
  715. ModalComponentManager::Callback* callback)
  716. {
  717. AlertWindow::showMessageBoxAsync (iconType, title, message, {}, associatedComponent, callback);
  718. }
  719. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  720. const String& title, const String& message,
  721. Component* associatedComponent,
  722. ModalComponentManager::Callback* callback)
  723. {
  724. return AlertWindow::showOkCancelBox (iconType, title, message, {}, {}, associatedComponent, callback);
  725. }
  726. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  727. const String& title, const String& message,
  728. Component* associatedComponent,
  729. ModalComponentManager::Callback* callback)
  730. {
  731. return AlertWindow::showYesNoCancelBox (iconType, title, message, {}, {}, {},
  732. associatedComponent, callback);
  733. }
  734. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  735. const String& title, const String& message,
  736. Component* associatedComponent,
  737. ModalComponentManager::Callback* callback)
  738. {
  739. return AlertWindow::showOkCancelBox (iconType, title, message, TRANS("Yes"), TRANS("No"),
  740. associatedComponent, callback);
  741. }
  742. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  743. ModalComponentManager::Callback* callback)
  744. {
  745. showDialog (options, callback, Async::yes);
  746. }
  747. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  748. std::function<void (int)> callback)
  749. {
  750. showAsync (options, ModalCallbackFunction::create (callback));
  751. }
  752. //==============================================================================
  753. Image juce_createIconForFile (const File&)
  754. {
  755. return {};
  756. }
  757. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy);
  758. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
  759. {
  760. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  761. linuxPeer->addOpenGLRepaintListener (dummy);
  762. }
  763. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy);
  764. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
  765. {
  766. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  767. linuxPeer->removeOpenGLRepaintListener (dummy);
  768. }
  769. } // namespace juce