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