// Created: 2007-11-08
// RCS-ID: $Id$
// Copyright: (c) 2007 Anders Larsen
-// License: wxWindows licence
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// we're using TCP/IP or real DDE.
#include "ipcsetup.h"
+#include "connection.h"
+
#include "wx/timer.h"
#include "wx/datetime.h"
+#include "wx/vector.h"
+
+class MyClient;
// ----------------------------------------------------------------------------
-// wxWin macros
+// classes
// ----------------------------------------------------------------------------
-
-// Define a new application
-class MyClient;
-class MyConnection;
-
-class MyApp: public wxApp
+class MyApp : public wxApp
{
public:
+ MyApp() { Connect(wxEVT_IDLE, wxIdleEventHandler(MyApp::OnIdle)); }
+
virtual bool OnInit();
virtual int OnExit();
-protected:
- MyClient *m_client;
+private:
+ void OnIdle(wxIdleEvent& event);
+
+ MyClient *m_client;
};
-class MyConnection: public wxConnection
+class MyConnection : public MyConnectionBase
{
public:
- MyConnection();
- virtual ~MyConnection();
virtual bool DoExecute(const void *data, size_t size, wxIPCFormat format);
virtual const void *Request(const wxString& item, size_t *size = NULL, wxIPCFormat format = wxIPC_TEXT);
virtual bool DoPoke(const wxString& item, const void* data, size_t size, wxIPCFormat format);
virtual bool OnAdvise(const wxString& topic, const wxString& item, const void *data, size_t size, wxIPCFormat format);
virtual bool OnDisconnect();
-
-protected:
- void Log(const wxString& command, const wxString& topic,
- const wxString& item, const void *data, size_t size, wxIPCFormat format);
};
-class MyClient: public wxClient, public wxTimer
+class MyClient : public wxClient,
+ private wxTimer
{
public:
MyClient();
virtual ~MyClient();
+
bool Connect(const wxString& sHost, const wxString& sService, const wxString& sTopic);
void Disconnect();
wxConnectionBase *OnMakeConnection();
bool IsConnected() { return m_connection != NULL; };
+
virtual void Notify();
-protected:
- MyConnection *m_connection;
- int m_step;
+ void StartNextTestIfNecessary();
+
+private:
+ void TestRequest();
+ void TestPoke();
+ void TestExecute();
+ void TestStartAdvise();
+ void TestStopAdvise();
+ void TestDisconnect();
+
+
+ MyConnection *m_connection;
+
+ // the test functions to be executed by StartNextTestIfNecessary()
+ typedef void (MyClient::*MyClientTestFunc)();
+ wxVector<MyClientTestFunc> m_tests;
+
+ // number of seconds since the start of the test
+ int m_step;
};
// ============================================================================
// implementation
// ============================================================================
-IMPLEMENT_APP(MyApp)
+IMPLEMENT_APP_CONSOLE(MyApp)
// ----------------------------------------------------------------------------
// MyApp
if ( !wxApp::OnInit() )
return false;
- delete wxLog::SetActiveTarget(new wxLogStderr);
-
// Create a new client
m_client = new MyClient;
bool retval = m_client->Connect("localhost", "4242", "IPC TEST");
- wxLogMessage(_T("Client host=\"localhost\" port=\"4242\" topic=\"IPC TEST\" %s"),
- retval ? _T("connected") : _T("failed to connect"));
+ wxLogMessage("Client host=\"localhost\" port=\"4242\" topic=\"IPC TEST\" %s",
+ retval ? "connected" : "failed to connect");
return retval;
}
return 0;
}
+void MyApp::OnIdle(wxIdleEvent& event)
+{
+ if ( m_client )
+ m_client->StartNextTestIfNecessary();
+
+ event.Skip();
+}
+
// ----------------------------------------------------------------------------
// MyClient
// ----------------------------------------------------------------------------
-MyClient::MyClient() : wxClient()
+MyClient::MyClient()
+ : wxClient()
{
m_connection = NULL;
m_step = 0;
}
-bool MyClient::Connect(const wxString& sHost, const wxString& sService, const wxString& sTopic)
+bool
+MyClient::Connect(const wxString& sHost,
+ const wxString& sService,
+ const wxString& sTopic)
{
// suppress the log messages from MakeConnection()
wxLogNull nolog;
m_connection = (MyConnection *)MakeConnection(sHost, sService, sTopic);
- if (m_connection)
- Start(1000, false);
- return m_connection != NULL;
+ if ( !m_connection )
+ return false;
+
+ Start(1000);
+
+ return true;
}
wxConnectionBase *MyClient::OnMakeConnection()
if (m_connection)
{
m_connection->Disconnect();
- delete m_connection;
- m_connection = NULL;
- wxLogMessage(_T("Client disconnected from server"));
+ wxDELETE(m_connection);
+ wxLogMessage("Client disconnected from server");
}
wxGetApp().ExitMainLoop();
}
void MyClient::Notify()
{
- switch (m_step++)
+ // we shouldn't call wxIPC methods from here directly as we may be called
+ // from inside an IPC call when using TCP/IP as the sockets are used in
+ // non-blocking code and so can dispatch events, including the timer ones,
+ // while waiting for IO and so starting another IPC call would result in
+ // fatal reentrancies -- instead, just set a flag and perform the test
+ // indicated by it later from our idle event handler
+ MyClientTestFunc testfunc = NULL;
+ switch ( m_step++ )
{
case 0:
- {
- size_t size;
- m_connection->Request(_T("Date"));
- m_connection->Request(_T("Date+len"), &size);
- m_connection->Request(_T("bytes[3]"), &size, wxIPC_PRIVATE);
+ testfunc = &MyClient::TestRequest;
break;
- }
+
case 1:
- {
- wxString s = wxDateTime::Now().Format();
- m_connection->Poke(_T("Date"), s);
- s = wxDateTime::Now().FormatTime() + _T(" ") + wxDateTime::Now().FormatDate();
- m_connection->Poke(_T("Date"), (const char *)s.c_str(), s.length() + 1);
- char bytes[3];
- bytes[0] = '1'; bytes[1] = '2'; bytes[2] = '3';
- m_connection->Poke(_T("bytes[3]"), bytes, 3, wxIPC_PRIVATE);
+ testfunc = &MyClient::TestPoke;
break;
- }
+
case 2:
- {
- wxString s = _T("Date");
- m_connection->Execute(s);
- m_connection->Execute((const char *)s.c_str(), s.length() + 1);
-#if wxUSE_DDE_FOR_IPC
- wxLogMessage(_T("DDE Execute can only be used to send text strings, not arbitrary data.\nThe type argument will be ignored, text truncated, converted to Unicode and null terminated."));
-#endif
- char bytes[3];
- bytes[0] = '1'; bytes[1] = '2'; bytes[2] = '3';
- m_connection->Execute(bytes, 3, wxIPC_PRIVATE);
+ testfunc = &MyClient::TestExecute;
break;
- }
+
case 3:
- wxLogMessage(_T("StartAdvise(\"something\")"));
- m_connection->StartAdvise(_T("something"));
+ testfunc = &MyClient::TestStartAdvise;
break;
+
case 10:
- wxLogMessage(_T("StopAdvise(\"something\")"));
- m_connection->StopAdvise(_T("something"));
+ testfunc = &MyClient::TestStopAdvise;
break;
+
case 15:
- Disconnect();
+ testfunc = &MyClient::TestDisconnect;
+ // We don't need the timer any more, we're going to exit soon.
+ Stop();
break;
+
+ default:
+ // No need to wake up idle handling.
+ return;
}
+
+ m_tests.push_back(testfunc);
+
+ wxWakeUpIdle();
}
-// ----------------------------------------------------------------------------
-// MyConnection
-// ----------------------------------------------------------------------------
+void MyClient::StartNextTestIfNecessary()
+{
+ while ( !m_tests.empty() )
+ {
+ MyClientTestFunc testfunc = m_tests.front();
+ m_tests.erase(m_tests.begin());
+ (this->*testfunc)();
+ }
+}
-MyConnection::MyConnection()
+void MyClient::TestRequest()
{
+ size_t size;
+ m_connection->Request("Date");
+ m_connection->Request("Date+len", &size);
+ m_connection->Request("bytes[3]", &size, wxIPC_PRIVATE);
}
-MyConnection::~MyConnection()
+void MyClient::TestPoke()
{
+ wxString s = wxDateTime::Now().Format();
+ m_connection->Poke("Date", s);
+ s = wxDateTime::Now().FormatTime() + " " + wxDateTime::Now().FormatDate();
+ m_connection->Poke("Date", (const char *)s.c_str(), s.length() + 1);
+ char bytes[3];
+ bytes[0] = '1'; bytes[1] = '2'; bytes[2] = '3';
+ m_connection->Poke("bytes[3]", bytes, 3, wxIPC_PRIVATE);
}
-void MyConnection::Log(const wxString& command, const wxString& topic,
- const wxString& item, const void *data, size_t size, wxIPCFormat format)
+void MyClient::TestExecute()
{
- wxString s;
- if (topic.IsEmpty() && item.IsEmpty())
- s.Printf(_T("%s("), command.c_str());
- else if (topic.IsEmpty())
- s.Printf(_T("%s(item=\"%s\","), command.c_str(), item.c_str());
- else if (item.IsEmpty())
- s.Printf(_T("%s(topic=\"%s\","), command.c_str(), topic.c_str());
- else
- s.Printf(_T("%s(topic=\"%s\",item=\"%s\","), command.c_str(), topic.c_str(), item.c_str());
-
- switch (format)
- {
- case wxIPC_TEXT:
- case wxIPC_UTF8TEXT:
-#if !wxUSE_UNICODE || wxUSE_UNICODE_UTF8
- wxLogMessage(_T("%s\"%s\",%d)"), s.c_str(), data, size);
-#else
- wxLogMessage(_T("%s\"%s\",%d)"), s.c_str(), wxConvUTF8.cMB2WC((const char*)data), size);
-#endif
- break;
- case wxIPC_PRIVATE:
- if (size == 3)
- {
- char *bytes = (char *)data;
- wxLogMessage(_T("%s'%c%c%c',%d)"), s.c_str(), bytes[0], bytes[1], bytes[2], size);
- }
- else
- wxLogMessage(_T("%s...,%d)"), s.c_str(), size);
- break;
- case wxIPC_INVALID:
- wxLogMessage(_T("%s[invalid data],%d)"), s.c_str(), size);
- break;
- default:
- wxLogMessage(_T("%s[unknown data],%d)"), s.c_str(), size);
- break;
- }
+ wxString s = "Date";
+ m_connection->Execute(s);
+ m_connection->Execute((const char *)s.c_str(), s.length() + 1);
+ char bytes[3];
+ bytes[0] = '1';
+ bytes[1] = '2';
+ bytes[2] = '3';
+ m_connection->Execute(bytes, WXSIZEOF(bytes));
+}
+
+void MyClient::TestStartAdvise()
+{
+ wxLogMessage("StartAdvise(\"something\")");
+ m_connection->StartAdvise("something");
+}
+
+void MyClient::TestStopAdvise()
+{
+ wxLogMessage("StopAdvise(\"something\")");
+ m_connection->StopAdvise("something");
}
+void MyClient::TestDisconnect()
+{
+ Disconnect();
+}
+
+// ----------------------------------------------------------------------------
+// MyConnection
+// ----------------------------------------------------------------------------
+
bool MyConnection::OnAdvise(const wxString& topic, const wxString& item, const void *data,
size_t size, wxIPCFormat format)
{
- Log(_T("OnAdvise"), topic, item, data, size, format);
+ Log("OnAdvise", topic, item, data, size, format);
return true;
}
bool MyConnection::OnDisconnect()
{
- wxLogMessage(_T("OnDisconnect()"));
+ wxLogMessage("OnDisconnect()");
wxGetApp().ExitMainLoop();
return true;
}
bool MyConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
{
- Log(_T("Execute"), wxEmptyString, wxEmptyString, data, size, format);
+ Log("Execute", wxEmptyString, wxEmptyString, data, size, format);
bool retval = wxConnection::DoExecute(data, size, format);
if (!retval)
- wxLogMessage(_T("Execute failed!"));
+ {
+ wxLogMessage("Execute failed!");
+ }
return retval;
}
const void *MyConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
{
const void *data = wxConnection::Request(item, size, format);
- Log(_T("Request"), wxEmptyString, item, data, size ? *size : wxNO_LEN, format);
+ Log("Request", wxEmptyString, item, data, size ? *size : wxNO_LEN, format);
return data;
}
bool MyConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
{
- Log(_T("Poke"), wxEmptyString, item, data, size, format);
+ Log("Poke", wxEmptyString, item, data, size, format);
return wxConnection::DoPoke(item, data, size, format);
}