KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_export_step_process.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * Copyright (C) 2020 New Pagodi(https://stackoverflow.com/users/6846682/new-pagodi)
6 * from https://stackoverflow.com/a/63289812/1522001
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <wx/textctrl.h>
28#include <wx/process.h>
29#include <wx/log.h>
30#include <wx/timer.h>
31#include <wx/txtstrm.h>
32#include <wx/msgdlg.h>
33
34wxDEFINE_EVENT( wxEVT_THREAD_STDIN, wxThreadEvent );
35wxDEFINE_EVENT( wxEVT_THREAD_STDERR, wxThreadEvent );
36
41class STDSTREAM_THREAD : public wxThread
42{
43public:
44 STDSTREAM_THREAD( wxEvtHandler* aEventHandler, wxProcess* aProcess,
45 wxMessageQueue<DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE>& aMsgQueue ) :
46 wxThread( wxTHREAD_JOINABLE ),
47 m_queue( aMsgQueue )
48 {
49 m_process = aProcess;
50 m_handler = aEventHandler;
51 m_bufferSize = 1024 * 1024;
52 m_buffer = new char[m_bufferSize];
53 }
54
56 {
57 delete[] m_buffer;
58 }
59
60private:
61 ExitCode Entry() override;
62 void DrainInput();
63
64 wxMessageQueue<DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE>& m_queue;
65 wxEvtHandler* m_handler;
66 wxProcess* m_process;
67 char* m_buffer;
69};
70
71
72wxThread::ExitCode STDSTREAM_THREAD::Entry()
73{
74 ExitCode c;
75
76 while( 1 )
77 {
78 // Check if termination was requested.
79 if( TestDestroy() )
80 {
81 wxProcess::Kill( m_process->GetPid(), wxSIGKILL );
82 c = reinterpret_cast<ExitCode>( 1 );
83 break;
84 }
85
87 wxMessageQueueError e = m_queue.ReceiveTimeout( 10, m );
88
89 // Check if a message was received or we timed out.
90 if( e == wxMSGQUEUE_NO_ERROR )
91 {
93 {
94 DrainInput();
95 c = reinterpret_cast<ExitCode>( 0 );
96 break;
97 }
99 {
100 wxProcess::Kill( m_process->GetPid(), wxSIGKILL );
101 c = reinterpret_cast<ExitCode>( 1 );
102 break;
103 }
104 }
105 else if( e == wxMSGQUEUE_TIMEOUT )
106 {
107 DrainInput();
108 }
109 }
110
111 return c;
112}
113
114
116{
117 if( !m_process->IsInputOpened() )
118 {
119 return;
120 }
121
122 wxString fromInputStream, fromErrorStream;
123 wxInputStream* stream;
124
125 while( m_process->IsInputAvailable() )
126 {
127 stream = m_process->GetInputStream();
128 stream->Read( m_buffer, m_bufferSize );
129 fromInputStream << wxString( m_buffer, stream->LastRead() );
130 }
131
132 while( m_process->IsErrorAvailable() )
133 {
134 stream = m_process->GetErrorStream();
135 stream->Read( m_buffer, m_bufferSize );
136 fromErrorStream << wxString( m_buffer, stream->LastRead() );
137 }
138
139 if( !fromInputStream.IsEmpty() )
140 {
141 wxThreadEvent* event = new wxThreadEvent( wxEVT_THREAD_STDIN );
142 event->SetString( fromInputStream );
143 m_handler->QueueEvent( event );
144 }
145
146 if( !fromErrorStream.IsEmpty() )
147 {
148 wxThreadEvent* event = new wxThreadEvent( wxEVT_THREAD_STDERR );
149 event->SetString( fromErrorStream );
150 m_handler->QueueEvent( event );
151 }
152}
153
154
156{
157 // Ensure we start with a clean log window message
158 m_textCtrlLog->Clear();
159 m_textCtrlLog->AppendText( m_startMessage );
160 return true;
161}
162
163
164void DIALOG_EXPORT_STEP_LOG::appendMessage( const wxString& aMessage )
165{
166 m_textCtrlLog->AppendText( aMessage );
167}
168
169
170void DIALOG_EXPORT_STEP_LOG::onProcessTerminate( wxProcessEvent& aEvent )
171{
172 // We need to inform the thread that the process has died
173 // Since it can't receive the event from wx
174 if( m_stdioThread && m_stdioThread->IsRunning() )
175 {
177 m_stdioThread->Wait();
178 delete m_stdioThread;
179 m_stdioThread = nullptr;
180 m_sdbSizerOK->Enable( true );
181
182 int exitCode = aEvent.GetExitCode();
183
184 // set the progress bar to complete/incomplete base don status
185 m_activityGauge->SetRange( 1 );
186
187 if( exitCode != 0 )
188 {
189 m_textCtrlLog->SetForegroundColour( *wxRED );
190
191 m_textCtrlLog->AppendText( wxS( "\n*** " ) );
192 m_textCtrlLog->AppendText(
193 wxString::Format( _( "Process failed with exit code %d" ), exitCode ) );
194 m_textCtrlLog->AppendText( wxS( " ***\n" ) );
195
196 m_activityGauge->SetValue( 0 );
197 }
198 else
199 {
200 m_textCtrlLog->AppendText( wxS( "\n*** " ) );
201 m_textCtrlLog->AppendText( wxString::Format( _( "Success" ) ) );
202 m_textCtrlLog->AppendText( wxS( " ***\n" ) );
203
204 m_activityGauge->SetValue( 1 );
205 }
206 }
207}
208
209void DIALOG_EXPORT_STEP_LOG::onThreadInput( wxThreadEvent& aEvent )
210{
211 m_textCtrlLog->AppendText( aEvent.GetString() );
212 m_activityGauge->Pulse();
213}
214
215
216void DIALOG_EXPORT_STEP_LOG::onClose( wxCloseEvent& aEvent )
217{
218 if( m_stdioThread && m_stdioThread->IsRunning() )
219 {
220 if( aEvent.CanVeto() )
221 {
222 wxMessageDialog dlg( this, _( "Do you want to cancel the export process?" ),
223 _( "Cancel Export" ), wxYES_NO );
224
225 if( dlg.ShowModal() == wxID_NO )
226 {
227 aEvent.Veto();
228 return;
229 }
230 }
231
233 m_stdioThread->Delete();
234
235 m_process->DeletePendingEvents();
236 m_process->Unlink();
237 m_process->CloseOutput();
238 m_process->Detach();
239 }
240
241 // Clear log window message, storing the log data in config has no interest.
242 m_textCtrlLog->Clear();
243
244 aEvent.Skip();
245}
246
251
252
253DIALOG_EXPORT_STEP_LOG::DIALOG_EXPORT_STEP_LOG( wxWindow* aParent, const wxString& aStepCmd ) :
255{
256 m_sdbSizerOK->Enable( false );
257
258 m_process = new wxProcess( this );
259 m_process->Redirect();
260
261 Bind( wxEVT_END_PROCESS, &DIALOG_EXPORT_STEP_LOG::onProcessTerminate, this );
262
263 Bind( wxEVT_THREAD_STDIN, &DIALOG_EXPORT_STEP_LOG::onThreadInput, this );
264 Bind( wxEVT_THREAD_STDERR, &DIALOG_EXPORT_STEP_LOG::onThreadInput, this );
265 Bind( wxEVT_CLOSE_WINDOW, &DIALOG_EXPORT_STEP_LOG::onClose, this );
266
267 // Print the command line used to run kicad-cli.
268 // it can be useful if kicad-cli as a problem.
269 // However it cannot be printed in the Ctor, but only in TransferDataToWindow(),
270 // after DIALOG_SHIM initializations
271 m_startMessage.Append( _( "Command line:\n" ) );
272 m_startMessage.Append( aStepCmd );
273 m_startMessage.Append( wxT( "\n\n" ) );
274
276 m_stdioThread->Run();
277
278 if( !m_stdioThread->IsRunning() )
279 {
280 m_startMessage.Append( "Unable to launch stdstream thread.\n" );
281 delete m_stdioThread;
282 return;
283 }
284
285 m_activityGauge->Pulse();
286
287 wxExecute( aStepCmd, wxEXEC_ASYNC, m_process );
288}
void appendMessage(const wxString &aMessage)
DIALOG_EXPORT_STEP_LOG(wxWindow *aParent, const wxString &aStepCmd)
void onProcessTerminate(wxProcessEvent &aEvent)
@ PROCESS_COMPLETE
Informs the thread the process terminate event was received from wx.
@ REQUEST_EXIT
Asks the thread to exit and kill the process.
@ SENTINEL
Just a dummy entry for end of list.
void onClose(wxCloseEvent &event)
wxMessageQueue< STATE_MESSAGE > m_msgQueue
DIALOG_EXPORT_STEP_PROCESS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("3D Export"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
This thread handles consuming the input streams from the launched process.
STDSTREAM_THREAD(wxEvtHandler *aEventHandler, wxProcess *aProcess, wxMessageQueue< DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE > &aMsgQueue)
wxMessageQueue< DIALOG_EXPORT_STEP_LOG::STATE_MESSAGE > & m_queue
wxDEFINE_EVENT(wxEVT_THREAD_STDIN, wxThreadEvent)
#define _(s)