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.

683 lines
24KB

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