X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d93c719ac09362890adde4f6f0e5962aed816280..dd46ee66e56b6d05cbf441dc59d1df3fe955e2b7:/samples/exec/exec.cpp diff --git a/samples/exec/exec.cpp b/samples/exec/exec.cpp index 884e6e147e..30bed13776 100644 --- a/samples/exec/exec.cpp +++ b/samples/exec/exec.cpp @@ -17,7 +17,7 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(__APPLE__) #pragma implementation "exec.cpp" #pragma interface "exec.cpp" #endif @@ -33,21 +33,39 @@ // need because it includes almost all "standard" wxWindows headers #ifndef WX_PRECOMP #include "wx/app.h" + #include "wx/log.h" #include "wx/frame.h" + #include "wx/panel.h" + + #include "wx/timer.h" + #include "wx/utils.h" #include "wx/menu.h" + #include "wx/msgdlg.h" #include "wx/textdlg.h" + #include "wx/filedlg.h" + #include "wx/choicdlg.h" + + #include "wx/button.h" + #include "wx/textctrl.h" + #include "wx/listbox.h" + + #include "wx/sizer.h" #endif +#include "wx/txtstrm.h" + #include "wx/process.h" +#include "wx/mimetype.h" + #ifdef __WINDOWS__ #include "wx/dde.h" #endif // __WINDOWS__ // ---------------------------------------------------------------------------- -// private classes +// the usual application and main frame classes // ---------------------------------------------------------------------------- // Define a new application type, each program should derive a class from wxApp @@ -63,6 +81,10 @@ public: virtual bool OnInit(); }; +// Define an array of process pointers used by MyFrame +class MyPipedProcess; +WX_DEFINE_ARRAY(MyPipedProcess *, MyProcessesArray); + // Define a new frame type: this is going to be our main frame class MyFrame : public wxFrame { @@ -73,25 +95,142 @@ public: // event handlers (these functions should _not_ be virtual) void OnQuit(wxCommandEvent& event); + void OnKill(wxCommandEvent& event); + + void OnClear(wxCommandEvent& event); + void OnSyncExec(wxCommandEvent& event); void OnAsyncExec(wxCommandEvent& event); void OnShell(wxCommandEvent& event); - void OnDDEExec(wxCommandEvent& event); + void OnExecWithRedirect(wxCommandEvent& event); + void OnExecWithPipe(wxCommandEvent& event); + + void OnPOpen(wxCommandEvent& event); + + void OnFileExec(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); + // polling output of async processes + void OnTimer(wxTimerEvent& event); + void OnIdle(wxIdleEvent& event); + + // for MyPipedProcess + void OnProcessTerminated(MyPipedProcess *process); + wxListBox *GetLogListBox() const { return m_lbox; } + private: + void ShowOutput(const wxString& cmd, + const wxArrayString& output, + const wxString& title); + + void DoAsyncExec(const wxString& cmd); + + void AddAsyncProcess(MyPipedProcess *process) + { + if ( m_running.IsEmpty() ) + { + // we want to start getting the timer events to ensure that a + // steady stream of idle events comes in -- otherwise we + // wouldn't be able to poll the child process input + m_timerIdleWakeUp.Start(100); + } + //else: the timer is already running + + m_running.Add(process); + } + + void RemoveAsyncProcess(MyPipedProcess *process) + { + m_running.Remove(process); + + if ( m_running.IsEmpty() ) + { + // we don't need to get idle events all the time any more + m_timerIdleWakeUp.Stop(); + } + } + + // the PID of the last process we launched asynchronously + long m_pidLast; + + // last command we executed wxString m_cmdLast; +#ifdef __WINDOWS__ + void OnDDEExec(wxCommandEvent& event); + void OnDDERequest(wxCommandEvent& event); + + bool GetDDEServer(); + + // last params of a DDE transaction + wxString m_server, + m_topic, + m_cmdDde; +#endif // __WINDOWS__ + + wxListBox *m_lbox; + + MyProcessesArray m_running; + + // the idle event wake up timer + wxTimer m_timerIdleWakeUp; + // any class wishing to process wxWindows events must use this macro DECLARE_EVENT_TABLE() }; +// ---------------------------------------------------------------------------- +// MyPipeFrame: allows the user to communicate with the child process +// ---------------------------------------------------------------------------- + +class MyPipeFrame : public wxFrame +{ +public: + MyPipeFrame(wxFrame *parent, + const wxString& cmd, + wxProcess *process); + +protected: + void OnTextEnter(wxCommandEvent& event) { DoSend(); } + void OnBtnSend(wxCommandEvent& event) { DoSend(); } + void OnBtnGet(wxCommandEvent& event) { DoGet(); } + + void OnClose(wxCloseEvent& event); + + void OnProcessTerm(wxProcessEvent& event); + + void DoSend() + { + m_out.WriteString(m_textIn->GetValue() + '\n'); + m_textIn->Clear(); + + DoGet(); + } + + void DoGet(); + +private: + wxProcess *m_process; + + wxTextInputStream m_in; + wxTextOutputStream m_out; + + wxTextCtrl *m_textIn, + *m_textOut; + + DECLARE_EVENT_TABLE() +}; + +// ---------------------------------------------------------------------------- +// wxProcess-derived classes +// ---------------------------------------------------------------------------- + // This is the handler for process termination events class MyProcess : public wxProcess { public: - MyProcess(wxFrame *parent, const wxString& cmd) + MyProcess(MyFrame *parent, const wxString& cmd) : wxProcess(parent), m_cmd(cmd) { m_parent = parent; @@ -102,11 +241,42 @@ public: // cases virtual void OnTerminate(int pid, int status); -private: - wxFrame *m_parent; +protected: + MyFrame *m_parent; wxString m_cmd; }; +// A specialization of MyProcess for redirecting the output +class MyPipedProcess : public MyProcess +{ +public: + MyPipedProcess(MyFrame *parent, const wxString& cmd) + : MyProcess(parent, cmd) + { + Redirect(); + } + + virtual void OnTerminate(int pid, int status); + + virtual bool HasInput(); +}; + +// A version of MyPipedProcess which also sends input to the stdin of the +// child process +class MyPipedProcess2 : public MyPipedProcess +{ +public: + MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input) + : MyPipedProcess(parent, cmd), m_input(input) + { + } + + virtual bool HasInput(); + +private: + wxString m_input; +}; + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -116,11 +286,22 @@ enum { // menu items Exec_Quit = 100, + Exec_Kill, + Exec_ClearLog, Exec_SyncExec = 200, Exec_AsyncExec, Exec_Shell, + Exec_POpen, + Exec_OpenFile, Exec_DDEExec, - Exec_About = 300 + Exec_DDERequest, + Exec_Redirect, + Exec_Pipe, + Exec_About = 300, + + // control ids + Exec_Btn_Send = 1000, + Exec_Btn_Get }; static const wxChar *DIALOG_TITLE = _T("Exec sample"); @@ -134,13 +315,40 @@ static const wxChar *DIALOG_TITLE = _T("Exec sample"); // simple menu events like this the static method is much simpler. BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(Exec_Quit, MyFrame::OnQuit) + EVT_MENU(Exec_Kill, MyFrame::OnKill) + EVT_MENU(Exec_ClearLog, MyFrame::OnClear) EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec) EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec) EVT_MENU(Exec_Shell, MyFrame::OnShell) + EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect) + EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe) + + EVT_MENU(Exec_POpen, MyFrame::OnPOpen) + + EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec) + +#ifdef __WINDOWS__ EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec) + EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest) +#endif // __WINDOWS__ EVT_MENU(Exec_About, MyFrame::OnAbout) + + EVT_IDLE(MyFrame::OnIdle) + + EVT_TIMER(-1, MyFrame::OnTimer) +END_EVENT_TABLE() + +BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame) + EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend) + EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet) + + EVT_TEXT_ENTER(-1, MyPipeFrame::OnTextEnter) + + EVT_CLOSE(MyPipeFrame::OnClose) + + EVT_END_PROCESS(-1, MyPipeFrame::OnProcessTerm) END_EVENT_TABLE() // Create a new application object: this macro will allow wxWindows to create @@ -181,19 +389,25 @@ bool MyApp::OnInit() // frame constructor MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) - : wxFrame((wxFrame *)NULL, -1, title, pos, size) + : wxFrame((wxFrame *)NULL, -1, title, pos, size), + m_timerIdleWakeUp(this) { + m_pidLast = 0; + #ifdef __WXMAC__ // we need this in order to allow the about menu relocation, since ABOUT is // not the default id of the about menu wxApp::s_macAboutMenuItemId = Exec_About; #endif - // set the frame icon - SetIcon(wxICON(mondrian)); - // create a menu bar wxMenu *menuFile = new wxMenu(_T(""), wxMENU_TEAROFF); + menuFile->Append(Exec_Kill, _T("&Kill process...\tCtrl-K"), + _T("Kill a process by PID")); + menuFile->AppendSeparator(); + menuFile->Append(Exec_ClearLog, _T("&Clear log\tCtrl-C"), + _T("Clear the log window")); + menuFile->AppendSeparator(); menuFile->Append(Exec_Quit, _T("E&xit\tAlt-X"), _T("Quit this program")); wxMenu *execMenu = new wxMenu; @@ -203,10 +417,21 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) _T("Launch a program and return immediately")); execMenu->Append(Exec_Shell, _T("Execute &shell command...\tCtrl-S"), _T("Launch a shell and execute a command in it")); + execMenu->AppendSeparator(); + execMenu->Append(Exec_Redirect, _T("Capture command &output...\tCtrl-O"), + _T("Launch a program and capture its output")); + execMenu->Append(Exec_Pipe, _T("&Pipe through command..."), + _T("Pipe a string through a filter")); + execMenu->Append(Exec_POpen, _T("&Open a pipe to a command...\tCtrl-P"), + _T("Open a pipe to and from another program")); + execMenu->AppendSeparator(); + execMenu->Append(Exec_OpenFile, _T("Open &file...\tCtrl-F"), + _T("Launch the command to open this kind of files")); #ifdef __WINDOWS__ execMenu->AppendSeparator(); execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D")); + execMenu->Append(Exec_DDERequest, _T("Send DDE &request...\tCtrl-R")); #endif wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF); @@ -221,15 +446,23 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) // ... and attach this menu bar to the frame SetMenuBar(menuBar); + // create the listbox in which we will show misc messages as they come + m_lbox = new wxListBox(this, -1); + wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, + wxFONTWEIGHT_NORMAL); + if ( font.Ok() ) + m_lbox->SetFont(font); + #if wxUSE_STATUSBAR // create a status bar just for fun (by default with 1 pane only) - CreateStatusBar(2); - SetStatusText(_T("Welcome to wxWindows!")); + CreateStatusBar(); + SetStatusText(_T("Welcome to wxWindows exec sample!")); #endif // wxUSE_STATUSBAR } - -// event handlers +// ---------------------------------------------------------------------------- +// event handlers: file and help menu +// ---------------------------------------------------------------------------- void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { @@ -237,12 +470,139 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) Close(TRUE); } +void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) +{ + m_lbox->Clear(); +} + void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) { - wxMessageBox(_T("Exec sample\n© 2000 Vadim Zeitlin"), + wxMessageBox(_T("Exec wxWindows Sample\n© 2000-2002 Vadim Zeitlin"), _T("About Exec"), wxOK | wxICON_INFORMATION, this); } +void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) +{ + long pid = wxGetNumberFromUser(_T("Please specify the process to kill"), + _T("Enter PID:"), + _T("Exec question"), + m_pidLast, + // we need the full unsigned int range + -INT_MAX, INT_MAX, + this); + if ( pid == -1 ) + { + // cancelled + return; + } + + static const wxString signalNames[] = + { + _T("Just test (SIGNONE)"), + _T("Hangup (SIGHUP)"), + _T("Interrupt (SIGINT)"), + _T("Quit (SIGQUIT)"), + _T("Illegal instruction (SIGILL)"), + _T("Trap (SIGTRAP)"), + _T("Abort (SIGABRT)"), + _T("Emulated trap (SIGEMT)"), + _T("FP exception (SIGFPE)"), + _T("Kill (SIGKILL)"), + _T("Bus (SIGBUS)"), + _T("Segment violation (SIGSEGV)"), + _T("System (SIGSYS)"), + _T("Broken pipe (SIGPIPE)"), + _T("Alarm (SIGALRM)"), + _T("Terminate (SIGTERM)"), + }; + + int sig = wxGetSingleChoiceIndex(_T("How to kill the process?"), + _T("Exec question"), + WXSIZEOF(signalNames), signalNames, + this); + switch ( sig ) + { + default: + wxFAIL_MSG( _T("unexpected return value") ); + // fall through + + case -1: + // cancelled + return; + + case wxSIGNONE: + case wxSIGHUP: + case wxSIGINT: + case wxSIGQUIT: + case wxSIGILL: + case wxSIGTRAP: + case wxSIGABRT: + case wxSIGEMT: + case wxSIGFPE: + case wxSIGKILL: + case wxSIGBUS: + case wxSIGSEGV: + case wxSIGSYS: + case wxSIGPIPE: + case wxSIGALRM: + case wxSIGTERM: + break; + } + + if ( sig == 0 ) + { + if ( wxProcess::Exists(pid) ) + wxLogStatus(_T("Process %ld is running."), pid); + else + wxLogStatus(_T("No process with pid = %ld."), pid); + } + else // not SIGNONE + { + wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig); + if ( rc == wxKILL_OK ) + { + wxLogStatus(_T("Process %ld killed with signal %d."), pid, sig); + } + else + { + static const wxChar *errorText[] = + { + _T(""), // no error + _T("signal not supported"), + _T("permission denied"), + _T("no such process"), + _T("unspecified error"), + }; + + wxLogStatus(_T("Failed to kill process %ld with signal %d: %s"), + pid, sig, errorText[rc]); + } + } +} + +// ---------------------------------------------------------------------------- +// event handlers: exec menu +// ---------------------------------------------------------------------------- + +void MyFrame::DoAsyncExec(const wxString& cmd) +{ + wxProcess *process = new MyProcess(this, cmd); + m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process); + if ( !m_pidLast ) + { + wxLogError( _T("Execution of '%s' failed."), cmd.c_str() ); + + delete process; + } + else + { + wxLogStatus( _T("Process %ld (%s) launched."), + m_pidLast, cmd.c_str() ); + + m_cmdLast = cmd; + } +} + void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event)) { wxString cmd = wxGetTextFromUser(_T("Enter the command: "), @@ -252,9 +612,13 @@ void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event)) if ( !cmd ) return; - int code = wxExecute(cmd, TRUE /* sync */); + wxLogStatus( _T("'%s' is running please wait..."), cmd.c_str() ); + + int code = wxExecute(cmd, wxEXEC_SYNC); + wxLogStatus(_T("Process '%s' terminated with exit code %d."), - cmd.c_str(), code); + cmd.c_str(), code); + m_cmdLast = cmd; } @@ -267,21 +631,86 @@ void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event)) if ( !cmd ) return; - wxProcess *process = new MyProcess(this, cmd); - if ( !wxExecute(cmd, FALSE /* async */, process) ) + DoAsyncExec(cmd); +} + +void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event)) +{ + wxString cmd = wxGetTextFromUser(_T("Enter the command: "), + DIALOG_TITLE, + m_cmdLast); + + if ( !cmd ) + return; + + int code = wxShell(cmd); + wxLogStatus(_T("Shell command '%s' terminated with exit code %d."), + cmd.c_str(), code); + m_cmdLast = cmd; +} + +void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) +{ + wxString cmd = wxGetTextFromUser(_T("Enter the command: "), + DIALOG_TITLE, + m_cmdLast); + + if ( !cmd ) + return; + + bool sync; + switch ( wxMessageBox(_T("Execute it synchronously?"), + _T("Exec question"), + wxYES_NO | wxCANCEL | wxICON_QUESTION, this) ) { - wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); + case wxYES: + sync = TRUE; + break; - delete process; + case wxNO: + sync = FALSE; + break; + + default: + return; } - else + + if ( sync ) { - m_cmdLast = cmd; + wxArrayString output, errors; + int code = wxExecute(cmd, output, errors); + wxLogStatus(_T("command '%s' terminated with exit code %d."), + cmd.c_str(), code); + + if ( code != -1 ) + { + ShowOutput(cmd, output, _T("Output")); + ShowOutput(cmd, errors, _T("Errors")); + } } + else // async exec + { + MyPipedProcess *process = new MyPipedProcess(this, cmd); + if ( !wxExecute(cmd, wxEXEC_ASYNC, process) ) + { + wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); + + delete process; + } + else + { + AddAsyncProcess(process); + } + } + + m_cmdLast = cmd; } -void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event)) +void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event)) { + if ( !m_cmdLast ) + m_cmdLast = _T("tr [a-z] [A-Z]"); + wxString cmd = wxGetTextFromUser(_T("Enter the command: "), DIALOG_TITLE, m_cmdLast); @@ -289,52 +718,225 @@ void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event)) if ( !cmd ) return; - int code = wxShell(cmd); - wxLogStatus(_T("Shell command '%s' terminated with exit code %d."), - cmd.c_str(), code); + wxString input = wxGetTextFromUser(_T("Enter the string to send to it: "), + DIALOG_TITLE); + if ( !input ) + return; + + // always execute the filter asynchronously + MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input); + long pid = wxExecute(cmd, wxEXEC_ASYNC, process); + if ( pid ) + { + wxLogStatus( _T("Process %ld (%s) launched."), pid, cmd.c_str() ); + + AddAsyncProcess(process); + } + else + { + wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); + + delete process; + } + m_cmdLast = cmd; } -void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event)) +void MyFrame::OnPOpen(wxCommandEvent& event) { + wxString cmd = wxGetTextFromUser(_T("Enter the command to launch: "), + DIALOG_TITLE, + m_cmdLast); + if ( cmd.empty() ) + return; + + wxProcess *process = wxProcess::Open(cmd); + if ( !process ) + { + wxLogError(_T("Failed to launch the command.")); + return; + } + + wxOutputStream *out = process->GetOutputStream(); + if ( !out ) + { + wxLogError(_T("Failed to connect to child stdin")); + return; + } + + wxInputStream *in = process->GetInputStream(); + if ( !in ) + { + wxLogError(_T("Failed to connect to child stdout")); + return; + } + + new MyPipeFrame(this, cmd, process); +} + +void MyFrame::OnFileExec(wxCommandEvent& event) +{ + static wxString s_filename; + + wxString filename = wxLoadFileSelector(_T(""), _T(""), s_filename); + if ( !filename ) + return; + + s_filename = filename; + + wxString ext = filename.AfterFirst(_T('.')); + wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext); + if ( !ft ) + { + wxLogError(_T("Impossible to determine the file type for extension '%s'"), + ext.c_str()); + return; + } + + wxString cmd; + bool ok = ft->GetOpenCommand(&cmd, + wxFileType::MessageParameters(filename, _T(""))); + delete ft; + if ( !ok ) + { + wxLogError(_T("Impossible to find out how to open files of extension '%s'"), + ext.c_str()); + return; + } + + DoAsyncExec(cmd); +} + +// ---------------------------------------------------------------------------- +// DDE stuff +// ---------------------------------------------------------------------------- + #ifdef __WINDOWS__ + +bool MyFrame::GetDDEServer() +{ wxString server = wxGetTextFromUser(_T("Server to connect to:"), - DIALOG_TITLE, _T("IExplore")); + DIALOG_TITLE, m_server); if ( !server ) - return; + return FALSE; - wxString topic = wxGetTextFromUser(_T("DDE topic:"), - DIALOG_TITLE, _T("WWW_OpenURL")); + m_server = server; + + wxString topic = wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE, m_topic); if ( !topic ) - return; + return FALSE; - wxString cmd = wxGetTextFromUser(_T("DDE command:"), - DIALOG_TITLE, - _T("\"file:F:\\wxWindows\\samples\\" - "image\\horse.gif\",,-1,,,,,")); + m_topic = topic; + + wxString cmd = wxGetTextFromUser(_T("DDE command:"), DIALOG_TITLE, m_cmdDde); if ( !cmd ) + return FALSE; + + m_cmdDde = cmd; + + return TRUE; +} + +void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event)) +{ + if ( !GetDDEServer() ) return; wxDDEClient client; - wxConnectionBase *conn = client.MakeConnection("", server, topic); + wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic); if ( !conn ) { wxLogError(_T("Failed to connect to the DDE server '%s'."), - server.c_str()); + m_server.c_str()); } else { - if ( !conn->Execute(cmd) ) + if ( !conn->Execute(m_cmdDde) ) { wxLogError(_T("Failed to execute command '%s' via DDE."), - cmd.c_str()); + m_cmdDde.c_str()); } else { wxLogStatus(_T("Successfully executed DDE command")); } } +} + +void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event)) +{ + if ( !GetDDEServer() ) + return; + + wxDDEClient client; + wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic); + if ( !conn ) + { + wxLogError(_T("Failed to connect to the DDE server '%s'."), + m_server.c_str()); + } + else + { + if ( !conn->Request(m_cmdDde) ) + { + wxLogError(_T("Failed to send request '%s' via DDE."), + m_cmdDde.c_str()); + } + else + { + wxLogStatus(_T("Successfully sent DDE request.")); + } + } +} + #endif // __WINDOWS__ + +// ---------------------------------------------------------------------------- +// various helpers +// ---------------------------------------------------------------------------- + +// input polling +void MyFrame::OnIdle(wxIdleEvent& event) +{ + size_t count = m_running.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + if ( m_running[n]->HasInput() ) + { + event.RequestMore(); + } + } +} + +void MyFrame::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + wxWakeUpIdle(); +} + +void MyFrame::OnProcessTerminated(MyPipedProcess *process) +{ + RemoveAsyncProcess(process); +} + + +void MyFrame::ShowOutput(const wxString& cmd, + const wxArrayString& output, + const wxString& title) +{ + size_t count = output.GetCount(); + if ( !count ) + return; + + m_lbox->Append(wxString::Format(_T("--- %s of '%s' ---"), + title.c_str(), cmd.c_str())); + + for ( size_t n = 0; n < count; n++ ) + { + m_lbox->Append(output[n]); + } + + m_lbox->Append(wxString::Format(_T("--- End of %s ---"), + title.Lower().c_str())); } // ---------------------------------------------------------------------------- @@ -349,3 +951,153 @@ void MyProcess::OnTerminate(int pid, int status) // we're not needed any more delete this; } + +// ---------------------------------------------------------------------------- +// MyPipedProcess +// ---------------------------------------------------------------------------- + +bool MyPipedProcess::HasInput() +{ + bool hasInput = FALSE; + + if ( IsInputAvailable() ) + { + wxTextInputStream tis(*GetInputStream()); + + // this assumes that the output is always line buffered + wxString msg; + msg << m_cmd << _T(" (stdout): ") << tis.ReadLine(); + + m_parent->GetLogListBox()->Append(msg); + + hasInput = TRUE; + } + + if ( IsErrorAvailable() ) + { + wxTextInputStream tis(*GetErrorStream()); + + // this assumes that the output is always line buffered + wxString msg; + msg << m_cmd << _T(" (stderr): ") << tis.ReadLine(); + + m_parent->GetLogListBox()->Append(msg); + + hasInput = TRUE; + } + + return hasInput; +} + +void MyPipedProcess::OnTerminate(int pid, int status) +{ + // show the rest of the output + while ( HasInput() ) + ; + + m_parent->OnProcessTerminated(this); + + MyProcess::OnTerminate(pid, status); +} + +// ---------------------------------------------------------------------------- +// MyPipedProcess2 +// ---------------------------------------------------------------------------- + +bool MyPipedProcess2::HasInput() +{ + if ( !!m_input ) + { + wxTextOutputStream os(*GetOutputStream()); + os.WriteString(m_input); + + CloseOutput(); + m_input.clear(); + + // call us once again - may be we'll have output + return TRUE; + } + + return MyPipedProcess::HasInput(); +} + +// ============================================================================ +// MyPipeFrame implementation +// ============================================================================ + +MyPipeFrame::MyPipeFrame(wxFrame *parent, + const wxString& cmd, + wxProcess *process) + : wxFrame(parent, -1, cmd), + m_process(process), + // in a real program we'd check that the streams are !NULL here + m_in(*process->GetInputStream()), + m_out(*process->GetOutputStream()) +{ + m_process->SetNextHandler(this); + + wxPanel *panel = new wxPanel(this, -1); + + m_textIn = new wxTextCtrl(panel, -1, _T(""), + wxDefaultPosition, wxDefaultSize, + wxTE_PROCESS_ENTER); + m_textOut = new wxTextCtrl(panel, -1, _T("")); + m_textOut->SetEditable(FALSE); + + wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL); + sizerTop->Add(m_textIn, 0, wxGROW | wxALL, 5); + + wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL); + sizerBtns->Add(new wxButton(panel, Exec_Btn_Send, _T("&Send")), 0, + wxALL, 10); + sizerBtns->Add(new wxButton(panel, Exec_Btn_Get, _T("&Get")), 0, + wxALL, 10); + + sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5); + sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5); + + panel->SetSizer(sizerTop); + sizerTop->Fit(this); + + Show(); +} + +void MyPipeFrame::DoGet() +{ + // we don't have any way to be notified when any input appears on the + // stream so we have to poll it :-( + // + // NB: this really must be done because otherwise the other program might + // not have enough time to receive or process our data and we'd read + // an empty string + while ( !m_process->IsInputAvailable() && m_process->IsInputOpened() ) + ; + + m_textOut->SetValue(m_in.ReadLine()); +} + +void MyPipeFrame::OnClose(wxCloseEvent& event) +{ + if ( m_process ) + { + // we're not interested in getting the process termination notification + // if we are closing it ourselves + wxProcess *process = m_process; + m_process = NULL; + process->SetNextHandler(NULL); + + process->CloseOutput(); + } + + event.Skip(); +} + +void MyPipeFrame::OnProcessTerm(wxProcessEvent& event) +{ + delete m_process; + m_process = NULL; + + wxLogWarning(_T("The other process has terminated, closing")); + + Close(); +}