1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-13 00:04:19 +00:00

Added Animated App template and examples

This commit is contained in:
Felix Faire 2014-10-29 15:55:23 +00:00
parent fefcf7aca6
commit ff6520a89a
1141 changed files with 438491 additions and 94 deletions

View file

@ -0,0 +1,264 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
enum { magicMastSlaveConnectionHeader = 0x712baf04 };
static const char* startMessage = "__ipc_st";
static const char* killMessage = "__ipc_k_";
static const char* pingMessage = "__ipc_p_";
enum { specialMessageSize = 8, defaultTimeoutMs = 8000 };
static String getCommandLinePrefix (const String& commandLineUniqueID)
{
return "--" + commandLineUniqueID + ":";
}
//==============================================================================
// This thread sends and receives ping messages every second, so that it
// can find out if the other process has stopped running.
struct ChildProcessPingThread : public Thread,
private AsyncUpdater
{
ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout)
{
pingReceived();
}
static bool isPingMessage (const MemoryBlock& m) noexcept
{
return memcmp (m.getData(), pingMessage, specialMessageSize) == 0;
}
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
virtual bool sendPingMessage (const MemoryBlock&) = 0;
virtual void pingFailed() = 0;
int timeoutMs;
private:
Atomic<int> countdown;
void handleAsyncUpdate() override { pingFailed(); }
void run() override
{
while (! threadShouldExit())
{
if (--countdown <= 0 || ! sendPingMessage (MemoryBlock (pingMessage, specialMessageSize)))
{
triggerConnectionLostMessage();
break;
}
wait (1000);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread)
};
//==============================================================================
struct ChildProcessMaster::Connection : public InterprocessConnection,
private ChildProcessPingThread
{
Connection (ChildProcessMaster& m, const String& pipeName, int timeout)
: InterprocessConnection (false, magicMastSlaveConnectionHeader),
ChildProcessPingThread (timeout),
owner (m)
{
if (createPipe (pipeName, timeoutMs))
startThread (4);
}
~Connection()
{
stopThread (10000);
}
private:
void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); }
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); }
void pingFailed() override { connectionLost(); }
void messageReceived (const MemoryBlock& m) override
{
pingReceived();
if (m.getSize() != specialMessageSize || ! isPingMessage (m))
owner.handleMessageFromSlave (m);
}
ChildProcessMaster& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
};
//==============================================================================
ChildProcessMaster::ChildProcessMaster() {}
ChildProcessMaster::~ChildProcessMaster()
{
if (connection != nullptr)
{
sendMessageToSlave (MemoryBlock (killMessage, specialMessageSize));
connection->disconnect();
connection = nullptr;
}
}
void ChildProcessMaster::handleConnectionLost() {}
bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb)
{
if (connection != nullptr)
return connection->sendMessage (mb);
jassertfalse; // this can only be used when the connection is active!
return false;
}
bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID, int timeoutMs)
{
connection = nullptr;
jassert (childProcess.kill());
const String pipeName ("p" + String::toHexString (Random().nextInt64()));
StringArray args;
args.add (executable.getFullPathName());
args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName);
if (childProcess.start (args))
{
connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs);
if (connection->isConnected())
{
sendMessageToSlave (MemoryBlock (startMessage, specialMessageSize));
return true;
}
connection = nullptr;
}
return false;
}
//==============================================================================
struct ChildProcessSlave::Connection : public InterprocessConnection,
private ChildProcessPingThread
{
Connection (ChildProcessSlave& p, const String& pipeName, int timeout)
: InterprocessConnection (false, magicMastSlaveConnectionHeader),
ChildProcessPingThread (timeout),
owner (p)
{
connectToPipe (pipeName, timeoutMs);
startThread (4);
}
~Connection()
{
stopThread (10000);
}
private:
ChildProcessSlave& owner;
void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); }
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); }
void pingFailed() override { connectionLost(); }
void messageReceived (const MemoryBlock& m) override
{
pingReceived();
if (m.getSize() == specialMessageSize)
{
if (isPingMessage (m))
return;
if (memcmp (m.getData(), killMessage, specialMessageSize) == 0)
{
triggerConnectionLostMessage();
return;
}
if (memcmp (m.getData(), startMessage, specialMessageSize) == 0)
{
owner.handleConnectionMade();
return;
}
}
owner.handleMessageFromMaster (m);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
};
//==============================================================================
ChildProcessSlave::ChildProcessSlave() {}
ChildProcessSlave::~ChildProcessSlave() {}
void ChildProcessSlave::handleConnectionMade() {}
void ChildProcessSlave::handleConnectionLost() {}
bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb)
{
if (connection != nullptr)
return connection->sendMessage (mb);
jassertfalse; // this can only be used when the connection is active!
return false;
}
bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine,
const String& commandLineUniqueID,
int timeoutMs)
{
String prefix (getCommandLinePrefix (commandLineUniqueID));
if (commandLine.trim().startsWith (prefix))
{
String pipeName (commandLine.fromFirstOccurrenceOf (prefix, false, false)
.upToFirstOccurrenceOf (" ", false, false).trim());
if (pipeName.isNotEmpty())
{
connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs);
if (! connection->isConnected())
connection = nullptr;
}
}
return connection != nullptr;
}

View file

@ -0,0 +1,191 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED
#define JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED
//==============================================================================
/**
Acts as the slave end of a master/slave pair of connected processes.
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app
to spawn a child process, and to manage a 2-way messaging connection to control it.
To use the system, you need to create subclasses of both ChildProcessSlave and
ChildProcessMaster. To instantiate the ChildProcessSlave object, you must
add some code to your main() or JUCEApplication::initialise() function that
calls the initialiseFromCommandLine() method to check the app's command-line
parameters to see whether it's being launched as a child process. If this returns
true then the slave process can be allowed to run, and its handleMessageFromMaster()
method will be called whenever a message arrives.
The juce demo app has a good example of this class in action.
@see ChildProcessMaster, InterprocessConnection, ChildProcess
*/
class JUCE_API ChildProcessSlave
{
public:
/** Creates a non-connected slave process.
Use initialiseFromCommandLine to connect to a master process.
*/
ChildProcessSlave();
/** Destructor. */
virtual ~ChildProcessSlave();
/** This checks some command-line parameters to see whether they were generated by
ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process.
In an exe that can be used as a child process, you should add some code to your
main() or JUCEApplication::initialise() that calls this method.
The commandLineUniqueID should be a short alphanumeric identifier (no spaces!)
that matches the string passed to ChildProcessMaster::launchSlaveProcess().
The timeoutMs parameter lets you specify how long the child process is allowed
to run without receiving a ping from the master before the master is considered to
have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout
makes it use a default value.
Returns true if the command-line matches and the connection is made successfully.
*/
bool initialiseFromCommandLine (const String& commandLine,
const String& commandLineUniqueID,
int timeoutMs = 0);
//==============================================================================
/** This will be called to deliver messages from the master process.
The call will probably be made on a background thread, so be careful with your
thread-safety! You may want to respond by sending back a message with
sendMessageToMaster()
*/
virtual void handleMessageFromMaster (const MemoryBlock&) = 0;
/** This will be called when the master process finishes connecting to this slave.
The call will probably be made on a background thread, so be careful with your thread-safety!
*/
virtual void handleConnectionMade();
/** This will be called when the connection to the master process is lost.
The call may be made from any thread (including the message thread).
Typically, if your process only exists to act as a slave, you should probably exit
when this happens.
*/
virtual void handleConnectionLost();
/** Tries to send a message to the master process.
This returns true if the message was sent, but doesn't check that it actually gets
delivered at the other end. If successful, the data will emerge in a call to your
ChildProcessMaster::handleMessageFromSlave().
*/
bool sendMessageToMaster (const MemoryBlock&);
private:
struct Connection;
friend struct Connection;
friend struct ContainerDeletePolicy<Connection>;
ScopedPointer<Connection> connection;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave)
};
//==============================================================================
/**
Acts as the master in a master/slave pair of connected processes.
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app
to spawn a child process, and to manage a 2-way messaging connection to control it.
To use the system, you need to create subclasses of both ChildProcessSlave and
ChildProcessMaster. When you want your master process to launch the slave, you
just call launchSlaveProcess(), and it'll attempt to launch the executable that
you specify (which may be the same exe), and assuming it has been set-up to
correctly parse the command-line parameters (see ChildProcessSlave) then a
two-way connection will be created.
The juce demo app has a good example of this class in action.
@see ChildProcessSlave, InterprocessConnection, ChildProcess
*/
class JUCE_API ChildProcessMaster
{
public:
/** Creates an uninitialised master process object.
Use launchSlaveProcess to launch and connect to a child process.
*/
ChildProcessMaster();
/** Destructor. */
virtual ~ChildProcessMaster();
/** Attempts to launch and connect to a slave process.
This will start the given executable, passing it a special command-line
parameter based around the commandLineUniqueID string, which must be a
short alphanumeric string (no spaces!) that identifies your app. The exe
that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine()
in its startup code, and must use a matching ID to commandLineUniqueID.
The timeoutMs parameter lets you specify how long the child process is allowed
to go without sending a ping before it is considered to have died and
handleConnectionLost() will be called. Passing <= 0 for this timeout makes
it use a default value.
If this all works, the method returns true, and you can begin sending and
receiving messages with the slave process.
*/
bool launchSlaveProcess (const File& executableToLaunch,
const String& commandLineUniqueID,
int timeoutMs = 0);
/** This will be called to deliver a message from the slave process.
The call will probably be made on a background thread, so be careful with your thread-safety!
*/
virtual void handleMessageFromSlave (const MemoryBlock&) = 0;
/** This will be called when the slave process dies or is somehow disconnected.
The call will probably be made on a background thread, so be careful with your thread-safety!
*/
virtual void handleConnectionLost();
/** Attempts to send a message to the slave process.
This returns true if the message was dispatched, but doesn't check that it actually
gets delivered at the other end. If successful, the data will emerge in a call to
your ChildProcessSlave::handleMessageFromMaster().
*/
bool sendMessageToSlave (const MemoryBlock&);
private:
ChildProcess childProcess;
struct Connection;
friend struct Connection;
friend struct ContainerDeletePolicy<Connection>;
ScopedPointer<Connection> connection;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster)
};
#endif // JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED

View file

@ -0,0 +1,365 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
struct InterprocessConnection::ConnectionThread : public Thread
{
ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {}
void run() override { owner.runThread(); }
private:
InterprocessConnection& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread)
};
//==============================================================================
InterprocessConnection::InterprocessConnection (const bool callbacksOnMessageThread,
const uint32 magicMessageHeaderNumber)
: callbackConnectionState (false),
useMessageThread (callbacksOnMessageThread),
magicMessageHeader (magicMessageHeaderNumber),
pipeReceiveMessageTimeout (-1)
{
thread = new ConnectionThread (*this);
}
InterprocessConnection::~InterprocessConnection()
{
callbackConnectionState = false;
disconnect();
masterReference.clear();
thread = nullptr;
}
//==============================================================================
bool InterprocessConnection::connectToSocket (const String& hostName,
const int portNumber,
const int timeOutMillisecs)
{
disconnect();
const ScopedLock sl (pipeAndSocketLock);
socket = new StreamingSocket();
if (socket->connect (hostName, portNumber, timeOutMillisecs))
{
connectionMadeInt();
thread->startThread();
return true;
}
socket = nullptr;
return false;
}
bool InterprocessConnection::connectToPipe (const String& pipeName, const int timeoutMs)
{
disconnect();
ScopedPointer<NamedPipe> newPipe (new NamedPipe());
if (newPipe->openExisting (pipeName))
{
const ScopedLock sl (pipeAndSocketLock);
pipeReceiveMessageTimeout = timeoutMs;
initialiseWithPipe (newPipe.release());
return true;
}
return false;
}
bool InterprocessConnection::createPipe (const String& pipeName, const int timeoutMs)
{
disconnect();
ScopedPointer<NamedPipe> newPipe (new NamedPipe());
if (newPipe->createNewPipe (pipeName))
{
const ScopedLock sl (pipeAndSocketLock);
pipeReceiveMessageTimeout = timeoutMs;
initialiseWithPipe (newPipe.release());
return true;
}
return false;
}
void InterprocessConnection::disconnect()
{
thread->signalThreadShouldExit();
{
const ScopedLock sl (pipeAndSocketLock);
if (socket != nullptr) socket->close();
if (pipe != nullptr) pipe->close();
}
thread->stopThread (4000);
deletePipeAndSocket();
connectionLostInt();
}
void InterprocessConnection::deletePipeAndSocket()
{
const ScopedLock sl (pipeAndSocketLock);
socket = nullptr;
pipe = nullptr;
}
bool InterprocessConnection::isConnected() const
{
const ScopedLock sl (pipeAndSocketLock);
return ((socket != nullptr && socket->isConnected())
|| (pipe != nullptr && pipe->isOpen()))
&& thread->isThreadRunning();
}
String InterprocessConnection::getConnectedHostName() const
{
{
const ScopedLock sl (pipeAndSocketLock);
if (pipe == nullptr && socket == nullptr)
return String();
if (socket != nullptr && ! socket->isLocal())
return socket->getHostName();
}
return IPAddress::local().toString();
}
//==============================================================================
bool InterprocessConnection::sendMessage (const MemoryBlock& message)
{
uint32 messageHeader[2] = { ByteOrder::swapIfBigEndian (magicMessageHeader),
ByteOrder::swapIfBigEndian ((uint32) message.getSize()) };
MemoryBlock messageData (sizeof (messageHeader) + message.getSize());
messageData.copyFrom (messageHeader, 0, sizeof (messageHeader));
messageData.copyFrom (message.getData(), sizeof (messageHeader), message.getSize());
return writeData (messageData.getData(), (int) messageData.getSize()) == (int) messageData.getSize();
}
int InterprocessConnection::writeData (void* data, int dataSize)
{
const ScopedLock sl (pipeAndSocketLock);
if (socket != nullptr)
return socket->write (data, dataSize);
if (pipe != nullptr)
return pipe->write (data, dataSize, pipeReceiveMessageTimeout);
return 0;
}
//==============================================================================
void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket)
{
jassert (socket == nullptr && pipe == nullptr);
socket = newSocket;
connectionMadeInt();
thread->startThread();
}
void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe)
{
jassert (socket == nullptr && pipe == nullptr);
pipe = newPipe;
connectionMadeInt();
thread->startThread();
}
//==============================================================================
struct ConnectionStateMessage : public MessageManager::MessageBase
{
ConnectionStateMessage (InterprocessConnection* ipc, bool connected) noexcept
: owner (ipc), connectionMade (connected)
{}
void messageCallback() override
{
if (InterprocessConnection* const ipc = owner)
{
if (connectionMade)
ipc->connectionMade();
else
ipc->connectionLost();
}
}
WeakReference<InterprocessConnection> owner;
bool connectionMade;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage)
};
void InterprocessConnection::connectionMadeInt()
{
if (! callbackConnectionState)
{
callbackConnectionState = true;
if (useMessageThread)
(new ConnectionStateMessage (this, true))->post();
else
connectionMade();
}
}
void InterprocessConnection::connectionLostInt()
{
if (callbackConnectionState)
{
callbackConnectionState = false;
if (useMessageThread)
(new ConnectionStateMessage (this, false))->post();
else
connectionLost();
}
}
struct DataDeliveryMessage : public Message
{
DataDeliveryMessage (InterprocessConnection* ipc, const MemoryBlock& d)
: owner (ipc), data (d)
{}
void messageCallback() override
{
if (InterprocessConnection* const ipc = owner)
ipc->messageReceived (data);
}
WeakReference<InterprocessConnection> owner;
MemoryBlock data;
};
void InterprocessConnection::deliverDataInt (const MemoryBlock& data)
{
jassert (callbackConnectionState);
if (useMessageThread)
(new DataDeliveryMessage (this, data))->post();
else
messageReceived (data);
}
//==============================================================================
bool InterprocessConnection::readNextMessageInt()
{
uint32 messageHeader[2];
const int bytes = socket != nullptr ? socket->read (messageHeader, sizeof (messageHeader), true)
: pipe ->read (messageHeader, sizeof (messageHeader), -1);
if (bytes == sizeof (messageHeader)
&& ByteOrder::swapIfBigEndian (messageHeader[0]) == magicMessageHeader)
{
int bytesInMessage = (int) ByteOrder::swapIfBigEndian (messageHeader[1]);
if (bytesInMessage > 0)
{
MemoryBlock messageData ((size_t) bytesInMessage, true);
int bytesRead = 0;
while (bytesInMessage > 0)
{
if (thread->threadShouldExit())
return false;
const int numThisTime = jmin (bytesInMessage, 65536);
void* const data = addBytesToPointer (messageData.getData(), bytesRead);
const int bytesIn = socket != nullptr ? socket->read (data, numThisTime, true)
: pipe ->read (data, numThisTime, -1);
if (bytesIn <= 0)
break;
bytesRead += bytesIn;
bytesInMessage -= bytesIn;
}
if (bytesRead >= 0)
deliverDataInt (messageData);
}
}
else if (bytes < 0)
{
if (socket != nullptr)
deletePipeAndSocket();
connectionLostInt();
return false;
}
return true;
}
void InterprocessConnection::runThread()
{
while (! thread->threadShouldExit())
{
if (socket != nullptr)
{
const int ready = socket->waitUntilReady (true, 0);
if (ready < 0)
{
deletePipeAndSocket();
connectionLostInt();
break;
}
if (ready == 0)
{
thread->wait (1);
continue;
}
}
else if (pipe != nullptr)
{
if (! pipe->isOpen())
{
deletePipeAndSocket();
connectionLostInt();
break;
}
}
else
{
break;
}
if (thread->threadShouldExit() || ! readNextMessageInt())
break;
}
}

View file

@ -0,0 +1,209 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_INTERPROCESSCONNECTION_H_INCLUDED
#define JUCE_INTERPROCESSCONNECTION_H_INCLUDED
class InterprocessConnectionServer;
class MemoryBlock;
//==============================================================================
/**
Manages a simple two-way messaging connection to another process, using either
a socket or a named pipe as the transport medium.
To connect to a waiting socket or an open pipe, use the connectToSocket() or
connectToPipe() methods. If this succeeds, messages can be sent to the other end,
and incoming messages will result in a callback via the messageReceived()
method.
To open a pipe and wait for another client to connect to it, use the createPipe()
method.
To act as a socket server and create connections for one or more client, see the
InterprocessConnectionServer class.
@see InterprocessConnectionServer, Socket, NamedPipe
*/
class JUCE_API InterprocessConnection
{
public:
//==============================================================================
/** Creates a connection.
Connections are created manually, connecting them with the connectToSocket()
or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer
when a client wants to connect.
@param callbacksOnMessageThread if true, callbacks to the connectionMade(),
connectionLost() and messageReceived() methods will
always be made using the message thread; if false,
these will be called immediately on the connection's
own thread.
@param magicMessageHeaderNumber a magic number to use in the header to check the
validity of the data blocks being sent and received. This
can be any number, but the sender and receiver must obviously
use matching values or they won't recognise each other.
*/
InterprocessConnection (bool callbacksOnMessageThread = true,
uint32 magicMessageHeaderNumber = 0xf2b49e2c);
/** Destructor. */
virtual ~InterprocessConnection();
//==============================================================================
/** Tries to connect this object to a socket.
For this to work, the machine on the other end needs to have a InterprocessConnectionServer
object waiting to receive client connections on this port number.
@param hostName the host computer, either a network address or name
@param portNumber the socket port number to try to connect to
@param timeOutMillisecs how long to keep trying before giving up
@returns true if the connection is established successfully
@see Socket
*/
bool connectToSocket (const String& hostName,
int portNumber,
int timeOutMillisecs);
/** Tries to connect the object to an existing named pipe.
For this to work, another process on the same computer must already have opened
an InterprocessConnection object and used createPipe() to create a pipe for this
to connect to.
@param pipeName the name to use for the pipe - this should be unique to your app
@param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing
to the pipe, or -1 for an infinite timeout.
@returns true if it connects successfully.
@see createPipe, NamedPipe
*/
bool connectToPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs);
/** Tries to create a new pipe for other processes to connect to.
This creates a pipe with the given name, so that other processes can use
connectToPipe() to connect to the other end.
@param pipeName the name to use for the pipe - this should be unique to your app
@param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing
to the pipe, or -1 for an infinite timeout.
@returns true if the pipe was created, or false if it fails (e.g. if another process is
already using using the pipe).
*/
bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs);
/** Disconnects and closes any currently-open sockets or pipes. */
void disconnect();
/** True if a socket or pipe is currently active. */
bool isConnected() const;
/** Returns the socket that this connection is using (or nullptr if it uses a pipe). */
StreamingSocket* getSocket() const noexcept { return socket; }
/** Returns the pipe that this connection is using (or nullptr if it uses a socket). */
NamedPipe* getPipe() const noexcept { return pipe; }
/** Returns the name of the machine at the other end of this connection.
This may return an empty string if the name is unknown.
*/
String getConnectedHostName() const;
//==============================================================================
/** Tries to send a message to the other end of this connection.
This will fail if it's not connected, or if there's some kind of write error. If
it succeeds, the connection object at the other end will receive the message by
a callback to its messageReceived() method.
@see messageReceived
*/
bool sendMessage (const MemoryBlock& message);
//==============================================================================
/** Called when the connection is first connected.
If the connection was created with the callbacksOnMessageThread flag set, then
this will be called on the message thread; otherwise it will be called on a server
thread.
*/
virtual void connectionMade() = 0;
/** Called when the connection is broken.
If the connection was created with the callbacksOnMessageThread flag set, then
this will be called on the message thread; otherwise it will be called on a server
thread.
*/
virtual void connectionLost() = 0;
/** Called when a message arrives.
When the object at the other end of this connection sends us a message with sendMessage(),
this callback is used to deliver it to us.
If the connection was created with the callbacksOnMessageThread flag set, then
this will be called on the message thread; otherwise it will be called on a server
thread.
@see sendMessage
*/
virtual void messageReceived (const MemoryBlock& message) = 0;
private:
//==============================================================================
WeakReference<InterprocessConnection>::Master masterReference;
friend class WeakReference<InterprocessConnection>;
CriticalSection pipeAndSocketLock;
ScopedPointer <StreamingSocket> socket;
ScopedPointer <NamedPipe> pipe;
bool callbackConnectionState;
const bool useMessageThread;
const uint32 magicMessageHeader;
int pipeReceiveMessageTimeout;
friend class InterprocessConnectionServer;
void initialiseWithSocket (StreamingSocket*);
void initialiseWithPipe (NamedPipe*);
void deletePipeAndSocket();
void connectionMadeInt();
void connectionLostInt();
void deliverDataInt (const MemoryBlock&);
bool readNextMessageInt();
struct ConnectionThread;
friend struct ConnectionThread;
friend struct ContainerDeletePolicy<ConnectionThread>;
ScopedPointer<ConnectionThread> thread;
void runThread();
int writeData (void*, int);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection)
};
#endif // JUCE_INTERPROCESSCONNECTION_H_INCLUDED

View file

@ -0,0 +1,73 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
InterprocessConnectionServer::InterprocessConnectionServer()
: Thread ("Juce IPC server")
{
}
InterprocessConnectionServer::~InterprocessConnectionServer()
{
stop();
}
//==============================================================================
bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber)
{
stop();
socket = new StreamingSocket();
if (socket->createListener (portNumber))
{
startThread();
return true;
}
socket = nullptr;
return false;
}
void InterprocessConnectionServer::stop()
{
signalThreadShouldExit();
if (socket != nullptr)
socket->close();
stopThread (4000);
socket = nullptr;
}
void InterprocessConnectionServer::run()
{
while ((! threadShouldExit()) && socket != nullptr)
{
ScopedPointer<StreamingSocket> clientSocket (socket->waitForNextConnection());
if (clientSocket != nullptr)
if (InterprocessConnection* newConnection = createConnectionObject())
newConnection->initialiseWithSocket (clientSocket.release());
}
}

View file

@ -0,0 +1,93 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED
#define JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED
//==============================================================================
/**
An object that waits for client sockets to connect to a port on this host, and
creates InterprocessConnection objects for each one.
To use this, create a class derived from it which implements the createConnectionObject()
method, so that it creates suitable connection objects for each client that tries
to connect.
@see InterprocessConnection
*/
class JUCE_API InterprocessConnectionServer : private Thread
{
public:
//==============================================================================
/** Creates an uninitialised server object.
*/
InterprocessConnectionServer();
/** Destructor. */
~InterprocessConnectionServer();
//==============================================================================
/** Starts an internal thread which listens on the given port number.
While this is running, in another process tries to connect with the
InterprocessConnection::connectToSocket() method, this object will call
createConnectionObject() to create a connection to that client.
Use stop() to stop the thread running.
@see createConnectionObject, stop
*/
bool beginWaitingForSocket (int portNumber);
/** Terminates the listener thread, if it's active.
@see beginWaitingForSocket
*/
void stop();
protected:
/** Creates a suitable connection object for a client process that wants to
connect to this one.
This will be called by the listener thread when a client process tries
to connect, and must return a new InterprocessConnection object that will
then run as this end of the connection.
@see InterprocessConnection
*/
virtual InterprocessConnection* createConnectionObject() = 0;
private:
//==============================================================================
ScopedPointer <StreamingSocket> socket;
void run() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnectionServer)
};
#endif // JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED