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.

356 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../jucedemo_headers.h"
  19. //==============================================================================
  20. class BouncingBallComp : public Component
  21. {
  22. public:
  23. BouncingBallComp()
  24. {
  25. x = Random::getSystemRandom().nextFloat() * 200.0f;
  26. y = Random::getSystemRandom().nextFloat() * 200.0f;
  27. parentWidth = 50;
  28. parentHeight = 50;
  29. innerX = 0;
  30. innerY = 0;
  31. threadId = 0;
  32. const float speed = 5.0f; // give each ball a fixed speed so we can
  33. // see the effects of thread priority on how fast
  34. // they actually go.
  35. const float angle = Random::getSystemRandom().nextFloat() * float_Pi * 2.0f;
  36. dx = sinf (angle) * speed;
  37. dy = cosf (angle) * speed;
  38. size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f;
  39. colour = Colour (Random::getSystemRandom().nextInt())
  40. .withAlpha (0.5f)
  41. .withBrightness (0.7f);
  42. }
  43. ~BouncingBallComp()
  44. {
  45. }
  46. void paint (Graphics& g)
  47. {
  48. g.setColour (colour);
  49. g.fillEllipse (innerX, innerY, size, size);
  50. g.setColour (Colours::black);
  51. g.setFont (10.0f);
  52. g.drawText (String::toHexString ((int64) threadId), 0, 0, getWidth(), getHeight(), Justification::centred, false);
  53. }
  54. void parentSizeChanged()
  55. {
  56. parentWidth = getParentWidth() - size;
  57. parentHeight = getParentHeight() - size;
  58. }
  59. void moveBall()
  60. {
  61. threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
  62. x += dx;
  63. y += dy;
  64. if (x < 0)
  65. dx = fabsf (dx);
  66. if (x > parentWidth)
  67. dx = -fabsf (dx);
  68. if (y < 0)
  69. dy = fabsf (dy);
  70. if (y > parentHeight)
  71. dy = -fabsf (dy);
  72. setBounds (((int) x) - 2,
  73. ((int) y) - 2,
  74. ((int) size) + 4,
  75. ((int) size) + 4);
  76. innerX = x - getX();
  77. innerY = y - getY();
  78. repaint();
  79. }
  80. private:
  81. float x, y, size, dx, dy, w, h, parentWidth, parentHeight;
  82. float innerX, innerY;
  83. Colour colour;
  84. Thread::ThreadID threadId;
  85. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComp);
  86. };
  87. //==============================================================================
  88. class DemoThread : public BouncingBallComp,
  89. public Thread
  90. {
  91. public:
  92. DemoThread()
  93. : Thread ("Juce Demo Thread")
  94. {
  95. interval = Random::getSystemRandom().nextInt (50) + 6;
  96. // give the threads a random priority, so some will move more
  97. // smoothly than others..
  98. startThread (Random::getSystemRandom().nextInt (3) + 3);
  99. }
  100. ~DemoThread()
  101. {
  102. // allow the thread 2 seconds to stop cleanly - should be plenty of time.
  103. stopThread (2000);
  104. }
  105. void run()
  106. {
  107. // this is the code that runs this thread - we'll loop continuously,
  108. // updating the co-ordinates of our blob.
  109. // threadShouldExit() returns true when the stopThread() method has been
  110. // called, so we should check it often, and exit as soon as it gets flagged.
  111. while (! threadShouldExit())
  112. {
  113. // sleep a bit so the threads don't all grind the CPU to a halt..
  114. wait (interval);
  115. // because this is a background thread, we mustn't do any UI work without
  116. // first grabbing a MessageManagerLock..
  117. const MessageManagerLock mml (Thread::getCurrentThread());
  118. if (! mml.lockWasGained()) // if something is trying to kill this job, the lock
  119. return; // will fail, in which case we'd better return..
  120. // now we've got the UI thread locked, we can mess about with the components
  121. moveBall();
  122. }
  123. }
  124. private:
  125. int interval;
  126. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread);
  127. };
  128. //==============================================================================
  129. class DemoThreadPoolJob : public BouncingBallComp,
  130. public ThreadPoolJob
  131. {
  132. public:
  133. DemoThreadPoolJob()
  134. : ThreadPoolJob ("Demo Threadpool Job")
  135. {
  136. }
  137. ~DemoThreadPoolJob()
  138. {
  139. }
  140. JobStatus runJob()
  141. {
  142. // this is the code that runs this job. It'll be repeatedly called until we return
  143. // jobHasFinished instead of jobNeedsRunningAgain.
  144. Thread::sleep (30);
  145. // because this is a background thread, we mustn't do any UI work without
  146. // first grabbing a MessageManagerLock..
  147. const MessageManagerLock mml (this);
  148. // before moving the ball, we need to check whether the lock was actually gained, because
  149. // if something is trying to stop this job, it will have failed..
  150. if (mml.lockWasGained())
  151. moveBall();
  152. return jobNeedsRunningAgain;
  153. }
  154. void removedFromQueue()
  155. {
  156. // This is called to tell us that our job has been removed from the pool.
  157. // In this case there's no need to do anything here.
  158. }
  159. private:
  160. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob);
  161. };
  162. //==============================================================================
  163. class ThreadingDemo : public Component,
  164. public Timer,
  165. public ButtonListener
  166. {
  167. public:
  168. //==============================================================================
  169. ThreadingDemo()
  170. : pool (3),
  171. controlButton ("Thread type"),
  172. isUsingPool (false)
  173. {
  174. setName ("Multithreading");
  175. setOpaque (true);
  176. addAndMakeVisible (&controlButton);
  177. controlButton.changeWidthToFitText (20);
  178. controlButton.setTopLeftPosition (20, 20);
  179. controlButton.setTriggeredOnMouseDown (true);
  180. controlButton.setAlwaysOnTop (true);
  181. controlButton.addListener (this);
  182. }
  183. ~ThreadingDemo()
  184. {
  185. pool.removeAllJobs (true, 2000);
  186. }
  187. void resetAllBalls()
  188. {
  189. stopTimer();
  190. pool.removeAllJobs (true, 4000);
  191. balls.clear();
  192. if (isShowing())
  193. {
  194. while (balls.size() < 5)
  195. addABall();
  196. startTimer (2000);
  197. }
  198. }
  199. void paint (Graphics& g)
  200. {
  201. g.fillAll (Colours::white);
  202. }
  203. void setUsingPool (bool usePool)
  204. {
  205. isUsingPool = usePool;
  206. resetAllBalls();
  207. }
  208. void addABall()
  209. {
  210. if (isUsingPool)
  211. {
  212. DemoThreadPoolJob* newBall = new DemoThreadPoolJob();
  213. balls.add (newBall);
  214. addAndMakeVisible (newBall);
  215. newBall->parentSizeChanged();
  216. pool.addJob (newBall);
  217. }
  218. else
  219. {
  220. DemoThread* newBall = new DemoThread();
  221. balls.add (newBall);
  222. addAndMakeVisible (newBall);
  223. newBall->parentSizeChanged();
  224. }
  225. }
  226. void removeABall()
  227. {
  228. if (balls.size() > 0)
  229. {
  230. int indexToRemove = Random::getSystemRandom().nextInt (balls.size());
  231. if (isUsingPool)
  232. pool.removeJob (dynamic_cast <DemoThreadPoolJob*> (balls [indexToRemove]), true, 4000);
  233. balls.remove (indexToRemove);
  234. }
  235. }
  236. void timerCallback()
  237. {
  238. if (Random::getSystemRandom().nextBool())
  239. {
  240. if (balls.size() <= 10)
  241. addABall();
  242. }
  243. else
  244. {
  245. if (balls.size() > 3)
  246. removeABall();
  247. }
  248. }
  249. void buttonClicked (Button*)
  250. {
  251. PopupMenu m;
  252. m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
  253. m.addItem (2, "Use a thread pool", true, isUsingPool);
  254. m.showMenuAsync (PopupMenu::Options().withTargetComponent (&controlButton),
  255. ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
  256. }
  257. static void menuItemChosenCallback (int result, ThreadingDemo* demoComponent)
  258. {
  259. if (demoComponent != 0)
  260. demoComponent->setUsingPool (result == 2);
  261. }
  262. // this gets called when a component is added or removed from a parent component.
  263. void parentHierarchyChanged()
  264. {
  265. // we'll use this as an opportunity to start and stop the threads, so that
  266. // we don't leave them going when the component's not actually visible.
  267. resetAllBalls();
  268. }
  269. private:
  270. ThreadPool pool;
  271. TextButton controlButton;
  272. bool isUsingPool;
  273. OwnedArray<Component> balls;
  274. };
  275. //==============================================================================
  276. Component* createThreadingDemo()
  277. {
  278. return new ThreadingDemo();
  279. }