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.

915 lines
33KB

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