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.

705 lines
25KB

  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. {
  26. public:
  27. LinuxComponentPeer (Component& comp, int windowStyleFlags, ::Window parentToAddTo)
  28. : ComponentPeer (comp, windowStyleFlags),
  29. isAlwaysOnTop (comp.isAlwaysOnTop())
  30. {
  31. // it's dangerous to create a window on a thread other than the message thread.
  32. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  33. if (isAlwaysOnTop)
  34. ++numAlwaysOnTopPeers;
  35. repainter = std::make_unique<LinuxRepaintManager> (*this);
  36. windowH = XWindowSystem::getInstance()->createWindow (parentToAddTo, this);
  37. parentWindow = parentToAddTo;
  38. setTitle (component.getName());
  39. getNativeRealtimeModifiers = []() -> ModifierKeys { return XWindowSystem::getInstance()->getNativeRealtimeModifiers(); };
  40. }
  41. ~LinuxComponentPeer() override
  42. {
  43. // it's dangerous to delete a window on a thread other than the message thread.
  44. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  45. repainter = nullptr;
  46. XWindowSystem::getInstance()->destroyWindow (windowH);
  47. if (isAlwaysOnTop)
  48. --numAlwaysOnTopPeers;
  49. }
  50. ::Window getWindowHandle() const noexcept
  51. {
  52. return windowH;
  53. }
  54. //==============================================================================
  55. void* getNativeHandle() const override
  56. {
  57. return reinterpret_cast<void*> (getWindowHandle());
  58. }
  59. //==============================================================================
  60. void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) override
  61. {
  62. bounds = newBounds.withSize (jmax (1, newBounds.getWidth()),
  63. jmax (1, newBounds.getHeight()));
  64. updateScaleFactorFromNewBounds (bounds, false);
  65. auto physicalBounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds)
  66. : bounds * currentScaleFactor);
  67. WeakReference<Component> deletionChecker (&component);
  68. XWindowSystem::getInstance()->setBounds (windowH, physicalBounds, isNowFullScreen);
  69. fullScreen = isNowFullScreen;
  70. if (deletionChecker != nullptr)
  71. {
  72. updateBorderSize();
  73. handleMovedOrResized();
  74. }
  75. }
  76. Point<int> getScreenPosition (bool physical) const
  77. {
  78. auto parentPosition = XWindowSystem::getInstance()->getParentScreenPosition();
  79. auto screenBounds = (parentWindow == 0 ? bounds
  80. : bounds.translated (parentPosition.x, parentPosition.y));
  81. if (physical)
  82. return Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft());
  83. return screenBounds.getTopLeft();
  84. }
  85. Rectangle<int> getBounds() const override
  86. {
  87. return bounds;
  88. }
  89. BorderSize<int> getFrameSize() const override
  90. {
  91. return windowBorder;
  92. }
  93. Point<float> localToGlobal (Point<float> relativePosition) override
  94. {
  95. return relativePosition + getScreenPosition (false).toFloat();
  96. }
  97. Point<float> globalToLocal (Point<float> screenPosition) override
  98. {
  99. return screenPosition - getScreenPosition (false).toFloat();
  100. }
  101. using ComponentPeer::localToGlobal;
  102. using ComponentPeer::globalToLocal;
  103. //==============================================================================
  104. StringArray getAvailableRenderingEngines() override
  105. {
  106. return { "Software Renderer" };
  107. }
  108. void setVisible (bool shouldBeVisible) override
  109. {
  110. XWindowSystem::getInstance()->setVisible (windowH, shouldBeVisible);
  111. }
  112. void setTitle (const String& title) override
  113. {
  114. XWindowSystem::getInstance()->setTitle (windowH, title);
  115. }
  116. void setMinimised (bool shouldBeMinimised) override
  117. {
  118. if (shouldBeMinimised)
  119. XWindowSystem::getInstance()->setMinimised (windowH, shouldBeMinimised);
  120. else
  121. setVisible (true);
  122. }
  123. bool isMinimised() const override
  124. {
  125. return XWindowSystem::getInstance()->isMinimised (windowH);
  126. }
  127. void setFullScreen (bool shouldBeFullScreen) override
  128. {
  129. auto r = lastNonFullscreenBounds; // (get a copy of this before de-minimising)
  130. setMinimised (false);
  131. if (fullScreen != shouldBeFullScreen)
  132. {
  133. XWindowSystem::getInstance()->setMaximised (windowH, shouldBeFullScreen);
  134. if (shouldBeFullScreen)
  135. r = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow);
  136. if (! r.isEmpty())
  137. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
  138. component.repaint();
  139. }
  140. }
  141. bool isFullScreen() const override
  142. {
  143. return fullScreen;
  144. }
  145. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  146. {
  147. if (! bounds.withZeroOrigin().contains (localPos))
  148. return false;
  149. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  150. {
  151. auto* c = Desktop::getInstance().getComponent (i);
  152. if (c == &component)
  153. break;
  154. if (! c->isVisible())
  155. continue;
  156. if (auto* peer = c->getPeer())
  157. if (peer->contains (localPos + bounds.getPosition() - peer->getBounds().getPosition(), true))
  158. return false;
  159. }
  160. if (trueIfInAChildWindow)
  161. return true;
  162. return XWindowSystem::getInstance()->contains (windowH, localPos * currentScaleFactor);
  163. }
  164. void toFront (bool makeActive) override
  165. {
  166. if (makeActive)
  167. {
  168. setVisible (true);
  169. grabFocus();
  170. }
  171. XWindowSystem::getInstance()->toFront (windowH, makeActive);
  172. handleBroughtToFront();
  173. }
  174. void toBehind (ComponentPeer* other) override
  175. {
  176. if (auto* otherPeer = dynamic_cast<LinuxComponentPeer*> (other))
  177. {
  178. if (otherPeer->styleFlags & windowIsTemporary)
  179. return;
  180. setMinimised (false);
  181. XWindowSystem::getInstance()->toBehind (windowH, otherPeer->windowH);
  182. }
  183. else
  184. {
  185. jassertfalse; // wrong type of window?
  186. }
  187. }
  188. bool isFocused() const override
  189. {
  190. return XWindowSystem::getInstance()->isFocused (windowH);
  191. }
  192. void grabFocus() override
  193. {
  194. if (XWindowSystem::getInstance()->grabFocus (windowH))
  195. isActiveApplication = true;
  196. }
  197. //==============================================================================
  198. void repaint (const Rectangle<int>& area) override
  199. {
  200. repainter->repaint (area.getIntersection (bounds.withZeroOrigin()));
  201. }
  202. void performAnyPendingRepaintsNow() override
  203. {
  204. repainter->performAnyPendingRepaintsNow();
  205. }
  206. void setIcon (const Image& newIcon) override
  207. {
  208. XWindowSystem::getInstance()->setIcon (windowH, newIcon);
  209. }
  210. double getPlatformScaleFactor() const noexcept override
  211. {
  212. return currentScaleFactor;
  213. }
  214. void setAlpha (float) override {}
  215. bool setAlwaysOnTop (bool) override { return false; }
  216. void textInputRequired (Point<int>, TextInputTarget&) override {}
  217. //==============================================================================
  218. void addOpenGLRepaintListener (Component* dummy)
  219. {
  220. if (dummy != nullptr)
  221. glRepaintListeners.addIfNotAlreadyThere (dummy);
  222. }
  223. void removeOpenGLRepaintListener (Component* dummy)
  224. {
  225. if (dummy != nullptr)
  226. glRepaintListeners.removeAllInstancesOf (dummy);
  227. }
  228. void repaintOpenGLContexts()
  229. {
  230. for (auto* c : glRepaintListeners)
  231. c->handleCommandMessage (0);
  232. }
  233. //==============================================================================
  234. ::Window getParentWindow() { return parentWindow; }
  235. void setParentWindow (::Window newParent) { parentWindow = newParent; }
  236. //==============================================================================
  237. void updateWindowBounds()
  238. {
  239. jassert (windowH != 0);
  240. if (windowH != 0)
  241. {
  242. auto physicalBounds = XWindowSystem::getInstance()->getWindowBounds (windowH, parentWindow);
  243. updateScaleFactorFromNewBounds (physicalBounds, true);
  244. bounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds)
  245. : physicalBounds / currentScaleFactor);
  246. }
  247. }
  248. void updateBorderSize()
  249. {
  250. if ((styleFlags & windowHasTitleBar) == 0)
  251. windowBorder = {};
  252. else if (windowBorder.getTopAndBottom() == 0 && windowBorder.getLeftAndRight() == 0)
  253. windowBorder = XWindowSystem::getInstance()->getBorderSize (windowH);
  254. }
  255. //==============================================================================
  256. static bool isActiveApplication;
  257. bool focused = false;
  258. private:
  259. //==============================================================================
  260. class LinuxRepaintManager : public Timer
  261. {
  262. public:
  263. LinuxRepaintManager (LinuxComponentPeer& p)
  264. : peer (p),
  265. isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
  266. {
  267. }
  268. void timerCallback() override
  269. {
  270. XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
  271. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  272. return;
  273. if (! regionsNeedingRepaint.isEmpty())
  274. {
  275. stopTimer();
  276. performAnyPendingRepaintsNow();
  277. }
  278. else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
  279. {
  280. stopTimer();
  281. image = Image();
  282. }
  283. }
  284. void repaint (Rectangle<int> area)
  285. {
  286. if (! isTimerRunning())
  287. startTimer (repaintTimerPeriod);
  288. regionsNeedingRepaint.add (area * peer.currentScaleFactor);
  289. }
  290. void performAnyPendingRepaintsNow()
  291. {
  292. if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
  293. {
  294. startTimer (repaintTimerPeriod);
  295. return;
  296. }
  297. auto originalRepaintRegion = regionsNeedingRepaint;
  298. regionsNeedingRepaint.clear();
  299. auto totalArea = originalRepaintRegion.getBounds();
  300. if (! totalArea.isEmpty())
  301. {
  302. if (image.isNull() || image.getWidth() < totalArea.getWidth()
  303. || image.getHeight() < totalArea.getHeight())
  304. {
  305. image = XWindowSystem::getInstance()->createImage (isSemiTransparentWindow,
  306. totalArea.getWidth(), totalArea.getHeight(),
  307. useARGBImagesForRendering);
  308. }
  309. startTimer (repaintTimerPeriod);
  310. RectangleList<int> adjustedList (originalRepaintRegion);
  311. adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY());
  312. if (XWindowSystem::getInstance()->canUseARGBImages())
  313. for (auto& i : originalRepaintRegion)
  314. image.clear (i - totalArea.getPosition());
  315. {
  316. auto context = peer.getComponent().getLookAndFeel()
  317. .createGraphicsContext (image, -totalArea.getPosition(), adjustedList);
  318. context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor));
  319. peer.handlePaint (*context);
  320. }
  321. for (auto& i : originalRepaintRegion)
  322. XWindowSystem::getInstance()->blitToWindow (peer.windowH, image, i, totalArea);
  323. }
  324. lastTimeImageUsed = Time::getApproximateMillisecondCounter();
  325. startTimer (repaintTimerPeriod);
  326. }
  327. private:
  328. enum { repaintTimerPeriod = 1000 / 100 };
  329. LinuxComponentPeer& peer;
  330. const bool isSemiTransparentWindow;
  331. Image image;
  332. uint32 lastTimeImageUsed = 0;
  333. RectangleList<int> regionsNeedingRepaint;
  334. bool useARGBImagesForRendering = XWindowSystem::getInstance()->canUseARGBImages();
  335. JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
  336. };
  337. //==============================================================================
  338. void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
  339. {
  340. if (! JUCEApplicationBase::isStandaloneApp())
  341. return;
  342. Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
  343. const auto& desktop = Desktop::getInstance();
  344. if (auto* display = desktop.getDisplays().getDisplayForRect (newBounds.translated (translation.x, translation.y),
  345. isPhysical))
  346. {
  347. auto newScaleFactor = display->scale / desktop.getGlobalScaleFactor();
  348. if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
  349. {
  350. currentScaleFactor = newScaleFactor;
  351. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
  352. }
  353. }
  354. }
  355. //==============================================================================
  356. std::unique_ptr<LinuxRepaintManager> repainter;
  357. ::Window windowH = {}, parentWindow = {};
  358. Rectangle<int> bounds;
  359. BorderSize<int> windowBorder;
  360. bool fullScreen = false, isAlwaysOnTop = false;
  361. double currentScaleFactor = 1.0;
  362. Array<Component*> glRepaintListeners;
  363. //==============================================================================
  364. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
  365. };
  366. bool LinuxComponentPeer::isActiveApplication = false;
  367. //==============================================================================
  368. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  369. {
  370. return new LinuxComponentPeer (*this, styleFlags, (::Window) nativeWindowToAttachTo);
  371. }
  372. //==============================================================================
  373. JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { return LinuxComponentPeer::isActiveApplication; }
  374. JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  375. JUCE_API void JUCE_CALLTYPE Process::hide() {}
  376. //==============================================================================
  377. void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool)
  378. {
  379. if (enableOrDisable)
  380. comp->setBounds (getDisplays().getDisplayForRect (comp->getScreenBounds())->totalArea);
  381. }
  382. void Displays::findDisplays (float masterScale)
  383. {
  384. if (XWindowSystem::getInstance()->getDisplay() != nullptr)
  385. {
  386. displays = XWindowSystem::getInstance()->findDisplays (masterScale);
  387. if (! displays.isEmpty())
  388. updateToLogical();
  389. }
  390. }
  391. bool Desktop::canUseSemiTransparentWindows() noexcept
  392. {
  393. return XWindowSystem::getInstance()->canUseSemiTransparentWindows();
  394. }
  395. static bool screenSaverAllowed = true;
  396. void Desktop::setScreenSaverEnabled (bool isEnabled)
  397. {
  398. if (screenSaverAllowed != isEnabled)
  399. {
  400. screenSaverAllowed = isEnabled;
  401. XWindowSystem::getInstance()->setScreenSaverEnabled (screenSaverAllowed);
  402. }
  403. }
  404. bool Desktop::isScreenSaverEnabled()
  405. {
  406. return screenSaverAllowed;
  407. }
  408. double Desktop::getDefaultMasterScale() { return 1.0; }
  409. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; }
  410. void Desktop::allowedOrientationsChanged() {}
  411. //==============================================================================
  412. bool MouseInputSource::SourceList::addSource()
  413. {
  414. if (sources.isEmpty())
  415. {
  416. addSource (0, MouseInputSource::InputSourceType::mouse);
  417. return true;
  418. }
  419. return false;
  420. }
  421. bool MouseInputSource::SourceList::canUseTouch()
  422. {
  423. return false;
  424. }
  425. Point<float> MouseInputSource::getCurrentRawMousePosition()
  426. {
  427. return Desktop::getInstance().getDisplays().physicalToLogical (XWindowSystem::getInstance()->getCurrentMousePosition());
  428. }
  429. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  430. {
  431. XWindowSystem::getInstance()->setMousePosition (Desktop::getInstance().getDisplays().logicalToPhysical (newPosition));
  432. }
  433. //==============================================================================
  434. void* CustomMouseCursorInfo::create() const
  435. {
  436. return XWindowSystem::getInstance()->createCustomMouseCursorInfo (image, hotspot);
  437. }
  438. void MouseCursor::deleteMouseCursor (void* cursorHandle, bool)
  439. {
  440. if (cursorHandle != nullptr)
  441. XWindowSystem::getInstance()->deleteMouseCursor (cursorHandle);
  442. }
  443. void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type)
  444. {
  445. return XWindowSystem::getInstance()->createStandardMouseCursor (type);
  446. }
  447. void MouseCursor::showInWindow (ComponentPeer* peer) const
  448. {
  449. if (peer != nullptr)
  450. XWindowSystem::getInstance()->showCursor ((::Window) peer->getNativeHandle(), getHandle());
  451. }
  452. //==============================================================================
  453. static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
  454. {
  455. if (sourceComp == nullptr)
  456. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  457. sourceComp = draggingSource->getComponentUnderMouse();
  458. if (sourceComp != nullptr)
  459. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (sourceComp->getPeer()))
  460. return lp;
  461. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  462. return nullptr;
  463. }
  464. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  465. Component* sourceComp, std::function<void()> callback)
  466. {
  467. if (files.isEmpty())
  468. return false;
  469. if (auto* peer = getPeerForDragEvent (sourceComp))
  470. return XWindowSystem::getInstance()->externalDragFileInit (peer, files, canMoveFiles, std::move (callback));
  471. // This method must be called in response to a component's mouseDown or mouseDrag event!
  472. jassertfalse;
  473. return false;
  474. }
  475. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
  476. std::function<void()> callback)
  477. {
  478. if (text.isEmpty())
  479. return false;
  480. if (auto* peer = getPeerForDragEvent (sourceComp))
  481. return XWindowSystem::getInstance()->externalDragTextInit (peer, text, std::move (callback));
  482. // This method must be called in response to a component's mouseDown or mouseDrag event!
  483. jassertfalse;
  484. return false;
  485. }
  486. //==============================================================================
  487. void SystemClipboard::copyTextToClipboard (const String& clipText)
  488. {
  489. XWindowSystem::getInstance()->copyTextToClipboard (clipText);
  490. }
  491. String SystemClipboard::getTextFromClipboard()
  492. {
  493. return XWindowSystem::getInstance()->getTextFromClipboard();
  494. }
  495. //==============================================================================
  496. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  497. {
  498. return XWindowSystem::getInstance()->isKeyCurrentlyDown (keyCode);
  499. }
  500. void LookAndFeel::playAlertSound()
  501. {
  502. std::cout << "\a" << std::flush;
  503. }
  504. //==============================================================================
  505. #if JUCE_MODAL_LOOPS_PERMITTED
  506. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
  507. const String& title, const String& message,
  508. Component*)
  509. {
  510. AlertWindow::showMessageBox (iconType, title, message);
  511. }
  512. #endif
  513. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
  514. const String& title, const String& message,
  515. Component* associatedComponent,
  516. ModalComponentManager::Callback* callback)
  517. {
  518. AlertWindow::showMessageBoxAsync (iconType, title, message, {}, associatedComponent, callback);
  519. }
  520. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
  521. const String& title, const String& message,
  522. Component* associatedComponent,
  523. ModalComponentManager::Callback* callback)
  524. {
  525. return AlertWindow::showOkCancelBox (iconType, title, message, {}, {}, associatedComponent, callback);
  526. }
  527. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
  528. const String& title, const String& message,
  529. Component* associatedComponent,
  530. ModalComponentManager::Callback* callback)
  531. {
  532. return AlertWindow::showYesNoCancelBox (iconType, title, message, {}, {}, {},
  533. associatedComponent, callback);
  534. }
  535. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType,
  536. const String& title, const String& message,
  537. Component* associatedComponent,
  538. ModalComponentManager::Callback* callback)
  539. {
  540. return AlertWindow::showOkCancelBox (iconType, title, message, TRANS ("Yes"), TRANS ("No"),
  541. associatedComponent, callback);
  542. }
  543. //==============================================================================
  544. Image juce_createIconForFile (const File&)
  545. {
  546. return {};
  547. }
  548. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
  549. {
  550. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  551. linuxPeer->addOpenGLRepaintListener (dummy);
  552. }
  553. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
  554. {
  555. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  556. linuxPeer->removeOpenGLRepaintListener (dummy);
  557. }
  558. } // namespace juce