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_Desktop.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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. Desktop::Desktop()
  22. : mouseSources (new MouseInputSource::SourceList()),
  23. mouseClickCounter (0), mouseWheelCounter (0),
  24. kioskModeComponent (nullptr),
  25. kioskModeReentrant (false),
  26. allowedOrientations (allOrientations),
  27. masterScaleFactor ((float) getDefaultMasterScale())
  28. {
  29. displays = new Displays (*this);
  30. }
  31. Desktop::~Desktop()
  32. {
  33. setScreenSaverEnabled (true);
  34. animator.cancelAllAnimations (false);
  35. jassert (instance == this);
  36. instance = nullptr;
  37. // doh! If you don't delete all your windows before exiting, you're going to
  38. // be leaking memory!
  39. jassert (desktopComponents.size() == 0);
  40. }
  41. Desktop& JUCE_CALLTYPE Desktop::getInstance()
  42. {
  43. if (instance == nullptr)
  44. instance = new Desktop();
  45. return *instance;
  46. }
  47. Desktop* Desktop::instance = nullptr;
  48. //==============================================================================
  49. int Desktop::getNumComponents() const noexcept
  50. {
  51. return desktopComponents.size();
  52. }
  53. Component* Desktop::getComponent (const int index) const noexcept
  54. {
  55. return desktopComponents [index];
  56. }
  57. Component* Desktop::findComponentAt (Point<int> screenPosition) const
  58. {
  59. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  60. for (int i = desktopComponents.size(); --i >= 0;)
  61. {
  62. Component* const c = desktopComponents.getUnchecked(i);
  63. if (c->isVisible())
  64. {
  65. const Point<int> relative (c->getLocalPoint (nullptr, screenPosition));
  66. if (c->contains (relative))
  67. return c->getComponentAt (relative);
  68. }
  69. }
  70. return nullptr;
  71. }
  72. //==============================================================================
  73. LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
  74. {
  75. if (currentLookAndFeel == nullptr)
  76. {
  77. if (defaultLookAndFeel == nullptr)
  78. defaultLookAndFeel = new LookAndFeel_V4();
  79. currentLookAndFeel = defaultLookAndFeel;
  80. }
  81. return *currentLookAndFeel;
  82. }
  83. void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
  84. {
  85. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  86. currentLookAndFeel = newDefaultLookAndFeel;
  87. for (int i = getNumComponents(); --i >= 0;)
  88. if (Component* const c = getComponent (i))
  89. c->sendLookAndFeelChange();
  90. }
  91. //==============================================================================
  92. void Desktop::addDesktopComponent (Component* const c)
  93. {
  94. jassert (c != nullptr);
  95. jassert (! desktopComponents.contains (c));
  96. desktopComponents.addIfNotAlreadyThere (c);
  97. }
  98. void Desktop::removeDesktopComponent (Component* const c)
  99. {
  100. desktopComponents.removeFirstMatchingValue (c);
  101. }
  102. void Desktop::componentBroughtToFront (Component* const c)
  103. {
  104. const int index = desktopComponents.indexOf (c);
  105. jassert (index >= 0);
  106. if (index >= 0)
  107. {
  108. int newIndex = -1;
  109. if (! c->isAlwaysOnTop())
  110. {
  111. newIndex = desktopComponents.size();
  112. while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
  113. --newIndex;
  114. --newIndex;
  115. }
  116. desktopComponents.move (index, newIndex);
  117. }
  118. }
  119. //==============================================================================
  120. Point<int> Desktop::getMousePosition()
  121. {
  122. return getMousePositionFloat().roundToInt();
  123. }
  124. Point<float> Desktop::getMousePositionFloat()
  125. {
  126. return getInstance().getMainMouseSource().getScreenPosition();
  127. }
  128. void Desktop::setMousePosition (Point<int> newPosition)
  129. {
  130. getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
  131. }
  132. Point<int> Desktop::getLastMouseDownPosition()
  133. {
  134. return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
  135. }
  136. int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; }
  137. int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; }
  138. void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; }
  139. void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; }
  140. const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; }
  141. int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); }
  142. int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); }
  143. MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); }
  144. MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); }
  145. MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
  146. void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); }
  147. //==============================================================================
  148. void Desktop::addFocusChangeListener (FocusChangeListener* const listener) { focusListeners.add (listener); }
  149. void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) { focusListeners.remove (listener); }
  150. void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); }
  151. void Desktop::handleAsyncUpdate()
  152. {
  153. // The component may be deleted during this operation, but we'll use a SafePointer rather than a
  154. // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
  155. WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
  156. focusListeners.call (&FocusChangeListener::globalFocusChanged, currentFocus);
  157. }
  158. //==============================================================================
  159. void Desktop::resetTimer()
  160. {
  161. if (mouseListeners.size() == 0)
  162. stopTimer();
  163. else
  164. startTimer (100);
  165. lastFakeMouseMove = getMousePositionFloat();
  166. }
  167. ListenerList<MouseListener>& Desktop::getMouseListeners()
  168. {
  169. resetTimer();
  170. return mouseListeners;
  171. }
  172. void Desktop::addGlobalMouseListener (MouseListener* const listener)
  173. {
  174. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  175. mouseListeners.add (listener);
  176. resetTimer();
  177. }
  178. void Desktop::removeGlobalMouseListener (MouseListener* const listener)
  179. {
  180. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  181. mouseListeners.remove (listener);
  182. resetTimer();
  183. }
  184. void Desktop::timerCallback()
  185. {
  186. if (lastFakeMouseMove != getMousePositionFloat())
  187. sendMouseMove();
  188. }
  189. void Desktop::sendMouseMove()
  190. {
  191. if (! mouseListeners.isEmpty())
  192. {
  193. startTimer (20);
  194. lastFakeMouseMove = getMousePositionFloat();
  195. if (auto* target = findComponentAt (lastFakeMouseMove.roundToInt()))
  196. {
  197. Component::BailOutChecker checker (target);
  198. const Point<float> pos (target->getLocalPoint (nullptr, lastFakeMouseMove));
  199. const Time now (Time::getCurrentTime());
  200. const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(), MouseInputSource::invalidPressure,
  201. MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
  202. MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
  203. target, target, now, pos, now, 0, false);
  204. if (me.mods.isAnyMouseButtonDown())
  205. mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);
  206. else
  207. mouseListeners.callChecked (checker, &MouseListener::mouseMove, me);
  208. }
  209. }
  210. }
  211. //==============================================================================
  212. Desktop::Displays::Displays (Desktop& desktop) { init (desktop); }
  213. Desktop::Displays::~Displays() {}
  214. const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept
  215. {
  216. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  217. jassert (displays.getReference(0).isMain);
  218. return displays.getReference(0);
  219. }
  220. const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (Point<int> position) const noexcept
  221. {
  222. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  223. const Display* best = &displays.getReference(0);
  224. double bestDistance = 1.0e10;
  225. for (int i = displays.size(); --i >= 0;)
  226. {
  227. const Display& d = displays.getReference(i);
  228. if (d.totalArea.contains (position))
  229. {
  230. best = &d;
  231. break;
  232. }
  233. const double distance = d.totalArea.getCentre().getDistanceFrom (position);
  234. if (distance < bestDistance)
  235. {
  236. bestDistance = distance;
  237. best = &d;
  238. }
  239. }
  240. return *best;
  241. }
  242. RectangleList<int> Desktop::Displays::getRectangleList (bool userAreasOnly) const
  243. {
  244. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  245. RectangleList<int> rl;
  246. for (int i = 0; i < displays.size(); ++i)
  247. {
  248. const Display& d = displays.getReference(i);
  249. rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
  250. }
  251. return rl;
  252. }
  253. Rectangle<int> Desktop::Displays::getTotalBounds (bool userAreasOnly) const
  254. {
  255. return getRectangleList (userAreasOnly).getBounds();
  256. }
  257. bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept;
  258. bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
  259. {
  260. return d1.userArea == d2.userArea
  261. && d1.totalArea == d2.totalArea
  262. && d1.scale == d2.scale
  263. && d1.isMain == d2.isMain;
  264. }
  265. bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept;
  266. bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept
  267. {
  268. return ! (d1 == d2);
  269. }
  270. void Desktop::Displays::init (Desktop& desktop)
  271. {
  272. findDisplays (desktop.getGlobalScaleFactor());
  273. }
  274. void Desktop::Displays::refresh()
  275. {
  276. Array<Display> oldDisplays;
  277. oldDisplays.swapWith (displays);
  278. init (Desktop::getInstance());
  279. if (oldDisplays != displays)
  280. {
  281. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  282. if (ComponentPeer* const peer = ComponentPeer::getPeer (i))
  283. peer->handleScreenSizeChange();
  284. }
  285. }
  286. //==============================================================================
  287. void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars)
  288. {
  289. if (kioskModeReentrant)
  290. return;
  291. const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
  292. if (kioskModeComponent != componentToUse)
  293. {
  294. // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
  295. jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  296. if (Component* const oldKioskComp = kioskModeComponent)
  297. {
  298. kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
  299. setKioskComponent (oldKioskComp, false, allowMenusAndBars);
  300. oldKioskComp->setBounds (kioskComponentOriginalBounds);
  301. }
  302. kioskModeComponent = componentToUse;
  303. if (kioskModeComponent != nullptr)
  304. {
  305. // Only components that are already on the desktop can be put into kiosk mode!
  306. jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
  307. kioskComponentOriginalBounds = kioskModeComponent->getBounds();
  308. setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
  309. }
  310. }
  311. }
  312. //==============================================================================
  313. void Desktop::setOrientationsEnabled (const int newOrientations)
  314. {
  315. if (allowedOrientations != newOrientations)
  316. {
  317. // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
  318. jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
  319. allowedOrientations = newOrientations;
  320. allowedOrientationsChanged();
  321. }
  322. }
  323. int Desktop::getOrientationsEnabled() const noexcept
  324. {
  325. return allowedOrientations;
  326. }
  327. bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept
  328. {
  329. // Make sure you only pass one valid flag in here...
  330. jassert (orientation == upright || orientation == upsideDown
  331. || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
  332. return (allowedOrientations & orientation) != 0;
  333. }
  334. void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
  335. {
  336. ASSERT_MESSAGE_MANAGER_IS_LOCKED
  337. if (masterScaleFactor != newScaleFactor)
  338. {
  339. masterScaleFactor = newScaleFactor;
  340. displays->refresh();
  341. }
  342. }
  343. } // namespace juce