fixed the async process IO polling code
[wxWidgets.git] / samples / exec / exec.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: exec.cpp
3 // Purpose: exec sample demonstrates wxExecute and related functions
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 15.01.00
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "exec.cpp"
22 #pragma interface "exec.cpp"
23 #endif
24
25 // For compilers that support precompilation, includes "wx/wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 // for all others, include the necessary headers (this file is usually all you
33 // need because it includes almost all "standard" wxWindows headers
34 #ifndef WX_PRECOMP
35 #include "wx/app.h"
36 #include "wx/frame.h"
37 #include "wx/panel.h"
38
39 #include "wx/timer.h"
40
41 #include "wx/utils.h"
42 #include "wx/menu.h"
43
44 #include "wx/msgdlg.h"
45 #include "wx/textdlg.h"
46 #include "wx/filedlg.h"
47 #include "wx/choicdlg.h"
48
49 #include "wx/button.h"
50 #include "wx/textctrl.h"
51 #include "wx/listbox.h"
52
53 #include "wx/sizer.h"
54 #endif
55
56 #include "wx/txtstrm.h"
57
58 #include "wx/process.h"
59
60 #include "wx/mimetype.h"
61
62 #ifdef __WINDOWS__
63 #include "wx/dde.h"
64 #endif // __WINDOWS__
65
66 // ----------------------------------------------------------------------------
67 // the usual application and main frame classes
68 // ----------------------------------------------------------------------------
69
70 // Define a new application type, each program should derive a class from wxApp
71 class MyApp : public wxApp
72 {
73 public:
74 // override base class virtuals
75 // ----------------------------
76
77 // this one is called on application startup and is a good place for the app
78 // initialization (doing it here and not in the ctor allows to have an error
79 // return: if OnInit() returns false, the application terminates)
80 virtual bool OnInit();
81 };
82
83 // Define an array of process pointers used by MyFrame
84 class MyPipedProcess;
85 WX_DEFINE_ARRAY(MyPipedProcess *, MyProcessesArray);
86
87 // Define a new frame type: this is going to be our main frame
88 class MyFrame : public wxFrame
89 {
90 public:
91 // ctor(s)
92 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
93
94 // event handlers (these functions should _not_ be virtual)
95 void OnQuit(wxCommandEvent& event);
96
97 void OnKill(wxCommandEvent& event);
98
99 void OnClear(wxCommandEvent& event);
100
101 void OnSyncExec(wxCommandEvent& event);
102 void OnAsyncExec(wxCommandEvent& event);
103 void OnShell(wxCommandEvent& event);
104 void OnExecWithRedirect(wxCommandEvent& event);
105 void OnExecWithPipe(wxCommandEvent& event);
106
107 void OnPOpen(wxCommandEvent& event);
108
109 void OnFileExec(wxCommandEvent& event);
110
111 void OnAbout(wxCommandEvent& event);
112
113 // polling output of async processes
114 void OnTimer(wxTimerEvent& event);
115 void OnIdle(wxIdleEvent& event);
116
117 // for MyPipedProcess
118 void OnProcessTerminated(MyPipedProcess *process);
119 wxListBox *GetLogListBox() const { return m_lbox; }
120
121 private:
122 void ShowOutput(const wxString& cmd,
123 const wxArrayString& output,
124 const wxString& title);
125
126 void DoAsyncExec(const wxString& cmd);
127
128 void AddAsyncProcess(MyPipedProcess *process)
129 {
130 if ( m_running.IsEmpty() )
131 {
132 // we want to start getting the timer events to ensure that a
133 // steady stream of idle events comes in -- otherwise we
134 // wouldn't be able to poll the child process input
135 m_timerIdleWakeUp.Start(100);
136 }
137 //else: the timer is already running
138
139 m_running.Add(process);
140 }
141
142 void RemoveAsyncProcess(MyPipedProcess *process)
143 {
144 m_running.Remove(process);
145
146 if ( m_running.IsEmpty() )
147 {
148 // we don't need to get idle events all the time any more
149 m_timerIdleWakeUp.Stop();
150 }
151 }
152
153 // the PID of the last process we launched asynchronously
154 long m_pidLast;
155
156 // last command we executed
157 wxString m_cmdLast;
158
159 #ifdef __WINDOWS__
160 void OnDDEExec(wxCommandEvent& event);
161 void OnDDERequest(wxCommandEvent& event);
162
163 bool GetDDEServer();
164
165 // last params of a DDE transaction
166 wxString m_server,
167 m_topic,
168 m_cmdDde;
169 #endif // __WINDOWS__
170
171 wxListBox *m_lbox;
172
173 MyProcessesArray m_running;
174
175 // the idle event wake up timer
176 wxTimer m_timerIdleWakeUp;
177
178 // any class wishing to process wxWindows events must use this macro
179 DECLARE_EVENT_TABLE()
180 };
181
182 // ----------------------------------------------------------------------------
183 // MyPipeFrame: allows the user to communicate with the child process
184 // ----------------------------------------------------------------------------
185
186 class MyPipeFrame : public wxFrame
187 {
188 public:
189 MyPipeFrame(wxFrame *parent,
190 const wxString& cmd,
191 wxProcess *process);
192
193 protected:
194 void OnTextEnter(wxCommandEvent& event) { DoSend(); }
195 void OnBtnSend(wxCommandEvent& event) { DoSend(); }
196 void OnBtnGet(wxCommandEvent& event) { DoGet(); }
197
198 void OnClose(wxCloseEvent& event);
199
200 void OnProcessTerm(wxProcessEvent& event);
201
202 void DoSend()
203 {
204 m_out.WriteString(m_textIn->GetValue() + '\n');
205 m_textIn->Clear();
206
207 DoGet();
208 }
209
210 void DoGet();
211
212 private:
213 wxProcess *m_process;
214
215 wxTextInputStream m_in;
216 wxTextOutputStream m_out;
217
218 wxTextCtrl *m_textIn,
219 *m_textOut;
220
221 DECLARE_EVENT_TABLE()
222 };
223
224 // ----------------------------------------------------------------------------
225 // wxProcess-derived classes
226 // ----------------------------------------------------------------------------
227
228 // This is the handler for process termination events
229 class MyProcess : public wxProcess
230 {
231 public:
232 MyProcess(MyFrame *parent, const wxString& cmd)
233 : wxProcess(parent), m_cmd(cmd)
234 {
235 m_parent = parent;
236 }
237
238 // instead of overriding this virtual function we might as well process the
239 // event from it in the frame class - this might be more convenient in some
240 // cases
241 virtual void OnTerminate(int pid, int status);
242
243 protected:
244 MyFrame *m_parent;
245 wxString m_cmd;
246 };
247
248 // A specialization of MyProcess for redirecting the output
249 class MyPipedProcess : public MyProcess
250 {
251 public:
252 MyPipedProcess(MyFrame *parent, const wxString& cmd)
253 : MyProcess(parent, cmd)
254 {
255 Redirect();
256 }
257
258 virtual void OnTerminate(int pid, int status);
259
260 virtual bool HasInput();
261 };
262
263 // A version of MyPipedProcess which also sends input to the stdin of the
264 // child process
265 class MyPipedProcess2 : public MyPipedProcess
266 {
267 public:
268 MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
269 : MyPipedProcess(parent, cmd), m_input(input)
270 {
271 }
272
273 virtual bool HasInput();
274
275 private:
276 wxString m_input;
277 };
278
279 // ----------------------------------------------------------------------------
280 // constants
281 // ----------------------------------------------------------------------------
282
283 // IDs for the controls and the menu commands
284 enum
285 {
286 // menu items
287 Exec_Quit = 100,
288 Exec_Kill,
289 Exec_ClearLog,
290 Exec_SyncExec = 200,
291 Exec_AsyncExec,
292 Exec_Shell,
293 Exec_POpen,
294 Exec_OpenFile,
295 Exec_DDEExec,
296 Exec_DDERequest,
297 Exec_Redirect,
298 Exec_Pipe,
299 Exec_About = 300,
300
301 // control ids
302 Exec_Btn_Send = 1000,
303 Exec_Btn_Get
304 };
305
306 static const wxChar *DIALOG_TITLE = _T("Exec sample");
307
308 // ----------------------------------------------------------------------------
309 // event tables and other macros for wxWindows
310 // ----------------------------------------------------------------------------
311
312 // the event tables connect the wxWindows events with the functions (event
313 // handlers) which process them. It can be also done at run-time, but for the
314 // simple menu events like this the static method is much simpler.
315 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
316 EVT_MENU(Exec_Quit, MyFrame::OnQuit)
317 EVT_MENU(Exec_Kill, MyFrame::OnKill)
318 EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
319
320 EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
321 EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
322 EVT_MENU(Exec_Shell, MyFrame::OnShell)
323 EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
324 EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
325
326 EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
327
328 EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
329
330 #ifdef __WINDOWS__
331 EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
332 EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
333 #endif // __WINDOWS__
334
335 EVT_MENU(Exec_About, MyFrame::OnAbout)
336
337 EVT_IDLE(MyFrame::OnIdle)
338
339 EVT_TIMER(-1, MyFrame::OnTimer)
340 END_EVENT_TABLE()
341
342 BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
343 EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend)
344 EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet)
345
346 EVT_TEXT_ENTER(-1, MyPipeFrame::OnTextEnter)
347
348 EVT_CLOSE(MyPipeFrame::OnClose)
349
350 EVT_END_PROCESS(-1, MyPipeFrame::OnProcessTerm)
351 END_EVENT_TABLE()
352
353 // Create a new application object: this macro will allow wxWindows to create
354 // the application object during program execution (it's better than using a
355 // static object for many reasons) and also declares the accessor function
356 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
357 // not wxApp)
358 IMPLEMENT_APP(MyApp)
359
360 // ============================================================================
361 // implementation
362 // ============================================================================
363
364 // ----------------------------------------------------------------------------
365 // the application class
366 // ----------------------------------------------------------------------------
367
368 // `Main program' equivalent: the program execution "starts" here
369 bool MyApp::OnInit()
370 {
371 // Create the main application window
372 MyFrame *frame = new MyFrame(_T("Exec wxWindows sample"),
373 wxDefaultPosition, wxSize(500, 140));
374
375 // Show it and tell the application that it's our main window
376 frame->Show(TRUE);
377 SetTopWindow(frame);
378
379 // success: wxApp::OnRun() will be called which will enter the main message
380 // loop and the application will run. If we returned FALSE here, the
381 // application would exit immediately.
382 return TRUE;
383 }
384
385 // ----------------------------------------------------------------------------
386 // main frame
387 // ----------------------------------------------------------------------------
388
389 // frame constructor
390 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
391 : wxFrame((wxFrame *)NULL, -1, title, pos, size),
392 m_timerIdleWakeUp(this)
393 {
394 m_pidLast = 0;
395
396 #ifdef __WXMAC__
397 // we need this in order to allow the about menu relocation, since ABOUT is
398 // not the default id of the about menu
399 wxApp::s_macAboutMenuItemId = Exec_About;
400 #endif
401
402 // create a menu bar
403 wxMenu *menuFile = new wxMenu(_T(""), wxMENU_TEAROFF);
404 menuFile->Append(Exec_Kill, _T("&Kill process...\tCtrl-K"),
405 _T("Kill a process by PID"));
406 menuFile->AppendSeparator();
407 menuFile->Append(Exec_ClearLog, _T("&Clear log\tCtrl-C"),
408 _T("Clear the log window"));
409 menuFile->AppendSeparator();
410 menuFile->Append(Exec_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
411
412 wxMenu *execMenu = new wxMenu;
413 execMenu->Append(Exec_SyncExec, _T("Sync &execution...\tCtrl-E"),
414 _T("Launch a program and return when it terminates"));
415 execMenu->Append(Exec_AsyncExec, _T("&Async execution...\tCtrl-A"),
416 _T("Launch a program and return immediately"));
417 execMenu->Append(Exec_Shell, _T("Execute &shell command...\tCtrl-S"),
418 _T("Launch a shell and execute a command in it"));
419 execMenu->AppendSeparator();
420 execMenu->Append(Exec_Redirect, _T("Capture command &output...\tCtrl-O"),
421 _T("Launch a program and capture its output"));
422 execMenu->Append(Exec_Pipe, _T("&Pipe through command..."),
423 _T("Pipe a string through a filter"));
424 execMenu->Append(Exec_POpen, _T("&Open a pipe to a command...\tCtrl-P"),
425 _T("Open a pipe to and from another program"));
426
427 execMenu->AppendSeparator();
428 execMenu->Append(Exec_OpenFile, _T("Open &file...\tCtrl-F"),
429 _T("Launch the command to open this kind of files"));
430 #ifdef __WINDOWS__
431 execMenu->AppendSeparator();
432 execMenu->Append(Exec_DDEExec, _T("Execute command via &DDE...\tCtrl-D"));
433 execMenu->Append(Exec_DDERequest, _T("Send DDE &request...\tCtrl-R"));
434 #endif
435
436 wxMenu *helpMenu = new wxMenu(_T(""), wxMENU_TEAROFF);
437 helpMenu->Append(Exec_About, _T("&About...\tF1"), _T("Show about dialog"));
438
439 // now append the freshly created menu to the menu bar...
440 wxMenuBar *menuBar = new wxMenuBar();
441 menuBar->Append(menuFile, _T("&File"));
442 menuBar->Append(execMenu, _T("&Exec"));
443 menuBar->Append(helpMenu, _T("&Help"));
444
445 // ... and attach this menu bar to the frame
446 SetMenuBar(menuBar);
447
448 // create the listbox in which we will show misc messages as they come
449 m_lbox = new wxListBox(this, -1);
450 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
451 wxFONTWEIGHT_NORMAL);
452 if ( font.Ok() )
453 m_lbox->SetFont(font);
454
455 #if wxUSE_STATUSBAR
456 // create a status bar just for fun (by default with 1 pane only)
457 CreateStatusBar();
458 SetStatusText(_T("Welcome to wxWindows exec sample!"));
459 #endif // wxUSE_STATUSBAR
460 }
461
462 // ----------------------------------------------------------------------------
463 // event handlers: file and help menu
464 // ----------------------------------------------------------------------------
465
466 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
467 {
468 // TRUE is to force the frame to close
469 Close(TRUE);
470 }
471
472 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
473 {
474 m_lbox->Clear();
475 }
476
477 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
478 {
479 wxMessageBox(_T("Exec wxWindows Sample\n© 2000-2002 Vadim Zeitlin"),
480 _T("About Exec"), wxOK | wxICON_INFORMATION, this);
481 }
482
483 void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
484 {
485 long pid = wxGetNumberFromUser(_T("Please specify the process to kill"),
486 _T("Enter PID:"),
487 _T("Exec question"),
488 m_pidLast,
489 // we need the full unsigned int range
490 -INT_MAX, INT_MAX,
491 this);
492 if ( pid == -1 )
493 {
494 // cancelled
495 return;
496 }
497
498 static const wxString signalNames[] =
499 {
500 _T("Just test (SIGNONE)"),
501 _T("Hangup (SIGHUP)"),
502 _T("Interrupt (SIGINT)"),
503 _T("Quit (SIGQUIT)"),
504 _T("Illegal instruction (SIGILL)"),
505 _T("Trap (SIGTRAP)"),
506 _T("Abort (SIGABRT)"),
507 _T("Emulated trap (SIGEMT)"),
508 _T("FP exception (SIGFPE)"),
509 _T("Kill (SIGKILL)"),
510 _T("Bus (SIGBUS)"),
511 _T("Segment violation (SIGSEGV)"),
512 _T("System (SIGSYS)"),
513 _T("Broken pipe (SIGPIPE)"),
514 _T("Alarm (SIGALRM)"),
515 _T("Terminate (SIGTERM)"),
516 };
517
518 int sig = wxGetSingleChoiceIndex(_T("How to kill the process?"),
519 _T("Exec question"),
520 WXSIZEOF(signalNames), signalNames,
521 this);
522 switch ( sig )
523 {
524 default:
525 wxFAIL_MSG( _T("unexpected return value") );
526 // fall through
527
528 case -1:
529 // cancelled
530 return;
531
532 case wxSIGNONE:
533 case wxSIGHUP:
534 case wxSIGINT:
535 case wxSIGQUIT:
536 case wxSIGILL:
537 case wxSIGTRAP:
538 case wxSIGABRT:
539 case wxSIGEMT:
540 case wxSIGFPE:
541 case wxSIGKILL:
542 case wxSIGBUS:
543 case wxSIGSEGV:
544 case wxSIGSYS:
545 case wxSIGPIPE:
546 case wxSIGALRM:
547 case wxSIGTERM:
548 break;
549 }
550
551 if ( sig == 0 )
552 {
553 if ( wxProcess::Exists(pid) )
554 wxLogStatus(_T("Process %ld is running."), pid);
555 else
556 wxLogStatus(_T("No process with pid = %ld."), pid);
557 }
558 else // not SIGNONE
559 {
560 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
561 if ( rc == wxKILL_OK )
562 {
563 wxLogStatus(_T("Process %ld killed with signal %d."), pid, sig);
564 }
565 else
566 {
567 static const wxChar *errorText[] =
568 {
569 _T(""), // no error
570 _T("signal not supported"),
571 _T("permission denied"),
572 _T("no such process"),
573 _T("unspecified error"),
574 };
575
576 wxLogStatus(_T("Failed to kill process %ld with signal %d: %s"),
577 pid, sig, errorText[rc]);
578 }
579 }
580 }
581
582 // ----------------------------------------------------------------------------
583 // event handlers: exec menu
584 // ----------------------------------------------------------------------------
585
586 void MyFrame::DoAsyncExec(const wxString& cmd)
587 {
588 wxProcess *process = new MyProcess(this, cmd);
589 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
590 if ( !m_pidLast )
591 {
592 wxLogError( _T("Execution of '%s' failed."), cmd.c_str() );
593
594 delete process;
595 }
596 else
597 {
598 wxLogStatus( _T("Process %ld (%s) launched."),
599 m_pidLast, cmd.c_str() );
600
601 m_cmdLast = cmd;
602 }
603 }
604
605 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
606 {
607 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
608 DIALOG_TITLE,
609 m_cmdLast);
610
611 if ( !cmd )
612 return;
613
614 wxLogStatus( _T("'%s' is running please wait..."), cmd.c_str() );
615
616 int code = wxExecute(cmd, wxEXEC_SYNC);
617
618 wxLogStatus(_T("Process '%s' terminated with exit code %d."),
619 cmd.c_str(), code);
620
621 m_cmdLast = cmd;
622 }
623
624 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
625 {
626 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
627 DIALOG_TITLE,
628 m_cmdLast);
629
630 if ( !cmd )
631 return;
632
633 DoAsyncExec(cmd);
634 }
635
636 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
637 {
638 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
639 DIALOG_TITLE,
640 m_cmdLast);
641
642 if ( !cmd )
643 return;
644
645 int code = wxShell(cmd);
646 wxLogStatus(_T("Shell command '%s' terminated with exit code %d."),
647 cmd.c_str(), code);
648 m_cmdLast = cmd;
649 }
650
651 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
652 {
653 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
654 DIALOG_TITLE,
655 m_cmdLast);
656
657 if ( !cmd )
658 return;
659
660 bool sync;
661 switch ( wxMessageBox(_T("Execute it synchronously?"),
662 _T("Exec question"),
663 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
664 {
665 case wxYES:
666 sync = TRUE;
667 break;
668
669 case wxNO:
670 sync = FALSE;
671 break;
672
673 default:
674 return;
675 }
676
677 if ( sync )
678 {
679 wxArrayString output, errors;
680 int code = wxExecute(cmd, output, errors);
681 wxLogStatus(_T("command '%s' terminated with exit code %d."),
682 cmd.c_str(), code);
683
684 if ( code != -1 )
685 {
686 ShowOutput(cmd, output, _T("Output"));
687 ShowOutput(cmd, errors, _T("Errors"));
688 }
689 }
690 else // async exec
691 {
692 MyPipedProcess *process = new MyPipedProcess(this, cmd);
693 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
694 {
695 wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
696
697 delete process;
698 }
699 else
700 {
701 AddAsyncProcess(process);
702 }
703 }
704
705 m_cmdLast = cmd;
706 }
707
708 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
709 {
710 if ( !m_cmdLast )
711 m_cmdLast = _T("tr [a-z] [A-Z]");
712
713 wxString cmd = wxGetTextFromUser(_T("Enter the command: "),
714 DIALOG_TITLE,
715 m_cmdLast);
716
717 if ( !cmd )
718 return;
719
720 wxString input = wxGetTextFromUser(_T("Enter the string to send to it: "),
721 DIALOG_TITLE);
722 if ( !input )
723 return;
724
725 // always execute the filter asynchronously
726 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
727 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
728 if ( pid )
729 {
730 wxLogStatus( _T("Process %ld (%s) launched."), pid, cmd.c_str() );
731
732 AddAsyncProcess(process);
733 }
734 else
735 {
736 wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
737
738 delete process;
739 }
740
741 m_cmdLast = cmd;
742 }
743
744 void MyFrame::OnPOpen(wxCommandEvent& event)
745 {
746 wxString cmd = wxGetTextFromUser(_T("Enter the command to launch: "),
747 DIALOG_TITLE,
748 m_cmdLast);
749 if ( cmd.empty() )
750 return;
751
752 wxProcess *process = wxProcess::Open(cmd);
753 if ( !process )
754 {
755 wxLogError(_T("Failed to launch the command."));
756 return;
757 }
758
759 wxOutputStream *out = process->GetOutputStream();
760 if ( !out )
761 {
762 wxLogError(_T("Failed to connect to child stdin"));
763 return;
764 }
765
766 wxInputStream *in = process->GetInputStream();
767 if ( !in )
768 {
769 wxLogError(_T("Failed to connect to child stdout"));
770 return;
771 }
772
773 new MyPipeFrame(this, cmd, process);
774 }
775
776 void MyFrame::OnFileExec(wxCommandEvent& event)
777 {
778 static wxString s_filename;
779
780 wxString filename = wxLoadFileSelector(_T(""), _T(""), s_filename);
781 if ( !filename )
782 return;
783
784 s_filename = filename;
785
786 wxString ext = filename.AfterFirst(_T('.'));
787 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
788 if ( !ft )
789 {
790 wxLogError(_T("Impossible to determine the file type for extension '%s'"),
791 ext.c_str());
792 return;
793 }
794
795 wxString cmd;
796 bool ok = ft->GetOpenCommand(&cmd,
797 wxFileType::MessageParameters(filename, _T("")));
798 delete ft;
799 if ( !ok )
800 {
801 wxLogError(_T("Impossible to find out how to open files of extension '%s'"),
802 ext.c_str());
803 return;
804 }
805
806 DoAsyncExec(cmd);
807 }
808
809 // ----------------------------------------------------------------------------
810 // DDE stuff
811 // ----------------------------------------------------------------------------
812
813 #ifdef __WINDOWS__
814
815 bool MyFrame::GetDDEServer()
816 {
817 wxString server = wxGetTextFromUser(_T("Server to connect to:"),
818 DIALOG_TITLE, m_server);
819 if ( !server )
820 return FALSE;
821
822 m_server = server;
823
824 wxString topic = wxGetTextFromUser(_T("DDE topic:"), DIALOG_TITLE, m_topic);
825 if ( !topic )
826 return FALSE;
827
828 m_topic = topic;
829
830 wxString cmd = wxGetTextFromUser(_T("DDE command:"), DIALOG_TITLE, m_cmdDde);
831 if ( !cmd )
832 return FALSE;
833
834 m_cmdDde = cmd;
835
836 return TRUE;
837 }
838
839 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
840 {
841 if ( !GetDDEServer() )
842 return;
843
844 wxDDEClient client;
845 wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic);
846 if ( !conn )
847 {
848 wxLogError(_T("Failed to connect to the DDE server '%s'."),
849 m_server.c_str());
850 }
851 else
852 {
853 if ( !conn->Execute(m_cmdDde) )
854 {
855 wxLogError(_T("Failed to execute command '%s' via DDE."),
856 m_cmdDde.c_str());
857 }
858 else
859 {
860 wxLogStatus(_T("Successfully executed DDE command"));
861 }
862 }
863 }
864
865 void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
866 {
867 if ( !GetDDEServer() )
868 return;
869
870 wxDDEClient client;
871 wxConnectionBase *conn = client.MakeConnection("", m_server, m_topic);
872 if ( !conn )
873 {
874 wxLogError(_T("Failed to connect to the DDE server '%s'."),
875 m_server.c_str());
876 }
877 else
878 {
879 if ( !conn->Request(m_cmdDde) )
880 {
881 wxLogError(_T("Failed to send request '%s' via DDE."),
882 m_cmdDde.c_str());
883 }
884 else
885 {
886 wxLogStatus(_T("Successfully sent DDE request."));
887 }
888 }
889 }
890
891 #endif // __WINDOWS__
892
893 // ----------------------------------------------------------------------------
894 // various helpers
895 // ----------------------------------------------------------------------------
896
897 // input polling
898 void MyFrame::OnIdle(wxIdleEvent& event)
899 {
900 size_t count = m_running.GetCount();
901 for ( size_t n = 0; n < count; n++ )
902 {
903 if ( m_running[n]->HasInput() )
904 {
905 event.RequestMore();
906 }
907 }
908 }
909
910 void MyFrame::OnTimer(wxTimerEvent& WXUNUSED(event))
911 {
912 wxWakeUpIdle();
913 }
914
915 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
916 {
917 RemoveAsyncProcess(process);
918 }
919
920
921 void MyFrame::ShowOutput(const wxString& cmd,
922 const wxArrayString& output,
923 const wxString& title)
924 {
925 size_t count = output.GetCount();
926 if ( !count )
927 return;
928
929 m_lbox->Append(wxString::Format(_T("--- %s of '%s' ---"),
930 title.c_str(), cmd.c_str()));
931
932 for ( size_t n = 0; n < count; n++ )
933 {
934 m_lbox->Append(output[n]);
935 }
936
937 m_lbox->Append(wxString::Format(_T("--- End of %s ---"),
938 title.Lower().c_str()));
939 }
940
941 // ----------------------------------------------------------------------------
942 // MyProcess
943 // ----------------------------------------------------------------------------
944
945 void MyProcess::OnTerminate(int pid, int status)
946 {
947 wxLogStatus(m_parent, _T("Process %u ('%s') terminated with exit code %d."),
948 pid, m_cmd.c_str(), status);
949
950 // we're not needed any more
951 delete this;
952 }
953
954 // ----------------------------------------------------------------------------
955 // MyPipedProcess
956 // ----------------------------------------------------------------------------
957
958 bool MyPipedProcess::HasInput()
959 {
960 bool hasInput = FALSE;
961
962 if ( IsInputAvailable() )
963 {
964 wxTextInputStream tis(*GetInputStream());
965
966 // this assumes that the output is always line buffered
967 wxString msg;
968 msg << m_cmd << _T(" (stdout): ") << tis.ReadLine();
969
970 m_parent->GetLogListBox()->Append(msg);
971
972 hasInput = TRUE;
973 }
974
975 if ( IsErrorAvailable() )
976 {
977 wxTextInputStream tis(*GetErrorStream());
978
979 // this assumes that the output is always line buffered
980 wxString msg;
981 msg << m_cmd << _T(" (stderr): ") << tis.ReadLine();
982
983 m_parent->GetLogListBox()->Append(msg);
984
985 hasInput = TRUE;
986 }
987
988 return hasInput;
989 }
990
991 void MyPipedProcess::OnTerminate(int pid, int status)
992 {
993 // show the rest of the output
994 while ( HasInput() )
995 ;
996
997 m_parent->OnProcessTerminated(this);
998
999 MyProcess::OnTerminate(pid, status);
1000 }
1001
1002 // ----------------------------------------------------------------------------
1003 // MyPipedProcess2
1004 // ----------------------------------------------------------------------------
1005
1006 bool MyPipedProcess2::HasInput()
1007 {
1008 if ( !!m_input )
1009 {
1010 wxTextOutputStream os(*GetOutputStream());
1011 os.WriteString(m_input);
1012
1013 CloseOutput();
1014 m_input.clear();
1015
1016 // call us once again - may be we'll have output
1017 return TRUE;
1018 }
1019
1020 return MyPipedProcess::HasInput();
1021 }
1022
1023 // ============================================================================
1024 // MyPipeFrame implementation
1025 // ============================================================================
1026
1027 MyPipeFrame::MyPipeFrame(wxFrame *parent,
1028 const wxString& cmd,
1029 wxProcess *process)
1030 : wxFrame(parent, -1, cmd),
1031 m_process(process),
1032 // in a real program we'd check that the streams are !NULL here
1033 m_in(*process->GetInputStream()),
1034 m_out(*process->GetOutputStream())
1035 {
1036 m_process->SetNextHandler(this);
1037
1038 wxPanel *panel = new wxPanel(this, -1);
1039
1040 m_textIn = new wxTextCtrl(panel, -1, _T(""),
1041 wxDefaultPosition, wxDefaultSize,
1042 wxTE_PROCESS_ENTER);
1043 m_textOut = new wxTextCtrl(panel, -1, _T(""));
1044 m_textOut->SetEditable(FALSE);
1045
1046 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1047 sizerTop->Add(m_textIn, 0, wxGROW | wxALL, 5);
1048
1049 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1050 sizerBtns->Add(new wxButton(panel, Exec_Btn_Send, _T("&Send")), 0,
1051 wxALL, 10);
1052 sizerBtns->Add(new wxButton(panel, Exec_Btn_Get, _T("&Get")), 0,
1053 wxALL, 10);
1054
1055 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1056 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1057
1058 panel->SetSizer(sizerTop);
1059 sizerTop->Fit(this);
1060
1061 Show();
1062 }
1063
1064 void MyPipeFrame::DoGet()
1065 {
1066 // we don't have any way to be notified when any input appears on the
1067 // stream so we have to poll it :-(
1068 //
1069 // NB: this really must be done because otherwise the other program might
1070 // not have enough time to receive or process our data and we'd read
1071 // an empty string
1072 while ( !m_process->IsInputAvailable() && m_process->IsInputOpened() )
1073 ;
1074
1075 m_textOut->SetValue(m_in.ReadLine());
1076 }
1077
1078 void MyPipeFrame::OnClose(wxCloseEvent& event)
1079 {
1080 if ( m_process )
1081 {
1082 // we're not interested in getting the process termination notification
1083 // if we are closing it ourselves
1084 wxProcess *process = m_process;
1085 m_process = NULL;
1086 process->SetNextHandler(NULL);
1087
1088 process->CloseOutput();
1089 }
1090
1091 event.Skip();
1092 }
1093
1094 void MyPipeFrame::OnProcessTerm(wxProcessEvent& event)
1095 {
1096 delete m_process;
1097 m_process = NULL;
1098
1099 wxLogWarning(_T("The other process has terminated, closing"));
1100
1101 Close();
1102 }