Remember last values used in exec sample "Kill" menu item dialogs.
[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 // For compilers that support precompilation, includes "wx/wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 // for all others, include the necessary headers (this file is usually all you
28 // need because it includes almost all "standard" wxWidgets headers
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/log.h"
32 #include "wx/frame.h"
33 #include "wx/panel.h"
34
35 #include "wx/timer.h"
36
37 #include "wx/utils.h"
38 #include "wx/menu.h"
39
40 #include "wx/msgdlg.h"
41 #include "wx/textdlg.h"
42 #include "wx/filedlg.h"
43 #include "wx/choicdlg.h"
44
45 #include "wx/button.h"
46 #include "wx/textctrl.h"
47 #include "wx/listbox.h"
48
49 #include "wx/sizer.h"
50 #endif
51
52 #include "wx/txtstrm.h"
53 #include "wx/numdlg.h"
54 #include "wx/textdlg.h"
55 #include "wx/ffile.h"
56 #include "wx/stopwatch.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_PTR(MyPipedProcess *, MyPipedProcessesArray);
86
87 class MyProcess;
88 WX_DEFINE_ARRAY_PTR(MyProcess *, MyProcessesArray);
89
90 // Define a new frame type: this is going to be our main frame
91 class MyFrame : public wxFrame
92 {
93 public:
94 // ctor and dtor
95 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
96 virtual ~MyFrame();
97
98 // event handlers (these functions should _not_ be virtual)
99 void OnQuit(wxCommandEvent& event);
100
101 void OnKill(wxCommandEvent& event);
102
103 void OnClear(wxCommandEvent& event);
104
105 void OnBeginBusyCursor(wxCommandEvent& event);
106 void OnEndBusyCursor(wxCommandEvent& event);
107
108 void OnSyncExec(wxCommandEvent& event);
109 void OnSyncNoEventsExec(wxCommandEvent& event);
110 void OnAsyncExec(wxCommandEvent& event);
111 void OnShell(wxCommandEvent& event);
112 void OnExecWithRedirect(wxCommandEvent& event);
113 void OnExecWithPipe(wxCommandEvent& event);
114
115 void OnPOpen(wxCommandEvent& event);
116
117 void OnFileExec(wxCommandEvent& event);
118 void OnFileLaunch(wxCommandEvent& event);
119 void OnOpenURL(wxCommandEvent& event);
120
121 void OnAbout(wxCommandEvent& event);
122
123 // polling output of async processes
124 void OnIdleTimer(wxTimerEvent& event);
125 void OnIdle(wxIdleEvent& event);
126
127 // for MyPipedProcess
128 void OnProcessTerminated(MyPipedProcess *process);
129 wxListBox *GetLogListBox() const { return m_lbox; }
130
131 // for MyProcess
132 void OnAsyncTermination(MyProcess *process);
133
134 // timer updating a counter in the background
135 void OnBgTimer(wxTimerEvent& event);
136
137 private:
138 void ShowOutput(const wxString& cmd,
139 const wxArrayString& output,
140 const wxString& title);
141
142 void DoAsyncExec(const wxString& cmd);
143
144 void AddAsyncProcess(MyProcess *process) { m_allAsync.push_back(process); }
145
146 void AddPipedProcess(MyPipedProcess *process);
147 void RemovePipedProcess(MyPipedProcess *process);
148
149
150 // the PID of the last process we launched asynchronously
151 long m_pidLast;
152
153 // last command we executed
154 wxString m_cmdLast;
155
156 #ifdef __WINDOWS__
157 void OnDDEExec(wxCommandEvent& event);
158 void OnDDERequest(wxCommandEvent& event);
159
160 bool GetDDEServer();
161
162 // last params of a DDE transaction
163 wxString m_server,
164 m_topic,
165 m_cmdDde;
166 #endif // __WINDOWS__
167
168 wxListBox *m_lbox;
169
170 // array of running processes with redirected IO
171 MyPipedProcessesArray m_running;
172
173 // array of all asynchrously running processes
174 MyProcessesArray m_allAsync;
175
176 // the idle event wake up timer
177 wxTimer m_timerIdleWakeUp;
178
179 // a background timer allowing to easily check visually whether the
180 // messages are processed or not
181 wxTimer m_timerBg;
182
183 // any class wishing to process wxWidgets events must use this macro
184 DECLARE_EVENT_TABLE()
185 };
186
187 // ----------------------------------------------------------------------------
188 // MyPipeFrame: allows the user to communicate with the child process
189 // ----------------------------------------------------------------------------
190
191 class MyPipeFrame : public wxFrame
192 {
193 public:
194 MyPipeFrame(wxFrame *parent,
195 const wxString& cmd,
196 wxProcess *process);
197
198 protected:
199 void OnTextEnter(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
200 void OnBtnSend(wxCommandEvent& WXUNUSED(event)) { DoSend(); }
201 void OnBtnSendFile(wxCommandEvent& WXUNUSED(event));
202 void OnBtnGet(wxCommandEvent& WXUNUSED(event)) { DoGet(); }
203 void OnBtnClose(wxCommandEvent& WXUNUSED(event)) { DoClose(); }
204
205 void OnClose(wxCloseEvent& event);
206
207 void OnProcessTerm(wxProcessEvent& event);
208
209 void DoSend()
210 {
211 wxString s(m_textOut->GetValue());
212 s += wxT('\n');
213 m_out.Write(s.c_str(), s.length());
214 m_textOut->Clear();
215
216 DoGet();
217 }
218
219 void DoGet();
220 void DoClose();
221
222 private:
223 void DoGetFromStream(wxTextCtrl *text, wxInputStream& in);
224 void DisableInput();
225 void DisableOutput();
226
227
228 wxProcess *m_process;
229
230 wxOutputStream &m_out;
231 wxInputStream &m_in,
232 &m_err;
233
234 wxTextCtrl *m_textOut,
235 *m_textIn,
236 *m_textErr;
237
238 DECLARE_EVENT_TABLE()
239 };
240
241 // ----------------------------------------------------------------------------
242 // wxProcess-derived classes
243 // ----------------------------------------------------------------------------
244
245 // This is the handler for process termination events
246 class MyProcess : public wxProcess
247 {
248 public:
249 MyProcess(MyFrame *parent, const wxString& cmd)
250 : wxProcess(parent), m_cmd(cmd)
251 {
252 m_parent = parent;
253 }
254
255 // instead of overriding this virtual function we might as well process the
256 // event from it in the frame class - this might be more convenient in some
257 // cases
258 virtual void OnTerminate(int pid, int status);
259
260 protected:
261 MyFrame *m_parent;
262 wxString m_cmd;
263 };
264
265 // A specialization of MyProcess for redirecting the output
266 class MyPipedProcess : public MyProcess
267 {
268 public:
269 MyPipedProcess(MyFrame *parent, const wxString& cmd)
270 : MyProcess(parent, cmd)
271 {
272 Redirect();
273 }
274
275 virtual void OnTerminate(int pid, int status);
276
277 virtual bool HasInput();
278 };
279
280 // A version of MyPipedProcess which also sends input to the stdin of the
281 // child process
282 class MyPipedProcess2 : public MyPipedProcess
283 {
284 public:
285 MyPipedProcess2(MyFrame *parent, const wxString& cmd, const wxString& input)
286 : MyPipedProcess(parent, cmd), m_input(input)
287 {
288 }
289
290 virtual bool HasInput();
291
292 private:
293 wxString m_input;
294 };
295
296 // ----------------------------------------------------------------------------
297 // constants
298 // ----------------------------------------------------------------------------
299
300 // IDs for the controls and the menu commands
301 enum
302 {
303 // timer ids
304 Exec_TimerIdle = 10,
305 Exec_TimerBg,
306
307 // menu items
308 Exec_Quit = 100,
309 Exec_Kill,
310 Exec_ClearLog,
311 Exec_BeginBusyCursor,
312 Exec_EndBusyCursor,
313 Exec_SyncExec = 200,
314 Exec_SyncNoEventsExec,
315 Exec_AsyncExec,
316 Exec_Shell,
317 Exec_POpen,
318 Exec_OpenFile,
319 Exec_LaunchFile,
320 Exec_OpenURL,
321 Exec_DDEExec,
322 Exec_DDERequest,
323 Exec_Redirect,
324 Exec_Pipe,
325 Exec_About = wxID_ABOUT,
326
327 // control ids
328 Exec_Btn_Send = 1000,
329 Exec_Btn_SendFile,
330 Exec_Btn_Get,
331 Exec_Btn_Close
332 };
333
334 static const wxChar *DIALOG_TITLE = wxT("Exec sample");
335
336 // ----------------------------------------------------------------------------
337 // event tables and other macros for wxWidgets
338 // ----------------------------------------------------------------------------
339
340 // the event tables connect the wxWidgets events with the functions (event
341 // handlers) which process them. It can be also done at run-time, but for the
342 // simple menu events like this the static method is much simpler.
343 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
344 EVT_MENU(Exec_Quit, MyFrame::OnQuit)
345 EVT_MENU(Exec_Kill, MyFrame::OnKill)
346 EVT_MENU(Exec_ClearLog, MyFrame::OnClear)
347 EVT_MENU(Exec_BeginBusyCursor, MyFrame::OnBeginBusyCursor)
348 EVT_MENU(Exec_EndBusyCursor, MyFrame::OnEndBusyCursor)
349
350 EVT_MENU(Exec_SyncExec, MyFrame::OnSyncExec)
351 EVT_MENU(Exec_SyncNoEventsExec, MyFrame::OnSyncNoEventsExec)
352 EVT_MENU(Exec_AsyncExec, MyFrame::OnAsyncExec)
353 EVT_MENU(Exec_Shell, MyFrame::OnShell)
354 EVT_MENU(Exec_Redirect, MyFrame::OnExecWithRedirect)
355 EVT_MENU(Exec_Pipe, MyFrame::OnExecWithPipe)
356
357 EVT_MENU(Exec_POpen, MyFrame::OnPOpen)
358
359 EVT_MENU(Exec_OpenFile, MyFrame::OnFileExec)
360 EVT_MENU(Exec_LaunchFile, MyFrame::OnFileLaunch)
361 EVT_MENU(Exec_OpenURL, MyFrame::OnOpenURL)
362
363 #ifdef __WINDOWS__
364 EVT_MENU(Exec_DDEExec, MyFrame::OnDDEExec)
365 EVT_MENU(Exec_DDERequest, MyFrame::OnDDERequest)
366 #endif // __WINDOWS__
367
368 EVT_MENU(Exec_About, MyFrame::OnAbout)
369
370 EVT_IDLE(MyFrame::OnIdle)
371
372 EVT_TIMER(Exec_TimerIdle, MyFrame::OnIdleTimer)
373 EVT_TIMER(Exec_TimerBg, MyFrame::OnBgTimer)
374 END_EVENT_TABLE()
375
376 BEGIN_EVENT_TABLE(MyPipeFrame, wxFrame)
377 EVT_BUTTON(Exec_Btn_Send, MyPipeFrame::OnBtnSend)
378 EVT_BUTTON(Exec_Btn_SendFile, MyPipeFrame::OnBtnSendFile)
379 EVT_BUTTON(Exec_Btn_Get, MyPipeFrame::OnBtnGet)
380 EVT_BUTTON(Exec_Btn_Close, MyPipeFrame::OnBtnClose)
381
382 EVT_TEXT_ENTER(wxID_ANY, MyPipeFrame::OnTextEnter)
383
384 EVT_CLOSE(MyPipeFrame::OnClose)
385
386 EVT_END_PROCESS(wxID_ANY, MyPipeFrame::OnProcessTerm)
387 END_EVENT_TABLE()
388
389 // Create a new application object: this macro will allow wxWidgets to create
390 // the application object during program execution (it's better than using a
391 // static object for many reasons) and also declares the accessor function
392 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
393 // not wxApp)
394 IMPLEMENT_APP(MyApp)
395
396 // ============================================================================
397 // implementation
398 // ============================================================================
399
400 // ----------------------------------------------------------------------------
401 // the application class
402 // ----------------------------------------------------------------------------
403
404 // `Main program' equivalent: the program execution "starts" here
405 bool MyApp::OnInit()
406 {
407 if ( !wxApp::OnInit() )
408 return false;
409
410 // Create the main application window
411 MyFrame *frame = new MyFrame(wxT("Exec wxWidgets sample"),
412 wxDefaultPosition, wxSize(500, 140));
413
414 // Show it and tell the application that it's our main window
415 frame->Show(true);
416 SetTopWindow(frame);
417
418 // success: wxApp::OnRun() will be called which will enter the main message
419 // loop and the application will run. If we returned false here, the
420 // application would exit immediately.
421 return true;
422 }
423
424 // ----------------------------------------------------------------------------
425 // main frame
426 // ----------------------------------------------------------------------------
427
428 #ifdef __VISUALC__
429 #pragma warning(disable: 4355) // this used in base member initializer list
430 #endif
431
432 // frame constructor
433 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
434 : wxFrame((wxFrame *)NULL, wxID_ANY, title, pos, size),
435 m_timerIdleWakeUp(this, Exec_TimerIdle),
436 m_timerBg(this, Exec_TimerBg)
437 {
438 m_pidLast = 0;
439
440 #ifdef __WXMAC__
441 // we need this in order to allow the about menu relocation, since ABOUT is
442 // not the default id of the about menu
443 wxApp::s_macAboutMenuItemId = Exec_About;
444 #endif
445
446 // create a menu bar
447 wxMenu *menuFile = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
448 menuFile->Append(Exec_Kill, wxT("&Kill process...\tCtrl-K"),
449 wxT("Kill a process by PID"));
450 menuFile->AppendSeparator();
451 menuFile->Append(Exec_ClearLog, wxT("&Clear log\tCtrl-L"),
452 wxT("Clear the log window"));
453 menuFile->AppendSeparator();
454 menuFile->Append(Exec_BeginBusyCursor, wxT("Show &busy cursor\tCtrl-C"));
455 menuFile->Append(Exec_EndBusyCursor, wxT("Show &normal cursor\tShift-Ctrl-C"));
456 menuFile->AppendSeparator();
457 menuFile->Append(Exec_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
458
459 wxMenu *execMenu = new wxMenu;
460 execMenu->Append(Exec_SyncExec, wxT("Sync &execution...\tCtrl-E"),
461 wxT("Launch a program and return when it terminates"));
462 execMenu->Append(Exec_SyncNoEventsExec, wxT("Sync execution and &block...\tCtrl-B"),
463 wxT("Launch a program and block until it terminates"));
464 execMenu->Append(Exec_AsyncExec, wxT("&Async execution...\tCtrl-A"),
465 wxT("Launch a program and return immediately"));
466 execMenu->Append(Exec_Shell, wxT("Execute &shell command...\tCtrl-S"),
467 wxT("Launch a shell and execute a command in it"));
468 execMenu->AppendSeparator();
469 execMenu->Append(Exec_Redirect, wxT("Capture command &output...\tCtrl-O"),
470 wxT("Launch a program and capture its output"));
471 execMenu->Append(Exec_Pipe, wxT("&Pipe through command..."),
472 wxT("Pipe a string through a filter"));
473 execMenu->Append(Exec_POpen, wxT("&Open a pipe to a command...\tCtrl-P"),
474 wxT("Open a pipe to and from another program"));
475
476 execMenu->AppendSeparator();
477 execMenu->Append(Exec_OpenFile, wxT("Open &file...\tCtrl-F"),
478 wxT("Launch the command to open this kind of files"));
479 execMenu->Append(Exec_LaunchFile, wxT("La&unch file...\tShift-Ctrl-F"),
480 wxT("Launch the default application associated with the file"));
481 execMenu->Append(Exec_OpenURL, wxT("Open &URL...\tCtrl-U"),
482 wxT("Launch the default browser with the given URL"));
483 #ifdef __WINDOWS__
484 execMenu->AppendSeparator();
485 execMenu->Append(Exec_DDEExec, wxT("Execute command via &DDE...\tCtrl-D"));
486 execMenu->Append(Exec_DDERequest, wxT("Send DDE &request...\tCtrl-R"));
487 #endif
488
489 wxMenu *helpMenu = new wxMenu(wxEmptyString, wxMENU_TEAROFF);
490 helpMenu->Append(Exec_About, wxT("&About...\tF1"), wxT("Show about dialog"));
491
492 // now append the freshly created menu to the menu bar...
493 wxMenuBar *menuBar = new wxMenuBar();
494 menuBar->Append(menuFile, wxT("&File"));
495 menuBar->Append(execMenu, wxT("&Exec"));
496 menuBar->Append(helpMenu, wxT("&Help"));
497
498 // ... and attach this menu bar to the frame
499 SetMenuBar(menuBar);
500
501 // create the listbox in which we will show misc messages as they come
502 m_lbox = new wxListBox(this, wxID_ANY);
503 wxFont font(12, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
504 wxFONTWEIGHT_NORMAL);
505 if ( font.Ok() )
506 m_lbox->SetFont(font);
507
508 #if wxUSE_STATUSBAR
509 // create a status bar just for fun (by default with 1 pane only)
510 CreateStatusBar(2);
511 SetStatusText(wxT("Welcome to wxWidgets exec sample!"));
512 #endif // wxUSE_STATUSBAR
513
514 m_timerBg.Start(1000);
515 }
516
517 MyFrame::~MyFrame()
518 {
519 // any processes left until now must be deleted manually: normally this is
520 // done when the associated process terminates but it must be still running
521 // if this didn't happen until now
522 for ( size_t n = 0; n < m_allAsync.size(); n++ )
523 {
524 delete m_allAsync[n];
525 }
526 }
527
528 // ----------------------------------------------------------------------------
529 // event handlers: file and help menu
530 // ----------------------------------------------------------------------------
531
532 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
533 {
534 // true is to force the frame to close
535 Close(true);
536 }
537
538 void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
539 {
540 m_lbox->Clear();
541 }
542
543 void MyFrame::OnBeginBusyCursor(wxCommandEvent& WXUNUSED(event))
544 {
545 wxBeginBusyCursor();
546 }
547
548 void MyFrame::OnEndBusyCursor(wxCommandEvent& WXUNUSED(event))
549 {
550 wxEndBusyCursor();
551 }
552
553 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
554 {
555 wxMessageBox(wxT("Exec wxWidgets Sample\n(c) 2000-2002 Vadim Zeitlin"),
556 wxT("About Exec"), wxOK | wxICON_INFORMATION, this);
557 }
558
559 void MyFrame::OnKill(wxCommandEvent& WXUNUSED(event))
560 {
561 long pid = wxGetNumberFromUser(wxT("Please specify the process to kill"),
562 wxT("Enter PID:"),
563 wxT("Exec question"),
564 m_pidLast,
565 // we need the full unsigned int range
566 -INT_MAX, INT_MAX,
567 this);
568 if ( pid == -1 )
569 {
570 // cancelled
571 return;
572 }
573
574 m_pidLast = pid;
575
576 static const wxString signalNames[] =
577 {
578 wxT("Just test (SIGNONE)"),
579 wxT("Hangup (SIGHUP)"),
580 wxT("Interrupt (SIGINT)"),
581 wxT("Quit (SIGQUIT)"),
582 wxT("Illegal instruction (SIGILL)"),
583 wxT("Trap (SIGTRAP)"),
584 wxT("Abort (SIGABRT)"),
585 wxT("Emulated trap (SIGEMT)"),
586 wxT("FP exception (SIGFPE)"),
587 wxT("Kill (SIGKILL)"),
588 wxT("Bus (SIGBUS)"),
589 wxT("Segment violation (SIGSEGV)"),
590 wxT("System (SIGSYS)"),
591 wxT("Broken pipe (SIGPIPE)"),
592 wxT("Alarm (SIGALRM)"),
593 wxT("Terminate (SIGTERM)"),
594 };
595
596 static int s_sigLast = wxSIGNONE;
597 int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
598 wxT("Exec question"),
599 WXSIZEOF(signalNames), signalNames,
600 s_sigLast,
601 this);
602 switch ( sig )
603 {
604 default:
605 wxFAIL_MSG( wxT("unexpected return value") );
606 // fall through
607
608 case -1:
609 // cancelled
610 return;
611
612 case wxSIGNONE:
613 case wxSIGHUP:
614 case wxSIGINT:
615 case wxSIGQUIT:
616 case wxSIGILL:
617 case wxSIGTRAP:
618 case wxSIGABRT:
619 case wxSIGEMT:
620 case wxSIGFPE:
621 case wxSIGKILL:
622 case wxSIGBUS:
623 case wxSIGSEGV:
624 case wxSIGSYS:
625 case wxSIGPIPE:
626 case wxSIGALRM:
627 case wxSIGTERM:
628 break;
629 }
630
631 s_sigLast = sig;
632
633 if ( sig == wxSIGNONE )
634 {
635 // This simply calls Kill(wxSIGNONE) but using it is more convenient.
636 if ( wxProcess::Exists(pid) )
637 {
638 wxLogStatus(wxT("Process %ld is running."), pid);
639 }
640 else
641 {
642 wxLogStatus(wxT("No process with pid = %ld."), pid);
643 }
644 }
645 else // not SIGNONE
646 {
647 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
648 if ( rc == wxKILL_OK )
649 {
650 wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
651 }
652 else
653 {
654 static const wxChar *errorText[] =
655 {
656 wxT(""), // no error
657 wxT("signal not supported"),
658 wxT("permission denied"),
659 wxT("no such process"),
660 wxT("unspecified error"),
661 };
662
663 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
664 pid, sig, errorText[rc]);
665 }
666 }
667 }
668
669 // ----------------------------------------------------------------------------
670 // event handlers: exec menu
671 // ----------------------------------------------------------------------------
672
673 void MyFrame::DoAsyncExec(const wxString& cmd)
674 {
675 MyProcess * const process = new MyProcess(this, cmd);
676 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
677 if ( !m_pidLast )
678 {
679 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
680
681 delete process;
682 }
683 else
684 {
685 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
686
687 m_cmdLast = cmd;
688
689 // the parent frame keeps track of all async processes as it needs to
690 // free them if we exit before the child process terminates
691 AddAsyncProcess(process);
692 }
693 }
694
695 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
696 {
697 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
698 DIALOG_TITLE,
699 m_cmdLast);
700
701 if ( !cmd )
702 return;
703
704 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
705
706 int code = wxExecute(cmd, wxEXEC_SYNC);
707
708 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
709 cmd.c_str(), code);
710
711 m_cmdLast = cmd;
712 }
713
714 void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event))
715 {
716 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
717 DIALOG_TITLE,
718 m_cmdLast);
719
720 if ( !cmd )
721 return;
722
723 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
724
725 int code = wxExecute(cmd, wxEXEC_BLOCK);
726
727 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
728 cmd.c_str(), code);
729
730 m_cmdLast = cmd;
731 }
732
733 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
734 {
735 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
736 DIALOG_TITLE,
737 m_cmdLast);
738
739 if ( !cmd )
740 return;
741
742 DoAsyncExec(cmd);
743 }
744
745 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
746 {
747 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
748 DIALOG_TITLE,
749 m_cmdLast);
750
751 if ( !cmd )
752 return;
753
754 int code = wxShell(cmd);
755 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
756 cmd.c_str(), code);
757 m_cmdLast = cmd;
758 }
759
760 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
761 {
762 if ( !m_cmdLast )
763 {
764 #ifdef __WXMSW__
765 m_cmdLast = "type Makefile.in";
766 #else
767 m_cmdLast = "cat -n Makefile";
768 #endif
769 }
770
771 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
772 DIALOG_TITLE,
773 m_cmdLast);
774
775 if ( !cmd )
776 return;
777
778 bool sync;
779 switch ( wxMessageBox(wxT("Execute it synchronously?"),
780 wxT("Exec question"),
781 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
782 {
783 case wxYES:
784 sync = true;
785 break;
786
787 case wxNO:
788 sync = false;
789 break;
790
791 default:
792 return;
793 }
794
795 if ( sync )
796 {
797 wxLogStatus("\"%s\" is running please wait...", cmd);
798
799 wxStopWatch sw;
800
801 wxArrayString output, errors;
802 int code = wxExecute(cmd, output, errors);
803
804 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
805 cmd, sw.Time(), code);
806
807 if ( code != -1 )
808 {
809 ShowOutput(cmd, output, wxT("Output"));
810 ShowOutput(cmd, errors, wxT("Errors"));
811 }
812 }
813 else // async exec
814 {
815 MyPipedProcess *process = new MyPipedProcess(this, cmd);
816 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
817 {
818 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
819
820 delete process;
821 }
822 else
823 {
824 AddPipedProcess(process);
825 }
826 }
827
828 m_cmdLast = cmd;
829 }
830
831 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
832 {
833 if ( !m_cmdLast )
834 m_cmdLast = wxT("tr [a-z] [A-Z]");
835
836 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
837 DIALOG_TITLE,
838 m_cmdLast);
839
840 if ( !cmd )
841 return;
842
843 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
844 DIALOG_TITLE);
845 if ( !input )
846 return;
847
848 // always execute the filter asynchronously
849 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
850 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
851 if ( pid )
852 {
853 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
854
855 AddPipedProcess(process);
856 }
857 else
858 {
859 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
860
861 delete process;
862 }
863
864 m_cmdLast = cmd;
865 }
866
867 void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
868 {
869 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
870 DIALOG_TITLE,
871 m_cmdLast);
872 if ( cmd.empty() )
873 return;
874
875 wxProcess *process = wxProcess::Open(cmd);
876 if ( !process )
877 {
878 wxLogError(wxT("Failed to launch the command."));
879 return;
880 }
881
882 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
883
884 wxOutputStream *out = process->GetOutputStream();
885 if ( !out )
886 {
887 wxLogError(wxT("Failed to connect to child stdin"));
888 return;
889 }
890
891 wxInputStream *in = process->GetInputStream();
892 if ( !in )
893 {
894 wxLogError(wxT("Failed to connect to child stdout"));
895 return;
896 }
897
898 new MyPipeFrame(this, cmd, process);
899 }
900
901 static wxString gs_lastFile;
902
903 static bool AskUserForFileName()
904 {
905 wxString filename;
906
907 #if wxUSE_FILEDLG
908 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
909 #else // !wxUSE_FILEDLG
910 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
911 gs_lastFile);
912 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
913
914 if ( filename.empty() )
915 return false;
916
917 gs_lastFile = filename;
918
919 return true;
920 }
921
922 void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
923 {
924 if ( !AskUserForFileName() )
925 return;
926
927 wxString ext = gs_lastFile.AfterLast(wxT('.'));
928 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
929 if ( !ft )
930 {
931 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
932 ext.c_str());
933 return;
934 }
935
936 wxString cmd;
937 bool ok = ft->GetOpenCommand(&cmd,
938 wxFileType::MessageParameters(gs_lastFile));
939 delete ft;
940 if ( !ok )
941 {
942 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
943 ext.c_str());
944 return;
945 }
946
947 DoAsyncExec(cmd);
948 }
949
950 void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
951 {
952 if ( !AskUserForFileName() )
953 return;
954
955 if ( !wxLaunchDefaultApplication(gs_lastFile) )
956 {
957 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
958 }
959 }
960
961 void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
962 {
963 static wxString s_url(wxT("http://www.wxwidgets.org/"));
964
965 wxString filename = wxGetTextFromUser
966 (
967 wxT("Enter the URL"),
968 wxT("exec sample"),
969 s_url,
970 this
971 );
972
973 if ( filename.empty() )
974 return;
975
976 s_url = filename;
977
978 if ( !wxLaunchDefaultBrowser(s_url) )
979 {
980 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
981 }
982 }
983
984 // ----------------------------------------------------------------------------
985 // DDE stuff
986 // ----------------------------------------------------------------------------
987
988 #ifdef __WINDOWS__
989
990 bool MyFrame::GetDDEServer()
991 {
992 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
993 DIALOG_TITLE, m_server);
994 if ( !server )
995 return false;
996
997 m_server = server;
998
999 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
1000 if ( !topic )
1001 return false;
1002
1003 m_topic = topic;
1004
1005 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
1006 if ( !cmd )
1007 return false;
1008
1009 m_cmdDde = cmd;
1010
1011 return true;
1012 }
1013
1014 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1015 {
1016 if ( !GetDDEServer() )
1017 return;
1018
1019 wxDDEClient client;
1020 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1021 if ( !conn )
1022 {
1023 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1024 m_server.c_str());
1025 }
1026 else
1027 {
1028 if ( !conn->Execute(m_cmdDde) )
1029 {
1030 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1031 m_cmdDde.c_str());
1032 }
1033 else
1034 {
1035 wxLogStatus(wxT("Successfully executed DDE command"));
1036 }
1037 }
1038 }
1039
1040 void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1041 {
1042 if ( !GetDDEServer() )
1043 return;
1044
1045 wxDDEClient client;
1046 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1047 if ( !conn )
1048 {
1049 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1050 m_server.c_str());
1051 }
1052 else
1053 {
1054 if ( !conn->Request(m_cmdDde) )
1055 {
1056 wxLogError(wxT("Failed to send request '%s' via DDE."),
1057 m_cmdDde.c_str());
1058 }
1059 else
1060 {
1061 wxLogStatus(wxT("Successfully sent DDE request."));
1062 }
1063 }
1064 }
1065
1066 #endif // __WINDOWS__
1067
1068 // ----------------------------------------------------------------------------
1069 // various helpers
1070 // ----------------------------------------------------------------------------
1071
1072 // input polling
1073 void MyFrame::OnIdle(wxIdleEvent& event)
1074 {
1075 size_t count = m_running.GetCount();
1076 for ( size_t n = 0; n < count; n++ )
1077 {
1078 if ( m_running[n]->HasInput() )
1079 {
1080 event.RequestMore();
1081 }
1082 }
1083 }
1084
1085 void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
1086 {
1087 wxWakeUpIdle();
1088 }
1089
1090 void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1091 {
1092 static unsigned long s_ticks = 0;
1093 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1094 }
1095
1096 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1097 {
1098 RemovePipedProcess(process);
1099 }
1100
1101 void MyFrame::OnAsyncTermination(MyProcess *process)
1102 {
1103 m_allAsync.Remove(process);
1104
1105 delete process;
1106 }
1107
1108 void MyFrame::AddPipedProcess(MyPipedProcess *process)
1109 {
1110 if ( m_running.IsEmpty() )
1111 {
1112 // we want to start getting the timer events to ensure that a
1113 // steady stream of idle events comes in -- otherwise we
1114 // wouldn't be able to poll the child process input
1115 m_timerIdleWakeUp.Start(100);
1116 }
1117 //else: the timer is already running
1118
1119 m_running.Add(process);
1120 m_allAsync.Add(process);
1121 }
1122
1123 void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1124 {
1125 m_running.Remove(process);
1126
1127 if ( m_running.IsEmpty() )
1128 {
1129 // we don't need to get idle events all the time any more
1130 m_timerIdleWakeUp.Stop();
1131 }
1132 }
1133
1134 void MyFrame::ShowOutput(const wxString& cmd,
1135 const wxArrayString& output,
1136 const wxString& title)
1137 {
1138 size_t count = output.GetCount();
1139 if ( !count )
1140 return;
1141
1142 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1143 title.c_str(), cmd.c_str()));
1144
1145 for ( size_t n = 0; n < count; n++ )
1146 {
1147 m_lbox->Append(output[n]);
1148 }
1149
1150 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
1151 title.Lower().c_str()));
1152 }
1153
1154 // ----------------------------------------------------------------------------
1155 // MyProcess
1156 // ----------------------------------------------------------------------------
1157
1158 void MyProcess::OnTerminate(int pid, int status)
1159 {
1160 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
1161 pid, m_cmd.c_str(), status);
1162
1163 m_parent->OnAsyncTermination(this);
1164 }
1165
1166 // ----------------------------------------------------------------------------
1167 // MyPipedProcess
1168 // ----------------------------------------------------------------------------
1169
1170 bool MyPipedProcess::HasInput()
1171 {
1172 bool hasInput = false;
1173
1174 if ( IsInputAvailable() )
1175 {
1176 wxTextInputStream tis(*GetInputStream());
1177
1178 // this assumes that the output is always line buffered
1179 wxString msg;
1180 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
1181
1182 m_parent->GetLogListBox()->Append(msg);
1183
1184 hasInput = true;
1185 }
1186
1187 if ( IsErrorAvailable() )
1188 {
1189 wxTextInputStream tis(*GetErrorStream());
1190
1191 // this assumes that the output is always line buffered
1192 wxString msg;
1193 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
1194
1195 m_parent->GetLogListBox()->Append(msg);
1196
1197 hasInput = true;
1198 }
1199
1200 return hasInput;
1201 }
1202
1203 void MyPipedProcess::OnTerminate(int pid, int status)
1204 {
1205 // show the rest of the output
1206 while ( HasInput() )
1207 ;
1208
1209 m_parent->OnProcessTerminated(this);
1210
1211 MyProcess::OnTerminate(pid, status);
1212 }
1213
1214 // ----------------------------------------------------------------------------
1215 // MyPipedProcess2
1216 // ----------------------------------------------------------------------------
1217
1218 bool MyPipedProcess2::HasInput()
1219 {
1220 if ( !m_input.empty() )
1221 {
1222 wxTextOutputStream os(*GetOutputStream());
1223 os.WriteString(m_input);
1224
1225 CloseOutput();
1226 m_input.clear();
1227
1228 // call us once again - may be we'll have output
1229 return true;
1230 }
1231
1232 return MyPipedProcess::HasInput();
1233 }
1234
1235 // ============================================================================
1236 // MyPipeFrame implementation
1237 // ============================================================================
1238
1239 MyPipeFrame::MyPipeFrame(wxFrame *parent,
1240 const wxString& cmd,
1241 wxProcess *process)
1242 : wxFrame(parent, wxID_ANY, cmd),
1243 m_process(process),
1244 // in a real program we'd check that the streams are !NULL here
1245 m_out(*process->GetOutputStream()),
1246 m_in(*process->GetInputStream()),
1247 m_err(*process->GetErrorStream())
1248 {
1249 m_process->SetNextHandler(this);
1250
1251 wxPanel *panel = new wxPanel(this, wxID_ANY);
1252
1253 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1254 wxDefaultPosition, wxDefaultSize,
1255 wxTE_PROCESS_ENTER);
1256 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1257 wxDefaultPosition, wxDefaultSize,
1258 wxTE_MULTILINE | wxTE_RICH);
1259 m_textIn->SetEditable(false);
1260 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1261 wxDefaultPosition, wxDefaultSize,
1262 wxTE_MULTILINE | wxTE_RICH);
1263 m_textErr->SetEditable(false);
1264
1265 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1266 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1267
1268 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1269 sizerBtns->
1270 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1271 sizerBtns->
1272 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1273 sizerBtns->
1274 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1275 sizerBtns->
1276 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
1277
1278 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1279 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1280 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
1281
1282 panel->SetSizer(sizerTop);
1283 sizerTop->Fit(this);
1284
1285 Show();
1286 }
1287
1288 void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1289 {
1290 #if wxUSE_FILEDLG
1291 wxFileDialog filedlg(this, wxT("Select file to send"));
1292 if ( filedlg.ShowModal() != wxID_OK )
1293 return;
1294
1295 wxFFile file(filedlg.GetFilename(), wxT("r"));
1296 wxString data;
1297 if ( !file.IsOpened() || !file.ReadAll(&data) )
1298 return;
1299
1300 // can't write the entire string at once, this risk overflowing the pipe
1301 // and we would dead lock
1302 size_t len = data.length();
1303 const wxChar *pc = data.c_str();
1304 while ( len )
1305 {
1306 const size_t CHUNK_SIZE = 4096;
1307 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1308
1309 // note that not all data could have been written as we don't block on
1310 // the write end of the pipe
1311 const size_t lenChunk = m_out.LastWrite();
1312
1313 pc += lenChunk;
1314 len -= lenChunk;
1315
1316 DoGet();
1317 }
1318 #endif // wxUSE_FILEDLG
1319 }
1320
1321 void MyPipeFrame::DoGet()
1322 {
1323 // we don't have any way to be notified when any input appears on the
1324 // stream so we have to poll it :-(
1325 DoGetFromStream(m_textIn, m_in);
1326 DoGetFromStream(m_textErr, m_err);
1327 }
1328
1329 void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1330 {
1331 while ( in.CanRead() )
1332 {
1333 wxChar buffer[4096];
1334 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = wxT('\0');
1335
1336 text->AppendText(buffer);
1337 }
1338 }
1339
1340 void MyPipeFrame::DoClose()
1341 {
1342 m_process->CloseOutput();
1343
1344 DisableInput();
1345 }
1346
1347 void MyPipeFrame::DisableInput()
1348 {
1349 m_textOut->SetEditable(false);
1350 FindWindow(Exec_Btn_Send)->Disable();
1351 FindWindow(Exec_Btn_SendFile)->Disable();
1352 FindWindow(Exec_Btn_Close)->Disable();
1353 }
1354
1355 void MyPipeFrame::DisableOutput()
1356 {
1357 FindWindow(Exec_Btn_Get)->Disable();
1358 }
1359
1360 void MyPipeFrame::OnClose(wxCloseEvent& event)
1361 {
1362 if ( m_process )
1363 {
1364 // we're not interested in getting the process termination notification
1365 // if we are closing it ourselves
1366 wxProcess *process = m_process;
1367 m_process = NULL;
1368 process->SetNextHandler(NULL);
1369
1370 process->CloseOutput();
1371 }
1372
1373 event.Skip();
1374 }
1375
1376 void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
1377 {
1378 DoGet();
1379
1380 wxDELETE(m_process);
1381
1382 wxLogWarning(wxT("The other process has terminated, closing"));
1383
1384 DisableInput();
1385 DisableOutput();
1386 }