| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2020 - Raw Material Software Limited
 - 
 -    JUCE is an open source library subject to commercial or open-source
 -    licensing.
 - 
 -    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 -    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 - 
 -    End User License Agreement: www.juce.com/juce-6-licence
 -    Privacy Policy: www.juce.com/juce-privacy-policy
 - 
 -    Or: You may also use this code under the terms of the GPL v3 (see
 -    www.gnu.org/licenses).
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - 
 - /**
 -     This component runs in a client process, draws the part of the canvas that this
 -     particular client covers, and updates itself when messages arrive from the master
 -     containing new canvas states.
 - */
 - class ClientCanvasComponent  : public Component,
 -                                private OSCSender,
 -                                private OSCReceiver,
 -                                private OSCReceiver::Listener<OSCReceiver::RealtimeCallback>,
 -                                private AsyncUpdater,
 -                                private Timer
 - {
 - public:
 -     ClientCanvasComponent (PropertiesFile& p, int windowIndex)  : properties (p)
 -     {
 -         {
 -             String uuidPropName ("UUID" + String (windowIndex));
 -             clientName = properties.getValue (uuidPropName);
 - 
 -             if (clientName.isEmpty())
 -             {
 -                 clientName = "CLIENT_" + String (Random().nextInt (10000)).toUpperCase();
 -                 properties.setValue (uuidPropName, clientName);
 -             }
 -         }
 - 
 -         setOpaque (true);
 -         setSize (1500, 900);
 - 
 -         if (! OSCSender::connect (getBroadcastIPAddress(), clientPortNumber))
 -             error = "Client app OSC sender: network connection error.";
 - 
 -         if (! OSCReceiver::connect (masterPortNumber))
 -             error = "Client app OSC receiver: network connection error.";
 - 
 -         OSCReceiver::addListener (this);
 - 
 -         timerCallback();
 -         startTimer (2000);
 -     }
 - 
 -     ~ClientCanvasComponent() override
 -     {
 -         OSCReceiver::removeListener (this);
 -     }
 - 
 - private:
 -     void mouseDrag (const MouseEvent& e) override
 -     {
 -         auto clientArea = getAreaInGlobalSpace();
 - 
 -         if (! clientArea.isEmpty())
 -         {
 -             OSCMessage message (userInputOSCAddress);
 - 
 -             message.addString (clientName);
 -             message.addFloat32 (e.position.x * clientArea.getWidth()  / (float) getWidth()  + clientArea.getX());
 -             message.addFloat32 (e.position.y * clientArea.getHeight() / (float) getHeight() + clientArea.getY());
 - 
 -             send (message);
 -         }
 -     }
 - 
 -     //==============================================================================
 -     void oscMessageReceived (const OSCMessage& message) override
 -     {
 -         auto address = message.getAddressPattern();
 - 
 -         if (address.matches (canvasStateOSCAddress))
 -             canvasStateOSCMessageReceived (message);
 -     }
 - 
 -     struct NewStateMessage  : public Message
 -     {
 -         NewStateMessage (const MemoryBlock& d) : data (d) {}
 -         MemoryBlock data;
 -     };
 - 
 -     void canvasStateOSCMessageReceived (const OSCMessage& message)
 -     {
 -         if (message.isEmpty() || ! message[0].isBlob())
 -             return;
 - 
 -         if (packetiser.appendIncomingBlock (message[0].getBlob()))
 -         {
 -             const ScopedLock sl (canvasLock);
 - 
 -             MemoryBlock newCanvasData;
 - 
 -             if (packetiser.reassemble (newCanvasData))
 -             {
 -                 MemoryInputStream i (newCanvasData.getData(), newCanvasData.getSize(), false);
 -                 canvas2.load (i);
 -                 triggerAsyncUpdate();
 -             }
 -         }
 -     }
 - 
 -     //==============================================================================
 -     String getMachineInfoToDisplay() const
 -     {
 -         auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (getScreenBounds().getCentre());
 -         return getOSName() + "   " + String (display->dpi) + "   "  + String (display->scale);
 -     }
 - 
 -     static String getOSName()
 -     {
 -        #if JUCE_MAC
 -         return "Mac OSX";
 -        #elif JUCE_ANDROID
 -         return "Android";
 -        #elif JUCE_IOS
 -         return "iOS";
 -        #elif JUCE_WINDOWS
 -         return "Windows";
 -        #elif JUCE_LINUX
 -         return "Linux";
 -        #elif JUCE_BSD
 -         return "BSD";
 -        #endif
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (canvas.backgroundColour);
 - 
 -         auto clientArea = getAreaInGlobalSpace();
 - 
 -         if (clientArea.isEmpty())
 -         {
 -             g.setColour (Colours::red.withAlpha (0.5f));
 -             g.setFont (20.0f);
 -             g.drawText ("Not Connected", getLocalBounds(), Justification::centred, false);
 -             return;
 -         }
 - 
 -         canvas.draw (g, getLocalBounds().toFloat(), clientArea);
 - 
 -         g.setFont (Font (34.0f));
 -         g.setColour (Colours::white.withAlpha (0.6f));
 - 
 -         g.drawText (getMachineInfoToDisplay(),
 -                     getLocalBounds().reduced (10).removeFromBottom (20),
 -                     Justification::centredRight, true);
 - 
 -         if (error.isNotEmpty())
 -         {
 -             g.setColour (Colours::red);
 -             g.drawText (error, getLocalBounds().reduced (10).removeFromBottom (80),
 -                         Justification::centredRight, true);
 -         }
 -     }
 - 
 -     Rectangle<float> getAreaInGlobalSpace() const
 -     {
 -         if (auto client = canvas.findClient (clientName))
 -         {
 -             auto screenBounds = getScreenBounds();
 -             auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (screenBounds.getCentre());
 -             return ((screenBounds - display->userArea.getCentre()).toFloat() / (client->scaleFactor * display->dpi / display->scale)) + client->centre;
 -         }
 - 
 -         return {};
 -     }
 - 
 -     Rectangle<float> getScreenAreaInGlobalSpace() const
 -     {
 -         if (auto client = canvas.findClient (clientName))
 -         {
 -             auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (getScreenBounds().getCentre());
 -             return (display->userArea.toFloat() / (client->scaleFactor * display->dpi / display->scale)).withCentre (client->centre);
 -         }
 - 
 -         return {};
 -     }
 - 
 -     void timerCallback() override
 -     {
 -         send (newClientOSCAddress, clientName + ":" + IPAddress::getLocalAddress().toString()
 -                                               + ":" + getScreenAreaInGlobalSpace().toString());
 -     }
 - 
 -     void handleAsyncUpdate() override
 -     {
 -         const ScopedLock sl (canvasLock);
 -         canvas.swapWith (canvas2);
 -         repaint();
 -     }
 - 
 -     SharedCanvasDescription canvas, canvas2;
 -     PropertiesFile& properties;
 -     String clientName, error;
 - 
 -     CriticalSection canvasLock;
 -     BlockPacketiser packetiser;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ClientCanvasComponent)
 - };
 
 
  |