/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-9 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online 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.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../jucedemo_headers.h" //============================================================================== class InterprocessCommsDemo : public Component, public ButtonListener, public ComboBoxListener { public: //============================================================================== InterprocessCommsDemo() { server = new DemoInterprocessConnectionServer (*this); setName (T("Interprocess Communication")); // create all our UI bits and pieces.. addAndMakeVisible (modeSelector = new ComboBox (T("mode:"))); modeSelector->setBounds (100, 25, 200, 24); (new Label (modeSelector->getName(), modeSelector->getName()))->attachToComponent (modeSelector, true); modeSelector->addItem (T("(Disconnected)"), 8); modeSelector->addSeparator(); modeSelector->addItem (T("Named pipe (listening)"), 1); modeSelector->addItem (T("Named pipe (connect to existing pipe)"), 5); modeSelector->addSeparator(); modeSelector->addItem (T("Socket (listening)"), 2); modeSelector->addItem (T("Socket (connect to existing socket)"), 6); modeSelector->setSelectedId (8); modeSelector->addListener (this); addAndMakeVisible (pipeName = new TextEditor (T("pipe name:"))); pipeName->setBounds (100, 60, 130, 24); pipeName->setMultiLine (false); pipeName->setText (T("juce demo pipe")); (new Label (pipeName->getName(), pipeName->getName()))->attachToComponent (pipeName, true); addAndMakeVisible (socketNumber = new TextEditor (T("socket port:"))); socketNumber->setBounds (350, 60, 80, 24); socketNumber->setMultiLine (false); socketNumber->setText (T("12345")); socketNumber->setInputRestrictions (5, T("0123456789")); (new Label (socketNumber->getName(), socketNumber->getName()))->attachToComponent (socketNumber, true); addAndMakeVisible (socketHost = new TextEditor (T("socket host:"))); socketHost->setBounds (530, 60, 130, 24); socketHost->setMultiLine (false); socketHost->setText (T("localhost")); socketNumber->setInputRestrictions (512); (new Label (socketHost->getName(), socketHost->getName()))->attachToComponent (socketHost, true); addChildComponent (sendText = new TextEditor (T("sendtext"))); sendText->setBounds (30, 120, 200, 24); sendText->setMultiLine (false); sendText->setReadOnly (false); sendText->setText (T("testing 1234")); addChildComponent (sendButton = new TextButton (T("send"), T("Fires off the message"))); sendButton->setBounds (240, 120, 200, 24); sendButton->changeWidthToFitText(); sendButton->addButtonListener (this); addChildComponent (incomingMessages = new TextEditor (T("messages"))); incomingMessages->setReadOnly (true); incomingMessages->setMultiLine (true); incomingMessages->setBounds (30, 150, 500, 250); // call this to set up everything's state correctly. comboBoxChanged (0); } ~InterprocessCommsDemo() { close(); delete server; deleteAllChildren(); } void buttonClicked (Button* button) { if (button == sendButton) { // The send button has been pressed, so write out the contents of the // text box to the socket or pipe, depending on which is active. const String text (sendText->getText()); MemoryBlock messageData (text.toUTF8(), text.getNumBytesAsUTF8()); for (int i = activeConnections.size(); --i >= 0;) { if (! activeConnections[i]->sendMessage (messageData)) { // the write failed, so indicate that the connection has broken.. appendMessage (T("send message failed!")); } } } } void comboBoxChanged (ComboBox*) { // This is called when the user picks a different mode from the drop-down list.. const int modeId = modeSelector->getSelectedId(); close(); if (modeId < 8) { open ((modeId & 2) != 0, (modeId & 4) != 0); } } //============================================================================== // Just closes any connections that are currently open. void close() { server->stop(); activeConnections.clear(); // Reset the UI stuff to a disabled state. sendText->setVisible (false); sendButton->setVisible (false); incomingMessages->setText (String::empty, false); incomingMessages->setVisible (true); appendMessage ( "To demonstrate named pipes, you'll need to run two instances of the JuceDemo application on this machine. On " "one of them, select \"named pipe (listening)\", and then on the other, select \"named pipe (connect to existing pipe)\". Then messages that you " "send from the 'sender' app should appear on the listener app. The \"pipe name\" field lets you choose a name for the pipe\n\n" "To demonstrate sockets, you can either run two instances of the app on the same machine, or on different " "machines on your network. In each one enter a socket number, then on one of the apps, select the " "\"Socket (listening)\" mode. On the other, enter the host address of the listening app, and select \"Socket (connect to existing socket)\". " "Messages should then be be sent between the apps in the same way as through the named pipes."); } void open (bool asSocket, bool asSender) { close(); // Make the appropriate bits of UI visible.. sendText->setVisible (true); sendButton->setVisible (true); incomingMessages->setText (String::empty, false); incomingMessages->setVisible (true); // and try to open the socket or pipe... bool openedOk = false; if (asSender) { // if we're connecting to an existing server, we can just create a connection object // directly. DemoInterprocessConnection* newConnection = new DemoInterprocessConnection (*this); if (asSocket) { openedOk = newConnection->connectToSocket (socketHost->getText(), socketNumber->getText().getIntValue(), 1000); } else { openedOk = newConnection->connectToPipe (pipeName->getText()); } if (openedOk) activeConnections.add (newConnection); else delete newConnection; } else { // if we're starting up a server, we need to tell the server to start waiting for // clients to connect. It'll then create connection objects for us when clients arrive. if (asSocket) { openedOk = server->beginWaitingForSocket (socketNumber->getText().getIntValue()); if (openedOk) appendMessage (T("Waiting for another app to connect to this socket..")); } else { DemoInterprocessConnection* newConnection = new DemoInterprocessConnection (*this); openedOk = newConnection->createPipe (pipeName->getText()); if (openedOk) { appendMessage (T("Waiting for another app to connect to this pipe..")); activeConnections.add (newConnection); } else { delete newConnection; } } } if (! openedOk) { modeSelector->setSelectedId (8); AlertWindow::showMessageBox (AlertWindow::WarningIcon, T("Interprocess Comms Demo"), T("Failed to open the socket or pipe...")); } } void appendMessage (const String& message) { incomingMessages->setCaretPosition (INT_MAX); incomingMessages->insertTextAtCaret (message + T("\n")); incomingMessages->setCaretPosition (INT_MAX); } //============================================================================== class DemoInterprocessConnection : public InterprocessConnection { InterprocessCommsDemo& owner; int ourNumber; public: DemoInterprocessConnection (InterprocessCommsDemo& owner_) : InterprocessConnection (true), owner (owner_) { static int totalConnections = 0; ourNumber = ++totalConnections; } ~DemoInterprocessConnection() { } void connectionMade() { owner.appendMessage (T("Connection #") + String (ourNumber) + T(" - connection started")); } void connectionLost() { owner.appendMessage (T("Connection #") + String (ourNumber) + T(" - connection lost")); } void messageReceived (const MemoryBlock& message) { owner.appendMessage (T("Connection #") + String (ourNumber) + T(" - message received: ") + message.toString()); } }; //============================================================================== class DemoInterprocessConnectionServer : public InterprocessConnectionServer { InterprocessCommsDemo& owner; public: DemoInterprocessConnectionServer (InterprocessCommsDemo& owner_) : owner (owner_) { } ~DemoInterprocessConnectionServer() { } InterprocessConnection* createConnectionObject() { DemoInterprocessConnection* newConnection = new DemoInterprocessConnection (owner); owner.activeConnections.add (newConnection); return newConnection; } }; OwnedArray activeConnections; //============================================================================== juce_UseDebuggingNewOperator private: ComboBox* modeSelector; TextEditor* sendText; TextButton* sendButton; TextEditor* incomingMessages; TextEditor* pipeName; TextEditor* socketNumber; TextEditor* socketHost; DemoInterprocessConnectionServer* server; }; //============================================================================== Component* createInterprocessCommsDemo() { return new InterprocessCommsDemo(); }