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.

222 lines
7.2KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. /**
  18. This component runs in a slave process, draws the part of the canvas that this
  19. particular client covers, and updates itself when messages arrive from the master
  20. containing new canvas states.
  21. */
  22. class SlaveCanvasComponent : public Component,
  23. private OSCSender,
  24. private OSCReceiver,
  25. private OSCReceiver::Listener<OSCReceiver::RealtimeCallback>,
  26. private AsyncUpdater,
  27. private Timer
  28. {
  29. public:
  30. SlaveCanvasComponent (PropertiesFile& p, int windowIndex) : properties (p)
  31. {
  32. {
  33. String uuidPropName ("UUID" + String (windowIndex));
  34. clientName = properties.getValue (uuidPropName);
  35. if (clientName.isEmpty())
  36. {
  37. clientName = "CLIENT_" + String (Random().nextInt (10000)).toUpperCase();
  38. properties.setValue (uuidPropName, clientName);
  39. }
  40. }
  41. setOpaque (true);
  42. setSize (1500, 900);
  43. if (! OSCSender::connect (getBroadcastIPAddress(), clientPortNumber))
  44. error = "Client app OSC sender: network connection error.";
  45. if (! OSCReceiver::connect (masterPortNumber))
  46. error = "Client app OSC receiver: network connection error.";
  47. OSCReceiver::addListener (this);
  48. timerCallback();
  49. startTimer (2000);
  50. }
  51. ~SlaveCanvasComponent()
  52. {
  53. OSCReceiver::removeListener (this);
  54. }
  55. private:
  56. void mouseDrag (const MouseEvent& e) override
  57. {
  58. auto clientArea = getAreaInGlobalSpace();
  59. if (! clientArea.isEmpty())
  60. {
  61. OSCMessage message (userInputOSCAddress);
  62. message.addString (clientName);
  63. message.addFloat32 (e.position.x * clientArea.getWidth() / getWidth() + clientArea.getX());
  64. message.addFloat32 (e.position.y * clientArea.getHeight() / getHeight() + clientArea.getY());
  65. send (message);
  66. }
  67. }
  68. //==============================================================================
  69. void oscMessageReceived (const OSCMessage& message) override
  70. {
  71. auto address = message.getAddressPattern();
  72. if (address.matches (canvasStateOSCAddress))
  73. canvasStateOSCMessageReceived (message);
  74. }
  75. struct NewStateMessage : public Message
  76. {
  77. NewStateMessage (const MemoryBlock& d) : data (d) {}
  78. MemoryBlock data;
  79. };
  80. void canvasStateOSCMessageReceived (const OSCMessage& message)
  81. {
  82. if (message.isEmpty() || ! message[0].isBlob())
  83. return;
  84. if (packetiser.appendIncomingBlock (message[0].getBlob()))
  85. {
  86. const ScopedLock sl (canvasLock);
  87. MemoryBlock newCanvasData;
  88. if (packetiser.reassemble (newCanvasData))
  89. {
  90. MemoryInputStream i (newCanvasData.getData(), newCanvasData.getSize(), false);
  91. canvas2.load (i);
  92. triggerAsyncUpdate();
  93. }
  94. }
  95. }
  96. //==============================================================================
  97. String getMachineInfoToDisplay() const
  98. {
  99. //auto display = Desktop::getInstance().getDisplays().getDisplayContaining (getScreenBounds().getCentre());
  100. return getOSName();// + " " + String (display.dpi) + " " + String (display.scale);
  101. }
  102. static String getOSName()
  103. {
  104. #if JUCE_MAC
  105. return "Mac OSX";
  106. #elif JUCE_ANDROID
  107. return "Android";
  108. #elif JUCE_IOS
  109. return "iOS";
  110. #elif JUCE_WINDOWS
  111. return "Windows";
  112. #elif JUCE_LINUX
  113. return "Linux";
  114. #endif
  115. }
  116. void paint (Graphics& g) override
  117. {
  118. g.fillAll (canvas.backgroundColour);
  119. auto clientArea = getAreaInGlobalSpace();
  120. if (clientArea.isEmpty())
  121. {
  122. g.setColour (Colours::red.withAlpha (0.5f));
  123. g.setFont (20.0f);
  124. g.drawText ("Not Connected", getLocalBounds(), Justification::centred, false);
  125. return;
  126. }
  127. canvas.draw (g, getLocalBounds().toFloat(), clientArea);
  128. g.setFont (Font (34.0f));
  129. g.setColour (Colours::white.withAlpha (0.6f));
  130. g.drawText (getMachineInfoToDisplay(),
  131. getLocalBounds().reduced (10).removeFromBottom (20),
  132. Justification::centredRight, true);
  133. if (error.isNotEmpty())
  134. {
  135. g.setColour (Colours::red);
  136. g.drawText (error, getLocalBounds().reduced (10).removeFromBottom (80),
  137. Justification::centredRight, true);
  138. }
  139. }
  140. Rectangle<float> getAreaInGlobalSpace() const
  141. {
  142. if (auto client = canvas.findClient (clientName))
  143. {
  144. auto screenBounds = getScreenBounds();
  145. auto display = Desktop::getInstance().getDisplays().getDisplayContaining (screenBounds.getCentre());
  146. return ((screenBounds - display.userArea.getCentre()).toFloat() / (client->scaleFactor * display.dpi / display.scale)) + client->centre;
  147. }
  148. return {};
  149. }
  150. Rectangle<float> getScreenAreaInGlobalSpace() const
  151. {
  152. if (auto client = canvas.findClient (clientName))
  153. {
  154. auto display = Desktop::getInstance().getDisplays().getDisplayContaining (getScreenBounds().getCentre());
  155. return (display.userArea.toFloat() / (client->scaleFactor * display.dpi / display.scale)).withCentre (client->centre);
  156. }
  157. return {};
  158. }
  159. void timerCallback() override
  160. {
  161. send (newClientOSCAddress, clientName + ":" + getIPAddress()
  162. + ":" + getScreenAreaInGlobalSpace().toString());
  163. }
  164. void handleAsyncUpdate() override
  165. {
  166. const ScopedLock sl (canvasLock);
  167. canvas.swapWith (canvas2);
  168. repaint();
  169. }
  170. SharedCanvasDescription canvas, canvas2;
  171. PropertiesFile& properties;
  172. String clientName, error;
  173. CriticalSection canvasLock;
  174. BlockPacketiser packetiser;
  175. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlaveCanvasComponent)
  176. };