Hugin trunk 0.1
Loading...
Searching...
No Matches
MyExternalCmdExecDialog.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
25// This class is written based on 'exec' sample of wxWidgets library.
26
27#include "hugin_config.h"
28#include "panoinc_WX.h"
29#include "panoinc.h"
30
31#include <errno.h>
32
33#include "base_wx/wxPlatform.h"
34
35#include "wx/ffile.h"
36#include "wx/process.h"
37#include "wx/mimetype.h"
38#include <wx/sstream.h>
39#include <wx/tokenzr.h>
40
41#ifdef _WIN32
42 #include "wx/dde.h"
43 #include <windows.h>
44 #include <tlhelp32.h> //needed to pause process on windows
45#else
46 #include <sys/types.h>
47 #include <signal.h> //needed to pause on unix - kill function
48 #include <unistd.h> //needed to separate the process group of make
49#endif // _WIN32
50
51// Slightly reworked fix for BUG_2075064
52#ifdef __WXMAC__
53 #include <iostream>
54 #include <stdio.h>
55 #include "wx/utils.h"
56#endif
57
60
61// ----------------------------------------------------------------------------
62// event tables and other macros for wxWidgets
63// ----------------------------------------------------------------------------
64
66
67// ============================================================================
68// implementation
69// ============================================================================
70
71
72// frame constructor
74 : wxPanel(parent),
75 m_timerIdleWakeUp(this), m_queue(nullptr), m_queueLength(0), m_checkReturnCode(true)
76{
77 m_pidLast = 0;
78
80 // create the listbox in which we will show misc messages as they come
83
85 if ( font.Ok() ) {
86 m_textctrl->SetFont(font);
87 }
88
89 topsizer->Add(m_textctrl, 1, wxEXPAND | wxALL, 10);
92 m_stopWatch.Start();
93}
94
96{
97 if (m_pidLast) {
98#ifdef __WXMSW__
99 DEBUG_DEBUG("Killing process " << m_pidLast << " with sigkill");
100 wxKillError rc = wxProcess::Kill(m_pidLast, wxSIGKILL, wxKILL_CHILDREN);
101#else
102 DEBUG_DEBUG("Killing process " << m_pidLast << " with sigterm");
103 wxKillError rc = wxProcess::Kill(m_pidLast, wxSIGTERM, wxKILL_CHILDREN);
104#endif
105 if ( rc != wxKILL_OK ) {
106 static const wxChar *errorText[] =
107 {
108 _T(""), // no error
109 _T("signal not supported"),
110 _T("permission denied"),
111 _T("no such process"),
112 _T("unspecified error"),
113 };
114
115 wxLogError(_("Failed to kill process %ld, error %d: %s"),
116 m_pidLast, rc, errorText[rc]);
117 }
118 }
119}
120
123{
124#ifdef __WXMSW__
127 THREADENTRY32 tEntry = {0};
128
129 //we take a snapshot of all system processes
131
133 wxLogError(_("Error pausing process %ld, code 1"),m_pidLast);
134 else
135 {
136 pEntry.dwSize = sizeof(PROCESSENTRY32);
137 tEntry.dwSize = sizeof(THREADENTRY32);
138
139 //we traverse all processes in the system
141 {
142 do
143 {
144 //we pause threads of the main (make) process and its children (nona,enblend...)
145 if((pEntry.th32ProcessID == m_pidLast) || (pEntry.th32ParentProcessID == m_pidLast))
146 {
147 //we take a snapshot of all system threads
150 wxLogError(_("Error pausing process %ld, code 2"),m_pidLast);
151
152 //we traverse all threads
154 {
155 do
156 {
157 //we find all threads of the process
158 if(tEntry.th32OwnerProcessID == pEntry.th32ProcessID)
159 {
161 if(pause)
163 else
166 }
168 }
170 }
172 }
173 }
175#else
176 //send the process group a pause/cont signal
177 if(pause)
179 else
181#endif //__WXMSW__
182}
183
185{
186 PauseProcess(false);
187}
188
190{
191 return m_pidLast;
192}
193
195{
196 if (!cmd)
197 return -1;
198
199 // Slightly reworked fix for BUG_2075064
200#if defined __WXMAC__ && defined __ppc__
201 int osVersionMajor;
202 int osVersionMinor;
203
205
206 cout << "osVersionCheck: os is " << os << "\n" << endl;
207 cout << "osVersionCheck: osVersionMajor = " << osVersionMajor << endl;
208 cout << "osVersionCheck: osVersionMinor = " << osVersionMinor << endl;
209 if ((osVersionMajor == 0x10) && (osVersionMinor >= 0x50))
210 {
211 //let the child process exit without becoming zombie
212 //may do some harm to internal handling by wxWidgets, but hey it's not working anyway
214 cout << "osVersionCheck: Leopard loop 1" << endl;
215 }
216 else
217 {
218 cout << "osVersionCheck: Tiger loop 1" << endl;
219 }
220#endif
221
224 if ( m_pidLast == 0 )
225 {
226 wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
227 delete process;
228 return -1;
229 }
230 else
231 {
233#ifndef _WIN32
234 //on linux we put the new process into a separate group,
235 //so it can be paused with all it's children at the same time
237#endif
238 }
239 return 0;
240}
241
243{
244 wxConfigBase* config = wxConfigBase::Get();
245 const long threads = config->Read("/output/NumberOfThreads", 0l);
246 // read all current environment variables
248 // now modify some variables before passing them to wxExecute
249 if (threads > 0)
250 {
251 wxString s;
252 s << threads;
253 m_executeEnv.env["OMP_NUM_THREADS"] = s;
254 };
255 // set temp dir
256 wxString tempDir = config->Read("tempDir", wxEmptyString);
257 if (!tempDir.IsEmpty())
258 {
259#ifdef UNIX_LIKE
260 m_executeEnv.env["TMPDIR"] = tempDir;
261#else
262 m_executeEnv.env["TMP"] = tempDir;
263#endif
264 };
265 m_queue = queue;
266 m_queueLength = queue->size() + 1;
267 if (m_queue->empty())
268 {
269 return 0;
270 };
271 m_stopWatch.Start();
272 return ExecNextQueue();
273};
274
277{
278 if (m_queue)
279 {
280 // get next command
282 const wxString cmdString = cmd->GetCommand();
283 // get comment, append line break and display comment in panel
284 AddString(cmd->GetComment());
285 m_checkReturnCode = cmd->CheckReturnCode();
286 // delete command from queue
287 delete cmd;
288 m_queue->erase(m_queue->begin());
289 // notify parent
290 if (this->GetParent())
291 {
292 wxCommandEvent event(EVT_QUEUE_PROGRESS, wxID_ANY);
293 event.SetInt(hugin_utils::roundi((m_queueLength - m_queue->size()) * 100.0f / m_queueLength));
294 this->GetParent()->GetEventHandler()->AddPendingEvent(event);
295 };
296 // now execute command
298 }
299 else
300 {
301 return -1;
302 };
303};
304
306{
307 if ( m_running.IsEmpty() )
308 {
309 // we want to start getting the timer events to ensure that a
310 // steady stream of idle events comes in -- otherwise we
311 // wouldn't be able to poll the child process input
312 m_timerIdleWakeUp.Start(200);
313 }
314 //else: the timer is already running
315
316 m_running.Add(process);
317}
318
319
321{
322 m_running.Remove(process);
323
324 if ( m_running.IsEmpty() )
325 {
326 // we don't need to get idle events all the time any more
327 m_timerIdleWakeUp.Stop();
328 }
329}
330
331
332// ----------------------------------------------------------------------------
333// various helpers
334// ----------------------------------------------------------------------------
335
337{
338 DEBUG_TRACE("");
339#if defined __WXGTK__
341#else
343#endif
344 bool lastCR= false;
345 wxString currLine = m_textctrl->GetRange(m_lastLineStart, m_textctrl->GetLastPosition());
346 while(s.CanRead()) {
347 wxChar c = ts.GetChar();
348 if (c == '\b') {
349 lastCR=false;
350 // backspace
351 if (!currLine.empty()) {
352 if (currLine.Last() != wxChar('\n') )
353 currLine.Trim();
354 }
355 } else if (c == 0x0) {
356 // ignore 0x0 chars
357 continue;
358 } else if (c == 0x0d) {
359 lastCR=true;
360#ifndef __WXMSW__
361 // back to start of line
362 if (currLine.Last() != wxChar('\n') ) {
363 currLine = currLine.BeforeLast('\n');
364 if(!currLine.empty()) {
365 currLine.Append('\n');
366 }
367 }
368#endif
369 } else if (c == '\n') {
370 currLine.Append(c);
371 lastCR=false;
372 } else {
373#ifdef __WXMSW__
374 if (lastCR) {
375 // back to start of line
376 if (currLine.Last() != wxChar('\n') ) {
377 currLine = currLine.BeforeLast('\n');
378 if(!currLine.empty()) {
379 currLine.Append('\n');
380 }
381 }
382 }
383#endif
384 currLine.Append(c);
385 lastCR=false;
386 }
387 }
388
389 m_textctrl->Replace(m_lastLineStart, m_textctrl->GetLastPosition(), currLine);
390 size_t lret = currLine.find_last_of(wxChar('\n'));
391 if (lret != wxString::npos && lret>0 && lret+1 < currLine.size()) {
393 }
394}
395
397{
398 size_t count = m_running.GetCount();
399 for ( size_t n = 0; n < count; n++ )
400 {
401 while ( m_running[n]->IsInputAvailable() )
402 {
404 };
405 while ( m_running[n]->IsErrorAvailable() )
406 {
408 };
409 };
410
411// Slightly reworked fix for BUG_2075064
412#if defined __WXMAC__ && defined __ppc__
413 int osVersionMajor;
414 int osVersionMinor;
415
417
418 cerr << "osVersionCheck: os is " << os << "\n" << endl;
419 cerr << "osVersionCheck: osVersionMajor = " << osVersionMajor << endl;
420 cerr << "osVersionCheck: osVersionMinor = " << osVersionMinor << endl;
421
422 if ((osVersionMajor == 0x10) && (osVersionMinor >= 0x50))
423 {
424 cerr << "osVersionCheck: Leopard loop 2" << endl;
425 if(m_pidLast)
426 {
427 if(kill((pid_t)m_pidLast,0)!=0) //if not pid exists
428 {
429 DEBUG_DEBUG("Found terminated process: " << (pid_t)m_pidLast)
430
431 // probably should clean up the wxProcess object which was newed when the process was launched.
432 // for now, nevermind the tiny memory leak... it's a hack to workaround the bug anyway
433
434 //notify dialog that it's finished.
435 if (this->GetParent()) {
436 wxProcessEvent event( wxID_ANY, m_pidLast, 0); // assume 0 exit code
437 event.SetEventObject( this );
438 DEBUG_TRACE("Sending wxProcess event");
439 this->GetParent()->ProcessEvent( event );
440 }
441 }
442 }
443 }
444 else
445 {
446 cerr << "osVersionCheck: Tiger loop 2" << endl;
447 }
448#endif
449}
450
452{
453 DEBUG_TRACE("process terminated: pid " << pid << " exit code:" << status);
454 // show the rest of the output
455 AddToOutput(*(process->GetInputStream()));
456 AddToOutput(*(process->GetErrorStream()));
457
459
460 if (m_queue && !m_queue->empty())
461 {
462 // queue has further commands
463 // should we check the exit code?
464 if ((m_checkReturnCode && status == 0) || (!m_checkReturnCode))
465 {
466 if (ExecNextQueue() == 0)
467 {
468 return;
469 };
470 };
471 };
472 {
473 // add elapsed time
474 const long duration = hugin_utils::roundi(m_stopWatch.Time() / 1000.0);
475 if (duration >= 60)
476 {
477 std::ldiv_t result=std::ldiv(duration, 60l);
478 *m_textctrl << "\n" << wxString::Format(_("Process took %ld:%2ld min"), result.quot, result.rem) << "\n";
479 }
480 else
481 {
482 *m_textctrl << "\n" << wxString::Format(_("Process took %ld s"), duration) << "\n";
483 };
484 };
485 // send termination to parent
486 if (this->GetParent())
487 {
489 event.SetEventObject(this);
490 DEBUG_TRACE("Sending wxProcess event");
491 this->GetParent()->GetEventHandler()->AddPendingEvent(event);
492 // notify parent to hide progress
493 wxCommandEvent event2(EVT_QUEUE_PROGRESS, wxID_ANY);
494 event2.SetInt(-1);
495 this->GetParent()->GetEventHandler()->AddPendingEvent(event2);
496 };
497}
498
500{
501 delete m_textctrl;
502 if (m_queue)
503 {
504 while (!m_queue->empty())
505 {
506 delete m_queue->back();
507 m_queue->pop_back();
508 };
509 delete m_queue;
510 };
511}
512
513bool MyExecPanel::SaveLog(const wxString &filename)
514{
515 return m_textctrl->SaveFile(filename);
516};
517
519{
520 m_textctrl->SelectAll();
521 m_textctrl->Copy();
522};
523
525{
526 return wxStringTokenize(m_textctrl->GetValue(), "\r\n");
527};
528
530{
531 if (!s.IsEmpty())
532 {
533 m_textctrl->AppendText(s + "\n");
534 m_lastLineStart = m_textctrl->GetLastPosition();
535 };
536};
537
539{
540 m_textctrl->Clear();
541 m_lastLineStart = 0;
542 m_stopWatch.Start();
543}
544
545// ----------------------------------------------------------------------------
546// MyPipedProcess
547// ----------------------------------------------------------------------------
548
549void MyPipedProcess::OnTerminate(int pid, int status)
550{
551 DEBUG_DEBUG("Process " << pid << " terminated with return code: " << status);
552 m_parent->OnProcessTerminated(this, pid, status);
553
554 // we're not needed any more
555 delete this;
556}
557
558// ==============================================================================
559// MyExecDialog
560MyExecDialog::MyExecDialog(wxWindow * parent, const wxString& title, const wxPoint& pos, const wxSize& size)
562{
563
565 m_execPanel = new MyExecPanel(this);
566 m_cancelled = false;
567
568 topsizer->Add(m_execPanel, 1, wxEXPAND | wxALL, 2);
569
570 topsizer->Add( new wxButton(this, wxID_CANCEL, _("Cancel")),
571 0, wxALL | wxALIGN_RIGHT, 10);
572
574// topsizer->SetSizeHints( this );
577}
578
580{
581 DEBUG_DEBUG("Process terminated with return code: " << event.GetExitCode());
582 if(wxConfigBase::Get()->Read("CopyLogToClipboard", 0l)==1l)
583 {
585 };
586 if (m_cancelled) {
588 } else {
589 EndModal(event.GetExitCode());
590 }
591}
592
594{
595 DEBUG_DEBUG("Cancel Pressed");
596 m_cancelled = true;
598}
599
600
602{
603 if (m_execPanel->ExecWithRedirect(cmd) == -1) {
604 return -1;
605 }
606
607 return ShowModal();
608}
609
611{
612 if (m_execPanel->ExecQueue(queue) == -1)
613 {
614 return -1;
615 }
616 return ShowModal();
617}
618
623
627
629 wxString title, bool isQuoted)
630{
631 if(!isQuoted)
632 {
634 };
635 wxString cmdline = command + " " + args;
637 wxDefaultPosition, wxSize(640, 400));
638#ifdef __WXMAC__
639 dlg.CentreOnParent();
640#endif
641 return dlg.ExecWithRedirect(cmdline);
642}
643
645{
647#ifdef __WXMAC__
648 dlg.CentreOnParent();
649#endif
650 if (!comment.IsEmpty())
651 {
652 dlg.AddString(comment);
653 };
654 int returnValue = dlg.ExecQueue(queue);
655 return returnValue;
656};
657
wxDEFINE_EVENT(EVT_QUEUE_PROGRESS, wxCommandEvent)
int MyExecuteCommandQueue(HuginQueue::CommandQueue *queue, wxWindow *parent, const wxString &title, const wxString &comment)
execute all commands in queue with redirection of output to frame and allow canceling the queue will ...
int MyExecuteCommandOnDialog(wxString command, wxString args, wxWindow *parent, wxString title, bool isQuoted)
execute a single command in own dialog, redirect output to frame and allow canceling
const int HUGIN_EXIT_CODE_CANCELLED
normal command for queue, processing is stopped if an error occurred in program
Definition Executor.h:38
virtual wxString GetCommand() const
Definition Executor.cpp:60
MyExecPanel * m_execPanel
void AddString(const wxString &s)
display the string in the panel
void OnProcessTerminate(wxProcessEvent &event)
void OnCancel(wxCommandEvent &event)
int ExecQueue(HuginQueue::CommandQueue *queue)
MyExecDialog(wxWindow *parent, const wxString &title, const wxPoint &pos, const wxSize &size)
int ExecWithRedirect(wxString command)
void OnProcessTerminated(MyPipedProcess *process, int pid, int status)
MyExecPanel(wxWindow *parent)
void AddToOutput(wxInputStream &s)
void CopyLogToClipboard()
copy the content of the log window into the clipboard
int ExecNextQueue()
execute next command in queue
int ExecQueue(HuginQueue::CommandQueue *queue)
void AddString(const wxString &s)
display the string in the panel
MyProcessesArray m_running
void RemoveAsyncProcess(MyPipedProcess *process)
int ExecWithRedirect(wxString command)
wxArrayString GetLogAsArrayString()
returns the output
void PauseProcess(bool pause=true)
function to pause running process, argument pause defaults to true - to resume, set it to false
bool SaveLog(const wxString &filename)
save the content of the window into a given log file
wxExecuteEnv m_executeEnv
HuginQueue::CommandQueue * m_queue
void AddAsyncProcess(MyPipedProcess *process)
void ClearOutput()
clear the output
void OnTimer(wxTimerEvent &event)
virtual void OnTerminate(int pid, int status)
MyProcessListener * m_parent
virtual void OnProcessTerminated(MyPipedProcess *process, int pid, int status)=0
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_TRACE(msg)
Definition utils.h:67
std::vector< NormalCommand * > CommandQueue
Definition Executor.h:61
str wxQuoteFilename(const str &arg)
Quote a filename, so that it is surrounded by "".
Definition wxPlatform.h:82
int roundi(T x)
Definition hugin_math.h:73
include file for the hugin project
include file for the hugin project
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
platform/compiler specific stuff.