X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/50567b69d60faa5a3575a077bb4f98530813c80d..cf6fec73d7f69bb86cb3e6165a41778609c47fd4:/samples/exec/exec.cpp diff --git a/samples/exec/exec.cpp b/samples/exec/exec.cpp index 1634f47fb7..13e9c0003d 100644 --- a/samples/exec/exec.cpp +++ b/samples/exec/exec.cpp @@ -17,11 +17,6 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ - #pragma implementation "exec.cpp" - #pragma interface "exec.cpp" -#endif - // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" @@ -30,20 +25,33 @@ #endif // for all others, include the necessary headers (this file is usually all you -// need because it includes almost all "standard" wxWindows headers +// need because it includes almost all "standard" wxWidgets 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/listbox.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/numdlg.h" +#include "wx/ffile.h" #include "wx/process.h" @@ -54,7 +62,7 @@ #endif // __WINDOWS__ // ---------------------------------------------------------------------------- -// private classes +// the usual application and main frame classes // ---------------------------------------------------------------------------- // Define a new application type, each program should derive a class from wxApp @@ -72,7 +80,7 @@ public: // Define an array of process pointers used by MyFrame class MyPipedProcess; -WX_DEFINE_ARRAY(MyPipedProcess *, MyProcessesArray); +WX_DEFINE_ARRAY_PTR(MyPipedProcess *, MyProcessesArray); // Define a new frame type: this is going to be our main frame class MyFrame : public wxFrame @@ -94,11 +102,14 @@ public: 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 @@ -112,8 +123,33 @@ private: 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 - int m_pidLast; + long m_pidLast; // last command we executed wxString m_cmdLast; @@ -134,10 +170,71 @@ private: MyProcessesArray m_running; - // any class wishing to process wxWindows events must use this macro + // the idle event wake up timer + wxTimer m_timerIdleWakeUp; + + // any class wishing to process wxWidgets 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& WXUNUSED(event)) { DoSend(); } + void OnBtnSend(wxCommandEvent& WXUNUSED(event)) { DoSend(); } + void OnBtnSendFile(wxCommandEvent& WXUNUSED(event)); + void OnBtnGet(wxCommandEvent& WXUNUSED(event)) { DoGet(); } + void OnBtnClose(wxCommandEvent& WXUNUSED(event)) { DoClose(); } + + void OnClose(wxCloseEvent& event); + + void OnProcessTerm(wxProcessEvent& event); + + void DoSend() + { + wxString s(m_textOut->GetValue()); + s += _T('\n'); + m_out.Write(s.c_str(), s.length()); + m_textOut->Clear(); + + DoGet(); + } + + void DoGet(); + void DoClose(); + +private: + void DoGetFromStream(wxTextCtrl *text, wxInputStream& in); + void DisableInput(); + void DisableOutput(); + + + wxProcess *m_process; + + wxOutputStream &m_out; + wxInputStream &m_in, + &m_err; + + wxTextCtrl *m_textOut, + *m_textIn, + *m_textErr; + + DECLARE_EVENT_TABLE() +}; + +// ---------------------------------------------------------------------------- +// wxProcess-derived classes +// ---------------------------------------------------------------------------- + // This is the handler for process termination events class MyProcess : public wxProcess { @@ -203,21 +300,28 @@ enum Exec_SyncExec = 200, Exec_AsyncExec, Exec_Shell, + Exec_POpen, Exec_OpenFile, Exec_DDEExec, Exec_DDERequest, Exec_Redirect, Exec_Pipe, - Exec_About = 300 + Exec_About = 300, + + // control ids + Exec_Btn_Send = 1000, + Exec_Btn_SendFile, + Exec_Btn_Get, + Exec_Btn_Close }; static const wxChar *DIALOG_TITLE = _T("Exec sample"); // ---------------------------------------------------------------------------- -// event tables and other macros for wxWindows +// event tables and other macros for wxWidgets // ---------------------------------------------------------------------------- -// the event tables connect the wxWindows events with the functions (event +// the event tables connect the wxWidgets events with the functions (event // handlers) which process them. It can be also done at run-time, but for the // simple menu events like this the static method is much simpler. BEGIN_EVENT_TABLE(MyFrame, wxFrame) @@ -231,17 +335,36 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame) 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(wxID_ANY, MyFrame::OnTimer) END_EVENT_TABLE() -// Create a new application object: this macro will allow wxWindows to create +BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame) + EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend) + EVT_BUTTON(Exec_Btn_SendFile, MyPipeFrame::OnBtnSendFile) + EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet) + EVT_BUTTON(Exec_Btn_Close, MyPipeFrame::OnBtnClose) + + EVT_TEXT_ENTER(wxID_ANY, MyPipeFrame::OnTextEnter) + + EVT_CLOSE(MyPipeFrame::OnClose) + + EVT_END_PROCESS(wxID_ANY, MyPipeFrame::OnProcessTerm) +END_EVENT_TABLE() + +// Create a new application object: this macro will allow wxWidgets to create // the application object during program execution (it's better than using a // static object for many reasons) and also declares the accessor function // wxGetApp() which will return the reference of the right type (i.e. MyApp and @@ -260,26 +383,31 @@ IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { // Create the main application window - MyFrame *frame = new MyFrame(_T("Exec wxWindows sample"), + MyFrame *frame = new MyFrame(_T("Exec wxWidgets sample"), wxDefaultPosition, wxSize(500, 140)); // Show it and tell the application that it's our main window - frame->Show(TRUE); + frame->Show(true); SetTopWindow(frame); // success: wxApp::OnRun() will be called which will enter the main message - // loop and the application will run. If we returned FALSE here, the + // loop and the application will run. If we returned false here, the // application would exit immediately. - return TRUE; + return true; } // ---------------------------------------------------------------------------- // main frame // ---------------------------------------------------------------------------- +#ifdef __VISUALC__ +#pragma warning(disable: 4355) // this used in base member initializer list +#endif + // frame constructor MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) - : wxFrame((wxFrame *)NULL, -1, title, pos, size) + : wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size), + m_timerIdleWakeUp(this) { m_pidLast = 0; @@ -290,7 +418,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) #endif // create a menu bar - wxMenu *menuFile = new wxMenu(_T(""), wxMENU_TEAROFF); + wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF); menuFile->Append(Exec_Kill, _T("&Kill process...\tCtrl-K"), _T("Kill a process by PID")); menuFile->AppendSeparator(); @@ -309,8 +437,10 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) 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...\tCtrl-P"), + 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"), @@ -321,7 +451,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) execMenu->Append(Exec_DDERequest, _T("Send DDE &request...\tCtrl-R")); #endif - wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF); + wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF); helpMenu->Append(Exec_About, _T("&About...\tF1"), _T("Show about dialog")); // now append the freshly created menu to the menu bar... @@ -334,7 +464,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) SetMenuBar(menuBar); // create the listbox in which we will show misc messages as they come - m_lbox = new wxListBox(this, -1); + m_lbox = new wxListBox(this, wxID_ANY); wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); if ( font.Ok() ) @@ -343,7 +473,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) #if wxUSE_STATUSBAR // create a status bar just for fun (by default with 1 pane only) CreateStatusBar(); - SetStatusText(_T("Welcome to wxWindows exec sample!")); + SetStatusText(_T("Welcome to wxWidgets exec sample!")); #endif // wxUSE_STATUSBAR } @@ -353,8 +483,8 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { - // TRUE is to force the frame to close - Close(TRUE); + // true is to force the frame to close + Close(true); } void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) @@ -364,7 +494,7 @@ void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) { - wxMessageBox(_T("Exec wxWindows Sample\n© 2000-2001 Vadim Zeitlin"), + wxMessageBox(_T("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"), _T("About Exec"), wxOK | wxICON_INFORMATION, this); } @@ -374,7 +504,8 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) _T("Enter PID:"), _T("Exec question"), m_pidLast, - 1, INT_MAX, + // we need the full unsigned int range + -INT_MAX, INT_MAX, this); if ( pid == -1 ) { @@ -438,16 +569,16 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) if ( sig == 0 ) { if ( wxProcess::Exists(pid) ) - wxLogStatus(_T("Process %d is running."), pid); + wxLogStatus(_T("Process %ld is running."), pid); else - wxLogStatus(_T("No process with pid = %d."), pid); + 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 %d killed with signal %d."), pid, sig); + wxLogStatus(_T("Process %ld killed with signal %d."), pid, sig); } else { @@ -460,7 +591,7 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) _T("unspecified error"), }; - wxLogStatus(_T("Failed to kill process %d with signal %d: %s"), + wxLogStatus(_T("Failed to kill process %ld with signal %d: %s"), pid, sig, errorText[rc]); } } @@ -473,16 +604,17 @@ void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event)) void MyFrame::DoAsyncExec(const wxString& cmd) { wxProcess *process = new MyProcess(this, cmd); - m_pidLast = wxExecute(cmd, FALSE /* async */, process); + m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process); if ( !m_pidLast ) { - wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); + wxLogError( _T("Execution of '%s' failed."), cmd.c_str() ); delete process; } else { - wxLogStatus(_T("Process %ld (%s) launched."), m_pidLast, cmd.c_str()); + wxLogStatus( _T("Process %ld (%s) launched."), + m_pidLast, cmd.c_str() ); m_cmdLast = cmd; } @@ -497,12 +629,13 @@ void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event)) if ( !cmd ) return; - wxLogStatus(_T("'%s' is running please wait..."), cmd.c_str()); + wxLogStatus( _T("'%s' is running please wait..."), cmd.c_str() ); - int code = wxExecute(cmd, TRUE /* sync */); + 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; } @@ -548,11 +681,11 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) wxYES_NO | wxCANCEL | wxICON_QUESTION, this) ) { case wxYES: - sync = TRUE; + sync = true; break; case wxNO: - sync = FALSE; + sync = false; break; default: @@ -575,7 +708,7 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) else // async exec { MyPipedProcess *process = new MyPipedProcess(this, cmd); - if ( !wxExecute(cmd, FALSE /* async */, process) ) + if ( !wxExecute(cmd, wxEXEC_ASYNC, process) ) { wxLogError(_T("Execution of '%s' failed."), cmd.c_str()); @@ -583,7 +716,7 @@ void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event)) } else { - m_running.Add(process); + AddAsyncProcess(process); } } @@ -609,12 +742,12 @@ void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event)) // always execute the filter asynchronously MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input); - int pid = wxExecute(cmd, FALSE /* async */, process); + long pid = wxExecute(cmd, wxEXEC_ASYNC, process); if ( pid ) { - wxLogStatus(_T("Process %ld (%s) launched."), pid, cmd.c_str()); + wxLogStatus( _T("Process %ld (%s) launched."), pid, cmd.c_str() ); - m_running.Add(process); + AddAsyncProcess(process); } else { @@ -626,12 +759,49 @@ void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event)) m_cmdLast = cmd; } -void MyFrame::OnFileExec(wxCommandEvent& event) +void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(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& WXUNUSED(event)) { static wxString s_filename; - wxString filename = wxLoadFileSelector(_T("file"), _T(""), s_filename); - if ( !filename ) + wxString filename; + +#if wxUSE_FILEDLG + filename = wxLoadFileSelector(wxEmptyString, wxEmptyString, s_filename); +#endif // wxUSE_FILEDLG + + if ( filename.empty() ) return; s_filename = filename; @@ -647,7 +817,7 @@ void MyFrame::OnFileExec(wxCommandEvent& event) wxString cmd; bool ok = ft->GetOpenCommand(&cmd, - wxFileType::MessageParameters(filename, _T(""))); + wxFileType::MessageParameters(filename)); delete ft; if ( !ok ) { @@ -670,23 +840,23 @@ bool MyFrame::GetDDEServer() wxString server = wxGetTextFromUser(_T("Server to connect to:"), DIALOG_TITLE, m_server); if ( !server ) - return FALSE; + return false; m_server = server; wxString topic = wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE, m_topic); if ( !topic ) - return FALSE; + return false; m_topic = topic; wxString cmd = wxGetTextFromUser(_T("DDE command:"), DIALOG_TITLE, m_cmdDde); if ( !cmd ) - return FALSE; + return false; m_cmdDde = cmd; - return TRUE; + return true; } void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event)) @@ -695,7 +865,7 @@ void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event)) return; wxDDEClient client; - wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic); + wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic); if ( !conn ) { wxLogError(_T("Failed to connect to the DDE server '%s'."), @@ -721,7 +891,7 @@ void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event)) return; wxDDEClient client; - wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic); + wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic); if ( !conn ) { wxLogError(_T("Failed to connect to the DDE server '%s'."), @@ -760,9 +930,14 @@ void MyFrame::OnIdle(wxIdleEvent& event) } } +void MyFrame::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + wxWakeUpIdle(); +} + void MyFrame::OnProcessTerminated(MyPipedProcess *process) { - m_running.Remove(process); + RemoveAsyncProcess(process); } @@ -782,7 +957,8 @@ void MyFrame::ShowOutput(const wxString& cmd, m_lbox->Append(output[n]); } - m_lbox->Append(_T("--- End of output ---")); + m_lbox->Append(wxString::Format(_T("--- End of %s ---"), + title.Lower().c_str())); } // ---------------------------------------------------------------------------- @@ -804,12 +980,11 @@ void MyProcess::OnTerminate(int pid, int status) bool MyPipedProcess::HasInput() { - bool hasInput = FALSE; + bool hasInput = false; - wxInputStream& is = *GetInputStream(); - if ( !is.Eof() ) + if ( IsInputAvailable() ) { - wxTextInputStream tis(is); + wxTextInputStream tis(*GetInputStream()); // this assumes that the output is always line buffered wxString msg; @@ -817,13 +992,12 @@ bool MyPipedProcess::HasInput() m_parent->GetLogListBox()->Append(msg); - hasInput = TRUE; + hasInput = true; } - wxInputStream& es = *GetErrorStream(); - if ( !es.Eof() ) + if ( IsErrorAvailable() ) { - wxTextInputStream tis(es); + wxTextInputStream tis(*GetErrorStream()); // this assumes that the output is always line buffered wxString msg; @@ -831,7 +1005,7 @@ bool MyPipedProcess::HasInput() m_parent->GetLogListBox()->Append(msg); - hasInput = TRUE; + hasInput = true; } return hasInput; @@ -854,7 +1028,7 @@ void MyPipedProcess::OnTerminate(int pid, int status) bool MyPipedProcess2::HasInput() { - if ( !!m_input ) + if ( !m_input.empty() ) { wxTextOutputStream os(*GetOutputStream()); os.WriteString(m_input); @@ -863,8 +1037,162 @@ bool MyPipedProcess2::HasInput() m_input.clear(); // call us once again - may be we'll have output - return TRUE; + return true; } return MyPipedProcess::HasInput(); } + +// ============================================================================ +// MyPipeFrame implementation +// ============================================================================ + +MyPipeFrame::MyPipeFrame(wxFrame *parent, + const wxString& cmd, + wxProcess *process) + : wxFrame(parent, wxID_ANY, cmd), + m_process(process), + // in a real program we'd check that the streams are !NULL here + m_out(*process->GetOutputStream()), + m_in(*process->GetInputStream()), + m_err(*process->GetErrorStream()) +{ + m_process->SetNextHandler(this); + + wxPanel *panel = new wxPanel(this, wxID_ANY); + + m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + wxTE_PROCESS_ENTER); + m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + wxTE_MULTILINE | wxTE_RICH); + m_textIn->SetEditable(false); + m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + wxTE_MULTILINE | wxTE_RICH); + m_textErr->SetEditable(false); + + wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL); + sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5); + + wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL); + sizerBtns-> + Add(new wxButton(panel, Exec_Btn_Send, _T("&Send")), 0, wxALL, 5); + sizerBtns-> + Add(new wxButton(panel, Exec_Btn_SendFile, _T("&File...")), 0, wxALL, 5); + sizerBtns-> + Add(new wxButton(panel, Exec_Btn_Get, _T("&Get")), 0, wxALL, 5); + sizerBtns-> + Add(new wxButton(panel, Exec_Btn_Close, _T("&Close")), 0, wxALL, 5); + + sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5); + sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5); + sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5); + + panel->SetSizer(sizerTop); + sizerTop->Fit(this); + + Show(); +} + +void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event)) +{ +#if wxUSE_FILEDLG + wxFileDialog filedlg(this, _T("Select file to send")); + if ( filedlg.ShowModal() != wxID_OK ) + return; + + wxFFile file(filedlg.GetFilename(), _T("r")); + wxString data; + if ( !file.IsOpened() || !file.ReadAll(&data) ) + return; + + // can't write the entire string at once, this risk overflowing the pipe + // and we would dead lock + size_t len = data.length(); + const wxChar *pc = data.c_str(); + while ( len ) + { + const size_t CHUNK_SIZE = 4096; + m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len); + + // note that not all data could have been written as we don't block on + // the write end of the pipe + const size_t lenChunk = m_out.LastWrite(); + + pc += lenChunk; + len -= lenChunk; + + DoGet(); + } +#endif // wxUSE_FILEDLG +} + +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 :-( + DoGetFromStream(m_textIn, m_in); + DoGetFromStream(m_textErr, m_err); +} + +void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in) +{ + while ( in.CanRead() ) + { + wxChar buffer[4096]; + buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = _T('\0'); + + text->AppendText(buffer); + } +} + +void MyPipeFrame::DoClose() +{ + m_process->CloseOutput(); + + DisableInput(); +} + +void MyPipeFrame::DisableInput() +{ + m_textOut->SetEditable(false); + FindWindow(Exec_Btn_Send)->Disable(); + FindWindow(Exec_Btn_SendFile)->Disable(); + FindWindow(Exec_Btn_Close)->Disable(); +} + +void MyPipeFrame::DisableOutput() +{ + FindWindow(Exec_Btn_Get)->Disable(); +} + +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& WXUNUSED(event)) +{ + DoGet(); + + delete m_process; + m_process = NULL; + + wxLogWarning(_T("The other process has terminated, closing")); + + DisableInput(); + DisableOutput(); +}