]> git.saurik.com Git - wxWidgets.git/blob - samples/exec/exec.cpp
Don't enter an infinite loop if a spacer with min size of -1 is used.
[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 static const wxString signalNames[] =
575 {
576 wxT("Just test (SIGNONE)"),
577 wxT("Hangup (SIGHUP)"),
578 wxT("Interrupt (SIGINT)"),
579 wxT("Quit (SIGQUIT)"),
580 wxT("Illegal instruction (SIGILL)"),
581 wxT("Trap (SIGTRAP)"),
582 wxT("Abort (SIGABRT)"),
583 wxT("Emulated trap (SIGEMT)"),
584 wxT("FP exception (SIGFPE)"),
585 wxT("Kill (SIGKILL)"),
586 wxT("Bus (SIGBUS)"),
587 wxT("Segment violation (SIGSEGV)"),
588 wxT("System (SIGSYS)"),
589 wxT("Broken pipe (SIGPIPE)"),
590 wxT("Alarm (SIGALRM)"),
591 wxT("Terminate (SIGTERM)"),
592 };
593
594 int sig = wxGetSingleChoiceIndex(wxT("How to kill the process?"),
595 wxT("Exec question"),
596 WXSIZEOF(signalNames), signalNames,
597 this);
598 switch ( sig )
599 {
600 default:
601 wxFAIL_MSG( wxT("unexpected return value") );
602 // fall through
603
604 case -1:
605 // cancelled
606 return;
607
608 case wxSIGNONE:
609 case wxSIGHUP:
610 case wxSIGINT:
611 case wxSIGQUIT:
612 case wxSIGILL:
613 case wxSIGTRAP:
614 case wxSIGABRT:
615 case wxSIGEMT:
616 case wxSIGFPE:
617 case wxSIGKILL:
618 case wxSIGBUS:
619 case wxSIGSEGV:
620 case wxSIGSYS:
621 case wxSIGPIPE:
622 case wxSIGALRM:
623 case wxSIGTERM:
624 break;
625 }
626
627 if ( sig == 0 )
628 {
629 if ( wxProcess::Exists(pid) )
630 {
631 wxLogStatus(wxT("Process %ld is running."), pid);
632 }
633 else
634 {
635 wxLogStatus(wxT("No process with pid = %ld."), pid);
636 }
637 }
638 else // not SIGNONE
639 {
640 wxKillError rc = wxProcess::Kill(pid, (wxSignal)sig);
641 if ( rc == wxKILL_OK )
642 {
643 wxLogStatus(wxT("Process %ld killed with signal %d."), pid, sig);
644 }
645 else
646 {
647 static const wxChar *errorText[] =
648 {
649 wxT(""), // no error
650 wxT("signal not supported"),
651 wxT("permission denied"),
652 wxT("no such process"),
653 wxT("unspecified error"),
654 };
655
656 wxLogStatus(wxT("Failed to kill process %ld with signal %d: %s"),
657 pid, sig, errorText[rc]);
658 }
659 }
660 }
661
662 // ----------------------------------------------------------------------------
663 // event handlers: exec menu
664 // ----------------------------------------------------------------------------
665
666 void MyFrame::DoAsyncExec(const wxString& cmd)
667 {
668 MyProcess * const process = new MyProcess(this, cmd);
669 m_pidLast = wxExecute(cmd, wxEXEC_ASYNC, process);
670 if ( !m_pidLast )
671 {
672 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
673
674 delete process;
675 }
676 else
677 {
678 wxLogStatus(wxT("Process %ld (%s) launched."), m_pidLast, cmd.c_str());
679
680 m_cmdLast = cmd;
681
682 // the parent frame keeps track of all async processes as it needs to
683 // free them if we exit before the child process terminates
684 AddAsyncProcess(process);
685 }
686 }
687
688 void MyFrame::OnSyncExec(wxCommandEvent& WXUNUSED(event))
689 {
690 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
691 DIALOG_TITLE,
692 m_cmdLast);
693
694 if ( !cmd )
695 return;
696
697 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
698
699 int code = wxExecute(cmd, wxEXEC_SYNC);
700
701 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
702 cmd.c_str(), code);
703
704 m_cmdLast = cmd;
705 }
706
707 void MyFrame::OnSyncNoEventsExec(wxCommandEvent& WXUNUSED(event))
708 {
709 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
710 DIALOG_TITLE,
711 m_cmdLast);
712
713 if ( !cmd )
714 return;
715
716 wxLogStatus( wxT("'%s' is running please wait..."), cmd.c_str() );
717
718 int code = wxExecute(cmd, wxEXEC_BLOCK);
719
720 wxLogStatus(wxT("Process '%s' terminated with exit code %d."),
721 cmd.c_str(), code);
722
723 m_cmdLast = cmd;
724 }
725
726 void MyFrame::OnAsyncExec(wxCommandEvent& WXUNUSED(event))
727 {
728 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
729 DIALOG_TITLE,
730 m_cmdLast);
731
732 if ( !cmd )
733 return;
734
735 DoAsyncExec(cmd);
736 }
737
738 void MyFrame::OnShell(wxCommandEvent& WXUNUSED(event))
739 {
740 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
741 DIALOG_TITLE,
742 m_cmdLast);
743
744 if ( !cmd )
745 return;
746
747 int code = wxShell(cmd);
748 wxLogStatus(wxT("Shell command '%s' terminated with exit code %d."),
749 cmd.c_str(), code);
750 m_cmdLast = cmd;
751 }
752
753 void MyFrame::OnExecWithRedirect(wxCommandEvent& WXUNUSED(event))
754 {
755 if ( !m_cmdLast )
756 {
757 #ifdef __WXMSW__
758 m_cmdLast = "type Makefile.in";
759 #else
760 m_cmdLast = "cat -n Makefile";
761 #endif
762 }
763
764 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
765 DIALOG_TITLE,
766 m_cmdLast);
767
768 if ( !cmd )
769 return;
770
771 bool sync;
772 switch ( wxMessageBox(wxT("Execute it synchronously?"),
773 wxT("Exec question"),
774 wxYES_NO | wxCANCEL | wxICON_QUESTION, this) )
775 {
776 case wxYES:
777 sync = true;
778 break;
779
780 case wxNO:
781 sync = false;
782 break;
783
784 default:
785 return;
786 }
787
788 if ( sync )
789 {
790 wxLogStatus("\"%s\" is running please wait...", cmd);
791
792 wxStopWatch sw;
793
794 wxArrayString output, errors;
795 int code = wxExecute(cmd, output, errors);
796
797 wxLogStatus("Command \"%s\" terminated after %ldms; exit code %d.",
798 cmd, sw.Time(), code);
799
800 if ( code != -1 )
801 {
802 ShowOutput(cmd, output, wxT("Output"));
803 ShowOutput(cmd, errors, wxT("Errors"));
804 }
805 }
806 else // async exec
807 {
808 MyPipedProcess *process = new MyPipedProcess(this, cmd);
809 if ( !wxExecute(cmd, wxEXEC_ASYNC, process) )
810 {
811 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
812
813 delete process;
814 }
815 else
816 {
817 AddPipedProcess(process);
818 }
819 }
820
821 m_cmdLast = cmd;
822 }
823
824 void MyFrame::OnExecWithPipe(wxCommandEvent& WXUNUSED(event))
825 {
826 if ( !m_cmdLast )
827 m_cmdLast = wxT("tr [a-z] [A-Z]");
828
829 wxString cmd = wxGetTextFromUser(wxT("Enter the command: "),
830 DIALOG_TITLE,
831 m_cmdLast);
832
833 if ( !cmd )
834 return;
835
836 wxString input = wxGetTextFromUser(wxT("Enter the string to send to it: "),
837 DIALOG_TITLE);
838 if ( !input )
839 return;
840
841 // always execute the filter asynchronously
842 MyPipedProcess2 *process = new MyPipedProcess2(this, cmd, input);
843 long pid = wxExecute(cmd, wxEXEC_ASYNC, process);
844 if ( pid )
845 {
846 wxLogStatus(wxT("Process %ld (%s) launched."), pid, cmd.c_str());
847
848 AddPipedProcess(process);
849 }
850 else
851 {
852 wxLogError(wxT("Execution of '%s' failed."), cmd.c_str());
853
854 delete process;
855 }
856
857 m_cmdLast = cmd;
858 }
859
860 void MyFrame::OnPOpen(wxCommandEvent& WXUNUSED(event))
861 {
862 wxString cmd = wxGetTextFromUser(wxT("Enter the command to launch: "),
863 DIALOG_TITLE,
864 m_cmdLast);
865 if ( cmd.empty() )
866 return;
867
868 wxProcess *process = wxProcess::Open(cmd);
869 if ( !process )
870 {
871 wxLogError(wxT("Failed to launch the command."));
872 return;
873 }
874
875 wxLogVerbose(wxT("PID of the new process: %ld"), process->GetPid());
876
877 wxOutputStream *out = process->GetOutputStream();
878 if ( !out )
879 {
880 wxLogError(wxT("Failed to connect to child stdin"));
881 return;
882 }
883
884 wxInputStream *in = process->GetInputStream();
885 if ( !in )
886 {
887 wxLogError(wxT("Failed to connect to child stdout"));
888 return;
889 }
890
891 new MyPipeFrame(this, cmd, process);
892 }
893
894 static wxString gs_lastFile;
895
896 static bool AskUserForFileName()
897 {
898 wxString filename;
899
900 #if wxUSE_FILEDLG
901 filename = wxLoadFileSelector(wxT("any"), wxEmptyString, gs_lastFile);
902 #else // !wxUSE_FILEDLG
903 filename = wxGetTextFromUser(wxT("Enter the file name"), wxT("exec sample"),
904 gs_lastFile);
905 #endif // wxUSE_FILEDLG/!wxUSE_FILEDLG
906
907 if ( filename.empty() )
908 return false;
909
910 gs_lastFile = filename;
911
912 return true;
913 }
914
915 void MyFrame::OnFileExec(wxCommandEvent& WXUNUSED(event))
916 {
917 if ( !AskUserForFileName() )
918 return;
919
920 wxString ext = gs_lastFile.AfterLast(wxT('.'));
921 wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
922 if ( !ft )
923 {
924 wxLogError(wxT("Impossible to determine the file type for extension '%s'"),
925 ext.c_str());
926 return;
927 }
928
929 wxString cmd;
930 bool ok = ft->GetOpenCommand(&cmd,
931 wxFileType::MessageParameters(gs_lastFile));
932 delete ft;
933 if ( !ok )
934 {
935 wxLogError(wxT("Impossible to find out how to open files of extension '%s'"),
936 ext.c_str());
937 return;
938 }
939
940 DoAsyncExec(cmd);
941 }
942
943 void MyFrame::OnFileLaunch(wxCommandEvent& WXUNUSED(event))
944 {
945 if ( !AskUserForFileName() )
946 return;
947
948 if ( !wxLaunchDefaultApplication(gs_lastFile) )
949 {
950 wxLogError("Opening \"%s\" in default application failed.", gs_lastFile);
951 }
952 }
953
954 void MyFrame::OnOpenURL(wxCommandEvent& WXUNUSED(event))
955 {
956 static wxString s_url(wxT("http://www.wxwidgets.org/"));
957
958 wxString filename = wxGetTextFromUser
959 (
960 wxT("Enter the URL"),
961 wxT("exec sample"),
962 s_url,
963 this
964 );
965
966 if ( filename.empty() )
967 return;
968
969 s_url = filename;
970
971 if ( !wxLaunchDefaultBrowser(s_url) )
972 {
973 wxLogError(wxT("Failed to open URL \"%s\""), s_url.c_str());
974 }
975 }
976
977 // ----------------------------------------------------------------------------
978 // DDE stuff
979 // ----------------------------------------------------------------------------
980
981 #ifdef __WINDOWS__
982
983 bool MyFrame::GetDDEServer()
984 {
985 wxString server = wxGetTextFromUser(wxT("Server to connect to:"),
986 DIALOG_TITLE, m_server);
987 if ( !server )
988 return false;
989
990 m_server = server;
991
992 wxString topic = wxGetTextFromUser(wxT("DDE topic:"), DIALOG_TITLE, m_topic);
993 if ( !topic )
994 return false;
995
996 m_topic = topic;
997
998 wxString cmd = wxGetTextFromUser(wxT("DDE command:"), DIALOG_TITLE, m_cmdDde);
999 if ( !cmd )
1000 return false;
1001
1002 m_cmdDde = cmd;
1003
1004 return true;
1005 }
1006
1007 void MyFrame::OnDDEExec(wxCommandEvent& WXUNUSED(event))
1008 {
1009 if ( !GetDDEServer() )
1010 return;
1011
1012 wxDDEClient client;
1013 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1014 if ( !conn )
1015 {
1016 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1017 m_server.c_str());
1018 }
1019 else
1020 {
1021 if ( !conn->Execute(m_cmdDde) )
1022 {
1023 wxLogError(wxT("Failed to execute command '%s' via DDE."),
1024 m_cmdDde.c_str());
1025 }
1026 else
1027 {
1028 wxLogStatus(wxT("Successfully executed DDE command"));
1029 }
1030 }
1031 }
1032
1033 void MyFrame::OnDDERequest(wxCommandEvent& WXUNUSED(event))
1034 {
1035 if ( !GetDDEServer() )
1036 return;
1037
1038 wxDDEClient client;
1039 wxConnectionBase *conn = client.MakeConnection(wxEmptyString, m_server, m_topic);
1040 if ( !conn )
1041 {
1042 wxLogError(wxT("Failed to connect to the DDE server '%s'."),
1043 m_server.c_str());
1044 }
1045 else
1046 {
1047 if ( !conn->Request(m_cmdDde) )
1048 {
1049 wxLogError(wxT("Failed to send request '%s' via DDE."),
1050 m_cmdDde.c_str());
1051 }
1052 else
1053 {
1054 wxLogStatus(wxT("Successfully sent DDE request."));
1055 }
1056 }
1057 }
1058
1059 #endif // __WINDOWS__
1060
1061 // ----------------------------------------------------------------------------
1062 // various helpers
1063 // ----------------------------------------------------------------------------
1064
1065 // input polling
1066 void MyFrame::OnIdle(wxIdleEvent& event)
1067 {
1068 size_t count = m_running.GetCount();
1069 for ( size_t n = 0; n < count; n++ )
1070 {
1071 if ( m_running[n]->HasInput() )
1072 {
1073 event.RequestMore();
1074 }
1075 }
1076 }
1077
1078 void MyFrame::OnIdleTimer(wxTimerEvent& WXUNUSED(event))
1079 {
1080 wxWakeUpIdle();
1081 }
1082
1083 void MyFrame::OnBgTimer(wxTimerEvent& WXUNUSED(event))
1084 {
1085 static unsigned long s_ticks = 0;
1086 SetStatusText(wxString::Format("%lu ticks", s_ticks++), 1);
1087 }
1088
1089 void MyFrame::OnProcessTerminated(MyPipedProcess *process)
1090 {
1091 RemovePipedProcess(process);
1092 }
1093
1094 void MyFrame::OnAsyncTermination(MyProcess *process)
1095 {
1096 m_allAsync.Remove(process);
1097
1098 delete process;
1099 }
1100
1101 void MyFrame::AddPipedProcess(MyPipedProcess *process)
1102 {
1103 if ( m_running.IsEmpty() )
1104 {
1105 // we want to start getting the timer events to ensure that a
1106 // steady stream of idle events comes in -- otherwise we
1107 // wouldn't be able to poll the child process input
1108 m_timerIdleWakeUp.Start(100);
1109 }
1110 //else: the timer is already running
1111
1112 m_running.Add(process);
1113 m_allAsync.Add(process);
1114 }
1115
1116 void MyFrame::RemovePipedProcess(MyPipedProcess *process)
1117 {
1118 m_running.Remove(process);
1119
1120 if ( m_running.IsEmpty() )
1121 {
1122 // we don't need to get idle events all the time any more
1123 m_timerIdleWakeUp.Stop();
1124 }
1125 }
1126
1127 void MyFrame::ShowOutput(const wxString& cmd,
1128 const wxArrayString& output,
1129 const wxString& title)
1130 {
1131 size_t count = output.GetCount();
1132 if ( !count )
1133 return;
1134
1135 m_lbox->Append(wxString::Format(wxT("--- %s of '%s' ---"),
1136 title.c_str(), cmd.c_str()));
1137
1138 for ( size_t n = 0; n < count; n++ )
1139 {
1140 m_lbox->Append(output[n]);
1141 }
1142
1143 m_lbox->Append(wxString::Format(wxT("--- End of %s ---"),
1144 title.Lower().c_str()));
1145 }
1146
1147 // ----------------------------------------------------------------------------
1148 // MyProcess
1149 // ----------------------------------------------------------------------------
1150
1151 void MyProcess::OnTerminate(int pid, int status)
1152 {
1153 wxLogStatus(m_parent, wxT("Process %u ('%s') terminated with exit code %d."),
1154 pid, m_cmd.c_str(), status);
1155
1156 m_parent->OnAsyncTermination(this);
1157 }
1158
1159 // ----------------------------------------------------------------------------
1160 // MyPipedProcess
1161 // ----------------------------------------------------------------------------
1162
1163 bool MyPipedProcess::HasInput()
1164 {
1165 bool hasInput = false;
1166
1167 if ( IsInputAvailable() )
1168 {
1169 wxTextInputStream tis(*GetInputStream());
1170
1171 // this assumes that the output is always line buffered
1172 wxString msg;
1173 msg << m_cmd << wxT(" (stdout): ") << tis.ReadLine();
1174
1175 m_parent->GetLogListBox()->Append(msg);
1176
1177 hasInput = true;
1178 }
1179
1180 if ( IsErrorAvailable() )
1181 {
1182 wxTextInputStream tis(*GetErrorStream());
1183
1184 // this assumes that the output is always line buffered
1185 wxString msg;
1186 msg << m_cmd << wxT(" (stderr): ") << tis.ReadLine();
1187
1188 m_parent->GetLogListBox()->Append(msg);
1189
1190 hasInput = true;
1191 }
1192
1193 return hasInput;
1194 }
1195
1196 void MyPipedProcess::OnTerminate(int pid, int status)
1197 {
1198 // show the rest of the output
1199 while ( HasInput() )
1200 ;
1201
1202 m_parent->OnProcessTerminated(this);
1203
1204 MyProcess::OnTerminate(pid, status);
1205 }
1206
1207 // ----------------------------------------------------------------------------
1208 // MyPipedProcess2
1209 // ----------------------------------------------------------------------------
1210
1211 bool MyPipedProcess2::HasInput()
1212 {
1213 if ( !m_input.empty() )
1214 {
1215 wxTextOutputStream os(*GetOutputStream());
1216 os.WriteString(m_input);
1217
1218 CloseOutput();
1219 m_input.clear();
1220
1221 // call us once again - may be we'll have output
1222 return true;
1223 }
1224
1225 return MyPipedProcess::HasInput();
1226 }
1227
1228 // ============================================================================
1229 // MyPipeFrame implementation
1230 // ============================================================================
1231
1232 MyPipeFrame::MyPipeFrame(wxFrame *parent,
1233 const wxString& cmd,
1234 wxProcess *process)
1235 : wxFrame(parent, wxID_ANY, cmd),
1236 m_process(process),
1237 // in a real program we'd check that the streams are !NULL here
1238 m_out(*process->GetOutputStream()),
1239 m_in(*process->GetInputStream()),
1240 m_err(*process->GetErrorStream())
1241 {
1242 m_process->SetNextHandler(this);
1243
1244 wxPanel *panel = new wxPanel(this, wxID_ANY);
1245
1246 m_textOut = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1247 wxDefaultPosition, wxDefaultSize,
1248 wxTE_PROCESS_ENTER);
1249 m_textIn = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1250 wxDefaultPosition, wxDefaultSize,
1251 wxTE_MULTILINE | wxTE_RICH);
1252 m_textIn->SetEditable(false);
1253 m_textErr = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
1254 wxDefaultPosition, wxDefaultSize,
1255 wxTE_MULTILINE | wxTE_RICH);
1256 m_textErr->SetEditable(false);
1257
1258 wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
1259 sizerTop->Add(m_textOut, 0, wxGROW | wxALL, 5);
1260
1261 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1262 sizerBtns->
1263 Add(new wxButton(panel, Exec_Btn_Send, wxT("&Send")), 0, wxALL, 5);
1264 sizerBtns->
1265 Add(new wxButton(panel, Exec_Btn_SendFile, wxT("&File...")), 0, wxALL, 5);
1266 sizerBtns->
1267 Add(new wxButton(panel, Exec_Btn_Get, wxT("&Get")), 0, wxALL, 5);
1268 sizerBtns->
1269 Add(new wxButton(panel, Exec_Btn_Close, wxT("&Close")), 0, wxALL, 5);
1270
1271 sizerTop->Add(sizerBtns, 0, wxCENTRE | wxALL, 5);
1272 sizerTop->Add(m_textIn, 1, wxGROW | wxALL, 5);
1273 sizerTop->Add(m_textErr, 1, wxGROW | wxALL, 5);
1274
1275 panel->SetSizer(sizerTop);
1276 sizerTop->Fit(this);
1277
1278 Show();
1279 }
1280
1281 void MyPipeFrame::OnBtnSendFile(wxCommandEvent& WXUNUSED(event))
1282 {
1283 #if wxUSE_FILEDLG
1284 wxFileDialog filedlg(this, wxT("Select file to send"));
1285 if ( filedlg.ShowModal() != wxID_OK )
1286 return;
1287
1288 wxFFile file(filedlg.GetFilename(), wxT("r"));
1289 wxString data;
1290 if ( !file.IsOpened() || !file.ReadAll(&data) )
1291 return;
1292
1293 // can't write the entire string at once, this risk overflowing the pipe
1294 // and we would dead lock
1295 size_t len = data.length();
1296 const wxChar *pc = data.c_str();
1297 while ( len )
1298 {
1299 const size_t CHUNK_SIZE = 4096;
1300 m_out.Write(pc, len > CHUNK_SIZE ? CHUNK_SIZE : len);
1301
1302 // note that not all data could have been written as we don't block on
1303 // the write end of the pipe
1304 const size_t lenChunk = m_out.LastWrite();
1305
1306 pc += lenChunk;
1307 len -= lenChunk;
1308
1309 DoGet();
1310 }
1311 #endif // wxUSE_FILEDLG
1312 }
1313
1314 void MyPipeFrame::DoGet()
1315 {
1316 // we don't have any way to be notified when any input appears on the
1317 // stream so we have to poll it :-(
1318 DoGetFromStream(m_textIn, m_in);
1319 DoGetFromStream(m_textErr, m_err);
1320 }
1321
1322 void MyPipeFrame::DoGetFromStream(wxTextCtrl *text, wxInputStream& in)
1323 {
1324 while ( in.CanRead() )
1325 {
1326 wxChar buffer[4096];
1327 buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = wxT('\0');
1328
1329 text->AppendText(buffer);
1330 }
1331 }
1332
1333 void MyPipeFrame::DoClose()
1334 {
1335 m_process->CloseOutput();
1336
1337 DisableInput();
1338 }
1339
1340 void MyPipeFrame::DisableInput()
1341 {
1342 m_textOut->SetEditable(false);
1343 FindWindow(Exec_Btn_Send)->Disable();
1344 FindWindow(Exec_Btn_SendFile)->Disable();
1345 FindWindow(Exec_Btn_Close)->Disable();
1346 }
1347
1348 void MyPipeFrame::DisableOutput()
1349 {
1350 FindWindow(Exec_Btn_Get)->Disable();
1351 }
1352
1353 void MyPipeFrame::OnClose(wxCloseEvent& event)
1354 {
1355 if ( m_process )
1356 {
1357 // we're not interested in getting the process termination notification
1358 // if we are closing it ourselves
1359 wxProcess *process = m_process;
1360 m_process = NULL;
1361 process->SetNextHandler(NULL);
1362
1363 process->CloseOutput();
1364 }
1365
1366 event.Skip();
1367 }
1368
1369 void MyPipeFrame::OnProcessTerm(wxProcessEvent& WXUNUSED(event))
1370 {
1371 DoGet();
1372
1373 delete m_process;
1374 m_process = NULL;
1375
1376 wxLogWarning(wxT("The other process has terminated, closing"));
1377
1378 DisableInput();
1379 DisableOutput();
1380 }