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.

352 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. float x, y, size, dx, dy, w, h, parentWidth, parentHeight;
  23. float innerX, innerY;
  24. Colour colour;
  25. Thread::ThreadID threadId;
  26. public:
  27. BouncingBallComp()
  28. {
  29. x = Random::getSystemRandom().nextFloat() * 200.0f;
  30. y = Random::getSystemRandom().nextFloat() * 200.0f;
  31. parentWidth = 50;
  32. parentHeight = 50;
  33. innerX = 0;
  34. innerY = 0;
  35. threadId = 0;
  36. const float speed = 5.0f; // give each ball a fixed speed so we can
  37. // see the effects of thread priority on how fast
  38. // they actually go.
  39. const float angle = Random::getSystemRandom().nextFloat() * float_Pi * 2.0f;
  40. dx = sinf (angle) * speed;
  41. dy = cosf (angle) * speed;
  42. size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f;
  43. colour = Colour (Random::getSystemRandom().nextInt())
  44. .withAlpha (0.5f)
  45. .withBrightness (0.7f);
  46. }
  47. ~BouncingBallComp()
  48. {
  49. }
  50. void paint (Graphics& g)
  51. {
  52. g.setColour (colour);
  53. g.fillEllipse (innerX, innerY, size, size);
  54. g.setColour (Colours::black);
  55. g.setFont (10.0f);
  56. g.drawText (String::toHexString ((int64) threadId), 0, 0, getWidth(), getHeight(), Justification::centred, false);
  57. }
  58. void parentSizeChanged()
  59. {
  60. parentWidth = getParentWidth() - size;
  61. parentHeight = getParentHeight() - size;
  62. }
  63. void moveBall()
  64. {
  65. threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
  66. x += dx;
  67. y += dy;
  68. if (x < 0)
  69. dx = fabsf (dx);
  70. if (x > parentWidth)
  71. dx = -fabsf (dx);
  72. if (y < 0)
  73. dy = fabsf (dy);
  74. if (y > parentHeight)
  75. dy = -fabsf (dy);
  76. setBounds (((int) x) - 2,
  77. ((int) y) - 2,
  78. ((int) size) + 4,
  79. ((int) size) + 4);
  80. innerX = x - getX();
  81. innerY = y - getY();
  82. repaint();
  83. }
  84. juce_UseDebuggingNewOperator
  85. };
  86. //==============================================================================
  87. class DemoThread : public BouncingBallComp,
  88. public Thread
  89. {
  90. int interval;
  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. juce_UseDebuggingNewOperator
  125. };
  126. //==============================================================================
  127. class DemoThreadPoolJob : public BouncingBallComp,
  128. public ThreadPoolJob
  129. {
  130. public:
  131. DemoThreadPoolJob()
  132. : ThreadPoolJob ("Demo Threadpool Job")
  133. {
  134. }
  135. ~DemoThreadPoolJob()
  136. {
  137. }
  138. JobStatus runJob()
  139. {
  140. // this is the code that runs this job. It'll be repeatedly called until we return
  141. // jobHasFinished instead of jobNeedsRunningAgain.
  142. Thread::sleep (30);
  143. // because this is a background thread, we mustn't do any UI work without
  144. // first grabbing a MessageManagerLock..
  145. const MessageManagerLock mml (this);
  146. // before moving the ball, we need to check whether the lock was actually gained, because
  147. // if something is trying to stop this job, it will have failed..
  148. if (mml.lockWasGained())
  149. moveBall();
  150. return jobNeedsRunningAgain;
  151. }
  152. void removedFromQueue()
  153. {
  154. // This is called to tell us that our job has been removed from the pool.
  155. // In this case there's no need to do anything here.
  156. }
  157. juce_UseDebuggingNewOperator
  158. };
  159. //==============================================================================
  160. class ThreadingDemo : public Component,
  161. public Timer,
  162. public ButtonListener
  163. {
  164. bool isUsingPool;
  165. ThreadPool pool;
  166. TextButton* controlButton;
  167. public:
  168. //==============================================================================
  169. ThreadingDemo()
  170. : pool (3)
  171. {
  172. isUsingPool = false;
  173. setName ("Multithreading");
  174. setOpaque (true);
  175. }
  176. ~ThreadingDemo()
  177. {
  178. pool.removeAllJobs (true, 2000);
  179. deleteAllChildren();
  180. }
  181. // this gets called when a component is added or removed from a parent component.
  182. void parentHierarchyChanged()
  183. {
  184. // we'll use this as an opportunity to start and stop the threads, so that
  185. // we don't leave them going when the component's not actually visible.
  186. stopTimer();
  187. pool.removeAllJobs (true, 4000);
  188. deleteAllChildren();
  189. addAndMakeVisible (controlButton = new TextButton ("Thread type"));
  190. controlButton->changeWidthToFitText (20);
  191. controlButton->setTopLeftPosition (20, 20);
  192. controlButton->setTriggeredOnMouseDown (true);
  193. controlButton->setAlwaysOnTop (true);
  194. controlButton->addButtonListener (this);
  195. if (isShowing())
  196. {
  197. while (getNumChildComponents() < 5)
  198. addABall();
  199. startTimer (2000);
  200. }
  201. }
  202. void paint (Graphics& g)
  203. {
  204. g.fillAll (Colours::white);
  205. }
  206. void setUsingPool (bool usePool)
  207. {
  208. isUsingPool = usePool;
  209. parentHierarchyChanged(); // resets everything
  210. }
  211. void addABall()
  212. {
  213. if (isUsingPool)
  214. {
  215. DemoThreadPoolJob* newComp = new DemoThreadPoolJob();
  216. addAndMakeVisible (newComp);
  217. newComp->parentSizeChanged();
  218. pool.addJob (newComp);
  219. }
  220. else
  221. {
  222. DemoThread* newComp = new DemoThread();
  223. addAndMakeVisible (newComp);
  224. newComp->parentSizeChanged();
  225. }
  226. }
  227. void removeABall()
  228. {
  229. if (isUsingPool)
  230. {
  231. ThreadPoolJob* jobToRemove = pool.getJob (Random::getSystemRandom().nextInt (pool.getNumJobs()));
  232. if (jobToRemove != 0)
  233. {
  234. pool.removeJob (jobToRemove, true, 4000);
  235. delete jobToRemove;
  236. }
  237. }
  238. else
  239. {
  240. if (getNumChildComponents() > 1)
  241. {
  242. Component* ball = getChildComponent (1 + Random::getSystemRandom().nextInt (getNumChildComponents() - 1));
  243. if (dynamic_cast <Button*> (ball) == 0) // don't delete our button!
  244. delete ball;
  245. }
  246. }
  247. }
  248. void timerCallback()
  249. {
  250. if (Random::getSystemRandom().nextBool())
  251. {
  252. if (getNumChildComponents() <= 10)
  253. addABall();
  254. }
  255. else
  256. {
  257. if (getNumChildComponents() > 3)
  258. removeABall();
  259. }
  260. }
  261. void buttonClicked (Button* button)
  262. {
  263. PopupMenu m;
  264. m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
  265. m.addItem (2, "Use a thread pool", true, isUsingPool);
  266. const int res = m.showAt (button);
  267. if (res != 0)
  268. setUsingPool (res == 2);
  269. }
  270. };
  271. //==============================================================================
  272. Component* createThreadingDemo()
  273. {
  274. return new ThreadingDemo();
  275. }