Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_linux_Windowing.cpp 24KB

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