/* ============================================================================== 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 Button::Listener, public ComboBox::Listener { public: //============================================================================== InterprocessCommsDemo() { server = new DemoInterprocessConnectionServer (*this); setName ("Interprocess Communication"); // create all our UI bits and pieces.. addAndMakeVisible (modeSelector = new ComboBox ("mode:")); modeSelector->setBounds (100, 25, 200, 24); (new Label (modeSelector->getName(), modeSelector->getName()))->attachToComponent (modeSelector, true); modeSelector->addItem ("(Disconnected)", 8); modeSelector->addSeparator(); modeSelector->addItem ("Named pipe (listening)", 1); modeSelector->addItem ("Named pipe (connect to existing pipe)", 5); modeSelector->addSeparator(); modeSelector->addItem ("Socket (listening)", 2); modeSelector->addItem ("Socket (connect to existing socket)", 6); modeSelector->setSelectedId (8); modeSelector->addListener (this); addAndMakeVisible (pipeName = new TextEditor ("pipe name:")); pipeName->setBounds (100, 60, 130, 24); pipeName->setMultiLine (false); pipeName->setText ("juce demo pipe"); (new Label (pipeName->getName(), pipeName->getName()))->attachToComponent (pipeName, true); addAndMakeVisible (socketNumber = new TextEditor ("socket port:")); socketNumber->setBounds (350, 60, 80, 24); socketNumber->setMultiLine (false); socketNumber->setText ("12345"); socketNumber->setInputRestrictions (5, "0123456789"); (new Label (socketNumber->getName(), socketNumber->getName()))->attachToComponent (socketNumber, true); addAndMakeVisible (socketHost = new TextEditor ("socket host:")); socketHost->setBounds (530, 60, 130, 24); socketHost->setMultiLine (false); socketHost->setText ("localhost"); socketNumber->setInputRestrictions (512); (new Label (socketHost->getName(), socketHost->getName()))->attachToComponent (socketHost, true); addChildComponent (sendText = new TextEditor ("sendtext")); sendText->setBounds (30, 120, 200, 24); sendText->setMultiLine (false); sendText->setReadOnly (false); sendText->setText ("testing 1234"); addChildComponent (sendButton = new TextButton ("send", "Fires off the message")); sendButton->setBounds (240, 120, 200, 24); sendButton->changeWidthToFitText(); sendButton->addButtonListener (this); addChildComponent (incomingMessages = new TextEditor ("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 ("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 ("Waiting for another app to connect to this socket.."); } else { DemoInterprocessConnection* newConnection = new DemoInterprocessConnection (*this); openedOk = newConnection->createPipe (pipeName->getText()); if (openedOk) { appendMessage ("Waiting for another app to connect to this pipe.."); activeConnections.add (newConnection); } else { delete newConnection; } } } if (! openedOk) { modeSelector->setSelectedId (8); AlertWindow::showMessageBox (AlertWindow::WarningIcon, "Interprocess Comms Demo", "Failed to open the socket or pipe..."); } } void appendMessage (const String& message) { incomingMessages->setCaretPosition (INT_MAX); incomingMessages->insertTextAtCaret (message + "\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 ("Connection #" + String (ourNumber) + " - connection started"); } void connectionLost() { owner.appendMessage ("Connection #" + String (ourNumber) + " - connection lost"); } void messageReceived (const MemoryBlock& message) { owner.appendMessage ("Connection #" + String (ourNumber) + " - 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(); }