MyExternalCmdExecDialog.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00025 // This class is written based on 'exec' sample of wxWidgets library.
00026 
00027 #include <config.h>
00028 #include "panoinc_WX.h"
00029 #include "panoinc.h"
00030 
00031 #include <errno.h>
00032 
00033 #include "base_wx/wxPlatform.h"
00034 
00035 #include "wx/ffile.h"
00036 #include "wx/process.h"
00037 #include "wx/mimetype.h"
00038 #include <wx/sstream.h>
00039 
00040 #ifdef _WIN32
00041     #include "wx/dde.h"
00042         #include <windows.h>
00043         #include <tlhelp32.h>   //needed to pause process on windows
00044 #else
00045         #include <sys/types.h>  
00046         #include <signal.h>     //needed to pause on unix - kill function
00047         #include <unistd.h> //needed to separate the process group of make
00048 #endif // _WIN32
00049 
00050 // Slightly reworked fix for BUG_2075064 
00051 #ifdef __WXMAC__
00052         #include <iostream>
00053         #include <stdio.h>
00054         #include "wx/utils.h"
00055 #endif
00056 
00057 #include "MyExternalCmdExecDialog.h"
00058 #include "hugin/config_defaults.h"
00059 
00060 // ----------------------------------------------------------------------------
00061 // event tables and other macros for wxWidgets
00062 // ----------------------------------------------------------------------------
00063 
00064 DEFINE_EVENT_TYPE(EVT_QUEUE_PROGRESS)
00065 
00066 BEGIN_EVENT_TABLE(MyExecPanel, wxPanel)
00067     EVT_TIMER(wxID_ANY, MyExecPanel::OnTimer)
00068 END_EVENT_TABLE()
00069 
00070 
00071 // ============================================================================
00072 // implementation
00073 // ============================================================================
00074 
00075 
00076 // frame constructor
00077 MyExecPanel::MyExecPanel(wxWindow * parent)
00078        : wxPanel(parent),
00079        m_timerIdleWakeUp(this), m_queue(NULL), m_queueLength(0), m_checkReturnCode(true)
00080 {
00081     m_pidLast = 0;
00082 
00083     wxBoxSizer * topsizer = new wxBoxSizer( wxVERTICAL );
00084     // create the listbox in which we will show misc messages as they come
00085     m_textctrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY);
00086     m_lastLineStart = 0;
00087     
00088 #ifdef __WXMAC__
00089     wxFont font(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
00090 #else
00091     wxFont font(8, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
00092 #endif
00093     
00094     if ( font.Ok() ) {
00095         m_textctrl->SetFont(font);
00096     }
00097 
00098     topsizer->Add(m_textctrl, 1, wxEXPAND | wxALL, 10);
00099     SetSizer( topsizer );
00100 }
00101 
00102 void MyExecPanel::KillProcess()
00103 {
00104     if (m_pidLast) {
00105 #ifdef __WXMSW__
00106         DEBUG_DEBUG("Killing process " << m_pidLast << " with sigkill");
00107         wxKillError rc = wxProcess::Kill(m_pidLast, wxSIGKILL, wxKILL_CHILDREN);
00108 #else
00109         DEBUG_DEBUG("Killing process " << m_pidLast << " with sigterm");
00110         wxKillError rc = wxProcess::Kill(m_pidLast, wxSIGTERM, wxKILL_CHILDREN);
00111 #endif
00112         if ( rc != wxKILL_OK ) {
00113             static const wxChar *errorText[] =
00114             {
00115                 _T(""), // no error
00116                 _T("signal not supported"),
00117                 _T("permission denied"),
00118                 _T("no such process"),
00119                 _T("unspecified error"),
00120             };
00121 
00122             wxLogError(_("Failed to kill process %ld, error %d: %s"),
00123                         m_pidLast, rc, errorText[rc]);
00124         }
00125     }
00126 }
00127 
00129 void MyExecPanel::PauseProcess(bool pause)
00130 {
00131 #ifdef __WXMSW__
00132         HANDLE hProcessSnapshot = NULL;
00133         PROCESSENTRY32 pEntry = {0};
00134         THREADENTRY32 tEntry = {0};
00135 
00136         //we take a snapshot of all system processes
00137         hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
00138 
00139         if (hProcessSnapshot == INVALID_HANDLE_VALUE)
00140                 wxLogError(_("Error pausing process %ld, code 1"),m_pidLast);
00141         else
00142         {
00143                 pEntry.dwSize = sizeof(PROCESSENTRY32);
00144                 tEntry.dwSize = sizeof(THREADENTRY32);
00145                 
00146                 //we traverse all processes in the system
00147                 if(Process32First(hProcessSnapshot, &pEntry))
00148                 {
00149                         do
00150                         {
00151                                 //we pause threads of the main (make) process and its children (nona,enblend...)
00152                                 if((pEntry.th32ProcessID == m_pidLast) || (pEntry.th32ParentProcessID == m_pidLast))
00153                                 {
00154                                         //we take a snapshot of all system threads
00155                                         HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 
00156                                         if (hThreadSnapshot == INVALID_HANDLE_VALUE)
00157                                                 wxLogError(_("Error pausing process %ld, code 2"),m_pidLast);
00158 
00159                                         //we traverse all threads
00160                                         if(Thread32First(hThreadSnapshot, &tEntry))
00161                                         {
00162                                                 do
00163                                                 {
00164                                                         //we find all threads of the process
00165                                                         if(tEntry.th32OwnerProcessID == pEntry.th32ProcessID)
00166                                                         {
00167                                                                 HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, tEntry.th32ThreadID);
00168                                                                 if(pause)
00169                                                                         SuspendThread(hThread);
00170                                                                 else
00171                                                                         ResumeThread(hThread);
00172                                                                 CloseHandle(hThread);
00173                                                         }
00174                                                 }while(Thread32Next(hThreadSnapshot, &tEntry));
00175                                         }
00176                                         CloseHandle(hThreadSnapshot);
00177                                 }
00178                         }while(Process32Next(hProcessSnapshot, &pEntry));
00179                 }
00180         }
00181         CloseHandle(hProcessSnapshot);
00182 #else
00183         //send the process group a pause/cont signal
00184         if(pause)
00185                 killpg(m_pidLast,SIGSTOP);
00186         else
00187                 killpg(m_pidLast,SIGCONT);
00188 #endif //__WXMSW__
00189 }
00190 
00191 void MyExecPanel::ContinueProcess()
00192 {
00193         PauseProcess(false);
00194 }
00195 
00196 long MyExecPanel::GetPid()
00197 {
00198         return m_pidLast;
00199 }
00200 
00201 int MyExecPanel::ExecWithRedirect(wxString cmd)
00202 {
00203     if (!cmd)
00204         return -1;
00205 
00206     // Slightly reworked fix for BUG_2075064 
00207 #if defined __WXMAC__ && defined __ppc__
00208     int osVersionMajor;
00209     int osVersionMinor;
00210 
00211     int os = wxGetOsVersion(&osVersionMajor, &osVersionMinor);
00212 
00213     cout << "osVersionCheck: os is " << os << "\n"  << endl;
00214     cout << "osVersionCheck: osVersionMajor = " << osVersionMajor << endl;
00215     cout << "osVersionCheck: osVersionMinor = " << osVersionMinor << endl;
00216     if ((osVersionMajor == 0x10) && (osVersionMinor >= 0x50))
00217     {
00218         //let the child process exit without becoming zombie
00219         //may do some harm to internal handling by wxWidgets, but hey it's not working anyway
00220         signal(SIGCHLD,SIG_IGN);
00221         cout <<  "osVersionCheck: Leopard loop 1" << endl;
00222     }
00223     else
00224     {
00225         cout <<  "osVersionCheck: Tiger loop 1" << endl;
00226     }   
00227 #endif
00228 
00229     MyPipedProcess *process = new MyPipedProcess(this, cmd);
00230     m_pidLast = wxExecute(cmd, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER, process, &m_executeEnv);
00231     if ( m_pidLast == 0 )
00232     {
00233         wxLogError(_T("Execution of '%s' failed."), cmd.c_str());
00234         delete process;
00235         return -1;
00236     }
00237     else
00238     {
00239         AddAsyncProcess(process);
00240 #ifndef _WIN32
00241                 //on linux we put the new process into a separate group, 
00242                 //so it can be paused with all it's children at the same time
00243                 setpgid(m_pidLast,m_pidLast);   
00244 #endif
00245     }
00246     return 0;   
00247 }
00248 
00249 int MyExecPanel::ExecQueue(HuginQueue::CommandQueue* queue)
00250 {
00251     wxConfigBase* config = wxConfigBase::Get();
00252     const long threads = config->Read(wxT("/output/NumberOfThreads"), 0l);
00253     if (threads > 0)
00254     {
00255         wxString s;
00256         s << threads;
00257         m_executeEnv.env["OMP_NUM_THREADS"] = s;
00258     };
00259     // set temp dir
00260     wxString tempDir = config->Read(wxT("tempDir"), wxT(""));
00261     if (!tempDir.IsEmpty())
00262     {
00263 #ifdef UNIX_LIKE
00264         m_executeEnv.env["TMPDIR"] = tempDir;
00265 #else
00266         m_executeEnv.env["TMP"] = tempDir;
00267 #endif
00268     };
00269     m_queue = queue;
00270     m_queueLength = queue->size() + 1;
00271     if (m_queue->empty())
00272     {
00273         return 0;
00274     };
00275     return ExecNextQueue();
00276 };
00277 
00279 int MyExecPanel::ExecNextQueue()
00280 {
00281     if (m_queue)
00282     {
00283         // get next command
00284         HuginQueue::NormalCommand* cmd = m_queue->front();
00285         const wxString cmdString = cmd->GetCommand();
00286         // get comment, append line break and display comment in panel
00287         AddString(cmd->GetComment());
00288         m_checkReturnCode = cmd->CheckReturnCode();
00289         // delete command from queue
00290         delete cmd;
00291         m_queue->erase(m_queue->begin());
00292         // notify parent
00293         if (this->GetParent())
00294         {
00295             wxCommandEvent event(EVT_QUEUE_PROGRESS, wxID_ANY);
00296             event.SetInt(hugin_utils::roundi((m_queueLength - m_queue->size()) * 100.0f / m_queueLength));
00297             this->GetParent()->GetEventHandler()->AddPendingEvent(event);
00298         };
00299         // now execute command
00300         return ExecWithRedirect(cmdString);
00301     }
00302     else
00303     {
00304         return -1;
00305     };
00306 };
00307 
00308 void MyExecPanel::AddAsyncProcess(MyPipedProcess *process)
00309 {
00310     if ( m_running.IsEmpty() )
00311     {
00312         // we want to start getting the timer events to ensure that a
00313         // steady stream of idle events comes in -- otherwise we
00314         // wouldn't be able to poll the child process input
00315         m_timerIdleWakeUp.Start(200);
00316     }
00317     //else: the timer is already running
00318 
00319     m_running.Add(process);
00320 }
00321 
00322 
00323 void MyExecPanel::RemoveAsyncProcess(MyPipedProcess *process)
00324 {
00325     m_running.Remove(process);
00326 
00327     if ( m_running.IsEmpty() )
00328     {
00329         // we don't need to get idle events all the time any more
00330         m_timerIdleWakeUp.Stop();
00331     }
00332 }
00333 
00334 
00335 // ----------------------------------------------------------------------------
00336 // various helpers
00337 // ----------------------------------------------------------------------------
00338 
00339 void MyExecPanel::AddToOutput(wxInputStream & s)
00340 {
00341     DEBUG_TRACE("");
00342 #if defined __WXGTK__
00343     wxTextInputStream ts(s, " \t", wxConvLibc);
00344 #else
00345     wxTextInputStream ts(s);
00346 #endif
00347     bool lastCR= false;
00348     wxString currLine = m_textctrl->GetRange(m_lastLineStart, m_textctrl->GetLastPosition());
00349     while(s.CanRead()) {
00350         wxChar c = ts.GetChar();
00351         if (c == '\b') {
00352             lastCR=false;
00353             // backspace
00354             if (currLine.size() > 0) {
00355                 if (currLine.Last() != wxChar('\n') )
00356                     currLine.Trim();
00357             }
00358         } else if (c == 0x0d) {
00359             lastCR=true;
00360 #ifndef __WXMSW__
00361             // back to start of line
00362              if (currLine.Last() != wxChar('\n') ) {
00363                 currLine = currLine.BeforeLast('\n');
00364                 if(currLine.size() > 0) {
00365                     currLine.Append('\n');
00366                 }
00367              }
00368 #endif
00369         } else if (c == '\n') {
00370             currLine.Append(c);
00371             lastCR=false;
00372         } else {
00373 #ifdef __WXMSW__
00374             if (lastCR) {
00375             // back to start of line
00376                 if (currLine.Last() != wxChar('\n') ) {
00377                     currLine = currLine.BeforeLast('\n');
00378                     if(currLine.size() > 0) {
00379                         currLine.Append('\n');
00380                     }
00381                 }
00382             }
00383 #endif
00384             currLine.Append(c);
00385             lastCR=false;
00386         }
00387     }
00388 
00389     m_textctrl->Replace(m_lastLineStart, m_textctrl->GetLastPosition(), currLine);
00390     size_t lret = currLine.find_last_of(wxChar('\n'));
00391     if (lret > 0 && lret+1 < currLine.size()) {
00392         m_lastLineStart += lret+1;
00393     }
00394 }
00395 
00396 void MyExecPanel::OnTimer(wxTimerEvent& WXUNUSED(event))
00397 {
00398     size_t count = m_running.GetCount();
00399     for ( size_t n = 0; n < count; n++ )
00400     {
00401         while ( m_running[n]->IsInputAvailable() )
00402         {
00403             AddToOutput(*(m_running[n]->GetInputStream()));
00404         };
00405         while ( m_running[n]->IsErrorAvailable() )
00406         {
00407             AddToOutput(*(m_running[n]->GetErrorStream()));
00408         };
00409     };
00410 
00411 // Slightly reworked fix for BUG_2075064
00412 #if defined __WXMAC__ && defined __ppc__
00413         int osVersionMajor;
00414         int osVersionMinor;
00415         
00416         int os = wxGetOsVersion(&osVersionMajor, &osVersionMinor);
00417         
00418         cerr << "osVersionCheck: os is " << os << "\n"  << endl;
00419         cerr << "osVersionCheck: osVersionMajor = " << osVersionMajor << endl;
00420         cerr << "osVersionCheck: osVersionMinor = " << osVersionMinor << endl;
00421 
00422         if ((osVersionMajor == 0x10) && (osVersionMinor >= 0x50))
00423     {   
00424                 cerr <<  "osVersionCheck: Leopard loop 2" << endl;      
00425                 if(m_pidLast)
00426                 {
00427                         if(kill((pid_t)m_pidLast,0)!=0) //if not pid exists
00428                         {
00429                                 DEBUG_DEBUG("Found terminated process: " << (pid_t)m_pidLast)
00430             
00431                                 // probably should clean up the wxProcess object which was newed when the process was launched.
00432                                 // for now, nevermind the tiny memory leak... it's a hack to workaround the bug anyway
00433             
00434                                 //notify dialog that it's finished.
00435                                 if (this->GetParent()) {
00436                                         wxProcessEvent event( wxID_ANY, m_pidLast, 0); // assume 0 exit code
00437                                         event.SetEventObject( this );
00438                                         DEBUG_TRACE("Sending wxProcess event");   
00439                                         this->GetParent()->ProcessEvent( event );
00440                                 }
00441                         }
00442                 }
00443         }
00444         else
00445         {
00446                 cerr <<  "osVersionCheck: Tiger loop 2" << endl;
00447         }
00448 #endif
00449 }
00450 
00451 void MyExecPanel::OnProcessTerminated(MyPipedProcess *process, int pid, int status)
00452 {
00453     DEBUG_TRACE("process terminated: pid " << pid << " exit code:" << status);
00454     // show the rest of the output
00455     AddToOutput(*(process->GetInputStream()));
00456     AddToOutput(*(process->GetErrorStream()));
00457 
00458     RemoveAsyncProcess(process);
00459 
00460     if (m_queue && !m_queue->empty())
00461     {
00462         // queue has further commands
00463         // should we check the exit code?
00464         if ((m_checkReturnCode && status == 0) || (!m_checkReturnCode))
00465         {
00466             if (ExecNextQueue() == 0)
00467             {
00468                 return;
00469             };
00470         };
00471     };
00472     // send termination to parent
00473     if (this->GetParent())
00474     {
00475         wxProcessEvent event(wxID_ANY, pid, m_checkReturnCode ? status : 0);
00476         event.SetEventObject(this);
00477         DEBUG_TRACE("Sending wxProcess event");
00478         this->GetParent()->GetEventHandler()->AddPendingEvent(event);
00479         // notify parent to hide progress 
00480         wxCommandEvent event2(EVT_QUEUE_PROGRESS, wxID_ANY);
00481         event2.SetInt(-1);
00482         this->GetParent()->GetEventHandler()->AddPendingEvent(event2);
00483     };
00484 }
00485 
00486 MyExecPanel::~MyExecPanel()
00487 {
00488     delete m_textctrl;
00489 }
00490 
00491 bool MyExecPanel::SaveLog(const wxString &filename)
00492 {
00493     return m_textctrl->SaveFile(filename);
00494 };
00495 
00496 void MyExecPanel::CopyLogToClipboard()
00497 {
00498     m_textctrl->SelectAll();
00499     m_textctrl->Copy();
00500 };
00501 
00502 void MyExecPanel::AddString(const wxString& s)
00503 {
00504     if (!s.IsEmpty())
00505     {
00506         m_textctrl->AppendText(s + wxT("\n"));
00507         m_lastLineStart = m_textctrl->GetLastPosition();
00508     };
00509 };
00510 
00511 // ----------------------------------------------------------------------------
00512 // MyPipedProcess
00513 // ----------------------------------------------------------------------------
00514 
00515 void MyPipedProcess::OnTerminate(int pid, int status)
00516 {
00517     DEBUG_DEBUG("Process " << pid << " terminated with return code: " << status);
00518     m_parent->OnProcessTerminated(this, pid, status);
00519 
00520     // we're not needed any more
00521     delete this;
00522 }
00523 
00524 // ==============================================================================
00525 // MyExecDialog
00526 
00527 BEGIN_EVENT_TABLE(MyExecDialog, wxDialog)
00528     EVT_BUTTON(wxID_CANCEL,  MyExecDialog::OnCancel)
00529     EVT_END_PROCESS(wxID_ANY, MyExecDialog::OnProcessTerminate)
00530 END_EVENT_TABLE()
00531 
00532 MyExecDialog::MyExecDialog(wxWindow * parent, const wxString& title, const wxPoint& pos, const wxSize& size)
00533     : wxDialog(parent, wxID_ANY, title, pos, size, wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX | wxSYSTEM_MENU)
00534 {
00535 
00536     wxBoxSizer * topsizer = new wxBoxSizer( wxVERTICAL );
00537     m_execPanel = new MyExecPanel(this);
00538     m_cancelled = false;
00539     
00540     topsizer->Add(m_execPanel, 1, wxEXPAND | wxALL, 2);
00541 
00542     topsizer->Add( new wxButton(this, wxID_CANCEL, _("Cancel")),
00543                    0, wxALL | wxALIGN_RIGHT, 10);
00544 
00545 #ifdef __WXMSW__
00546     // wxFrame does have a strange background color on Windows..
00547     this->SetBackgroundColour(m_execPanel->GetBackgroundColour());
00548 #endif
00549     SetSizer( topsizer );
00550 //    topsizer->SetSizeHints( this );
00551 }
00552 
00553 void MyExecDialog::OnProcessTerminate(wxProcessEvent & event)
00554 {
00555     DEBUG_DEBUG("Process terminated with return code: " << event.GetExitCode());
00556     if(wxConfigBase::Get()->Read(wxT("CopyLogToClipboard"), 0l)==1l)
00557     {
00558         m_execPanel->CopyLogToClipboard();
00559     };
00560     if (m_cancelled) {
00561         EndModal(HUGIN_EXIT_CODE_CANCELLED);
00562     } else {
00563         EndModal(event.GetExitCode());
00564     }
00565 }
00566 
00567 void MyExecDialog::OnCancel(wxCommandEvent& WXUNUSED(event))
00568 {
00569     DEBUG_DEBUG("Cancel Pressed");
00570     m_cancelled = true;
00571     m_execPanel->KillProcess();
00572 }
00573 
00574 
00575 int MyExecDialog::ExecWithRedirect(wxString cmd)
00576 {
00577     if (m_execPanel->ExecWithRedirect(cmd) == -1) {
00578         return -1;
00579     }
00580 
00581     return ShowModal();
00582 }
00583 
00584 int MyExecDialog::ExecQueue(HuginQueue::CommandQueue* queue)
00585 {
00586     if (m_execPanel->ExecQueue(queue) == -1)
00587     {
00588         return -1;
00589     }
00590     return ShowModal();
00591 }
00592 
00593 void MyExecDialog::AddString(const wxString& s)
00594 {
00595     m_execPanel->AddString(s);
00596 };
00597 
00598 MyExecDialog::~MyExecDialog() {
00599     delete m_execPanel;
00600 }
00601 
00602 int MyExecuteCommandOnDialog(wxString command, wxString args, wxWindow* parent,
00603                              wxString title, bool isQuoted)
00604 {
00605     if(!isQuoted)
00606     {
00607         command = hugin_utils::wxQuoteFilename(command);
00608     };
00609     wxString cmdline = command + wxT(" ") + args;
00610     MyExecDialog dlg(parent, title,
00611                      wxDefaultPosition, wxSize(640, 400));
00612 #ifdef __WXMAC__
00613     dlg.CentreOnParent();
00614 #endif
00615     return dlg.ExecWithRedirect(cmdline);
00616 }
00617 
00618 int MyExecuteCommandQueue(HuginQueue::CommandQueue* queue, wxWindow* parent, const wxString& title, const wxString& comment)
00619 {
00620     MyExecDialog dlg(parent, title, wxDefaultPosition, wxSize(640, 400));
00621 #ifdef __WXMAC__
00622     dlg.CentreOnParent();
00623 #endif
00624     if (!comment.IsEmpty())
00625     {
00626         dlg.AddString(comment);
00627     };
00628     int returnValue = dlg.ExecQueue(queue);
00629     while (!queue->empty())
00630     {
00631         delete queue->back();
00632         queue->pop_back();
00633     };
00634     delete queue;
00635     return returnValue;
00636 };
00637 

Generated on 25 Sep 2016 for Hugintrunk by  doxygen 1.4.7