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.

699 lines
24KB

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