KiCad PCB EDA Suite
Loading...
Searching...
No Matches
single_top.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 (C) 2014 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22/*
23
24 This is a program launcher for a single KIFACE DSO. It only mimics a KIWAY,
25 not actually implements one, since only a single DSO is supported by it.
26
27 It is compiled multiple times, once for each standalone program and as such
28 gets different compiler command line supplied #defines from CMake.
29
30*/
31
32
33#include <typeinfo>
34#include <wx/cmdline.h>
35#include <wx/dialog.h>
36#include <wx/filename.h>
37#include <wx/stdpaths.h>
38#include <wx/snglinst.h>
39#include <wx/html/htmlwin.h>
40
41#include <api/api_server.h>
42#include <kiway.h>
43#include <build_version.h>
44#include <pgm_base.h>
45#include <app_monitor.h>
46#include <kiway_player.h>
47#include <macros.h>
48#include <confirm.h>
50
54#include <paths.h>
55
56#include <kiplatform/app.h>
58
59#include <git2.h>
60#include <git/git_backend.h>
61#include <git/libgit_backend.h>
62#include <thread_pool.h>
63
66
67#ifdef KICAD_USE_SENTRY
68#include <sentry.h>
69#endif
70
71// Only a single KIWAY is supported in this single_top top level component,
72// which is dedicated to loading only a single DSO.
74
75
76// implement a PGM_BASE and a wxApp side by side:
77
81static struct PGM_SINGLE_TOP : public PGM_BASE
82{
83 bool OnPgmInit();
84
85 void OnPgmExit()
86 {
87 // Abort and wait on any background jobs
88 GetKiCadThreadPool().purge();
89 GetKiCadThreadPool().wait();
90
91 Kiway.OnKiwayEnd();
92
93 m_api_server.reset();
94
96 {
98 m_settings_manager->Save();
99 }
100
101 // Destroy everything in PGM_BASE, especially wxSingleInstanceCheckerImpl
102 // earlier than wxApp and earlier than static destruction would.
104
105 if( GIT_BACKEND* backend = GetGitBackend() )
106 {
107 backend->Shutdown();
108 delete backend;
109 SetGitBackend( nullptr );
110 }
111 }
112
113 void MacOpenFile( const wxString& aFileName ) override
114 {
115 wxFileName filename( aFileName );
116
117 if( filename.FileExists() )
118 {
119 #if 0
120 // this pulls in EDA_DRAW_FRAME type info, which we don't want in
121 // the single_top link image.
122 KIWAY_PLAYER* frame = dynamic_cast<KIWAY_PLAYER*>( App().GetTopWindow() );
123 #else
124 KIWAY_PLAYER* frame = (KIWAY_PLAYER*) App().GetTopWindow();
125 #endif
126 if( frame )
127 {
128 if( wxWindow* blocking_win = frame->Kiway().GetBlockingDialog() )
129 blocking_win->Close( true );
130
131 frame->OpenProjectFiles( std::vector<wxString>( 1, aFileName ) );
132 }
133 }
134 }
135
137
138
139// A module to allow Html module initialization/cleanup
140// When a wxHtmlWindow is used *only* in a dll/so module, the Html text is displayed
141// as plain text.
142// This helper class is just used to force wxHtmlWinParser initialization
143// see https://groups.google.com/forum/#!topic/wx-users/FF0zv5qGAT0
144class HtmlModule: public wxModule
145{
146public:
148 virtual bool OnInit() override { AddDependency( CLASSINFO( wxHtmlWinParser ) ); return true; };
149 virtual void OnExit() override {};
150
151private:
153};
154
156
157
158#ifdef NDEBUG
159// Define a custom assertion handler
160void CustomAssertHandler( const wxString& file,
161 int line,
162 const wxString& func,
163 const wxString& cond,
164 const wxString& msg )
165{
166 Pgm().HandleAssert( file, line, func, cond, msg );
167}
168#endif
169
170
175struct APP_SINGLE_TOP : public wxApp
176{
177 APP_SINGLE_TOP() : wxApp()
178 {
179 SetPgm( &program );
180
181 // Init the environment each platform wants
183 }
184
185
186 bool OnInit() override
187 {
188#ifdef NDEBUG
189 // These checks generate extra assert noise
190 wxSizerFlags::DisableConsistencyChecks();
191 wxDISABLE_DEBUG_SUPPORT();
192 wxSetAssertHandler( CustomAssertHandler );
193#endif
194
195 // Perform platform-specific init tasks
196 if( !KIPLATFORM::APP::Init() )
197 return false;
198
199#ifndef DEBUG
200 // Enable logging traces to the console in release build.
201 // This is usually disabled, but it can be useful for users to run to help
202 // debug issues and other problems.
203 if( wxGetEnv( wxS( "KICAD_ENABLE_WXTRACE" ), nullptr ) )
204 {
205 wxLog::EnableLogging( true );
206 wxLog::SetLogLevel( wxLOG_Trace );
207 }
208#endif
209
210 // Force wxHtmlWinParser initialization when a wxHtmlWindow is used only
211 // in a shared library (.so or .dll file)
212 // Otherwise the Html text is displayed as plain text.
213 HtmlModule html_init;
214
215 try
216 {
217 return program.OnPgmInit();
218 }
219 catch( ... )
220 {
221 Pgm().HandleException( std::current_exception() );
222 }
223
224 program.OnPgmExit();
225
226 return false;
227 }
228
229 int OnExit() override
230 {
231 // Drain wxPendingDelete (frames deferred via Destroy()) before tearing down
232 // PGM_BASE singletons. On macOS the dock-quit path leaves frames in this
233 // queue at OnExit() time, and their canvas destructors call into
234 // Pgm().GetGLContextManager(). Running OnPgmExit() first would null that
235 // pointer out from under them. See https://gitlab.com/kicad/code/kicad/-/issues/23373
236 int ret = wxApp::OnExit();
237 program.OnPgmExit();
238 return ret;
239 }
240
241 int OnRun() override
242 {
243 int ret = -1;
244
245 try
246 {
247 ret = wxApp::OnRun();
248 }
249 catch(...)
250 {
251 Pgm().HandleException( std::current_exception() );
252 }
253
254 return ret;
255 }
256
257 int FilterEvent( wxEvent& aEvent ) override
258 {
259 if( aEvent.GetEventType() == wxEVT_SHOW )
260 {
261 wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
262 wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
263
264 std::vector<void*>& dlgs = Pgm().m_ModalDialogs;
265
266 if( dialog )
267 {
268 if( event.IsShown() && dialog->IsModal() )
269 {
270 dlgs.push_back( dialog );
271 }
272 // Under GTK, sometimes the modal flag is cleared before hiding
273 else if( !event.IsShown() && !dlgs.empty() )
274 {
275 // If we close the expected dialog, remove it from our stack
276 if( dlgs.back() == dialog )
277 dlgs.pop_back();
278 // If an out-of-order, remove all dialogs added after the closed one
279 else if( auto it = std::find( dlgs.begin(), dlgs.end(), dialog ); it != dlgs.end() )
280 dlgs.erase( it, dlgs.end() );
281 }
282 }
283 }
284
285 return Event_Skip;
286 }
287
288 void OnUnhandledException() override
289 {
290 Pgm().HandleException( std::current_exception(), true );
291 }
292
293#if defined( DEBUG )
301 virtual bool OnExceptionInMainLoop() override
302 {
303 try
304 {
305 throw;
306 }
307 catch( ... )
308 {
309 Pgm().HandleException( std::current_exception() );
310 }
311
312 return false; // continue on. Return false to abort program
313 }
314#endif
315
316#ifdef __WXMAC__
317
325 void MacOpenFile( const wxString& aFileName ) override
326 {
327 Pgm().MacOpenFile( aFileName );
328 }
329
330#endif
331};
332
333IMPLEMENT_APP( APP_SINGLE_TOP )
334
335
337{
338#if defined(DEBUG)
339 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
340
341 if( !wxIsAbsolutePath( absoluteArgv0 ) )
342 {
343 wxLogError( wxT( "No meaningful argv[0]" ) );
344 return false;
345 }
346#endif
347
348 // Initialize the git backend before trying to initialize individual programs
350 GetGitBackend()->Init();
351
352 if( !GetGitBackend()->IsLibraryAvailable() )
353 {
354 const git_error* err = git_error_last();
355 wxString msg = wxS( "Failed to initialize git library" );
356
357 if( err && err->message )
358 msg += wxS( ": " ) + wxString::FromUTF8( err->message );
359
360 wxLogError( msg );
361 return false;
362 }
363
364 if( !InitPgm( false ) )
365 {
366 // Clean up
367 OnPgmExit();
368 return false;
369 }
370
371#if !defined(BUILD_KIWAY_DLL)
372
373 // Only bitmap2component and pcb_calculator use this code currently, as they
374 // are not split to use single_top as a link image separate from a *.kiface.
375 // i.e. they are single part link images so don't need to load a *.kiface.
376
377 // Get the getter, it is statically linked into this binary image.
378 KIFACE_GETTER_FUNC* ki_getter = &KIFACE_GETTER;
379
380 int kiface_version;
381
382 // Get the KIFACE.
383 KIFACE* kiface = ki_getter( &kiface_version, KIFACE_VERSION, this );
384
385 // Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE
386 // in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways.
387 Kiway.set_kiface( KIWAY::KifaceType( TOP_FRAME ), kiface );
388#endif
389
390 // Tell the settings manager about the current Kiway
391 GetSettingsManager().SetKiway( &Kiway );
392
393 GetSettingsManager().RegisterSettings( new KICAD_SETTINGS );
394
395
396 if( const COMMON_SETTINGS* cfg = Pgm().GetCommonSettings() )
397 {
398 if( cfg->m_Appearance.app_theme == APP_THEME::DARK )
400 else if( cfg->m_Appearance.app_theme == APP_THEME::AUTO )
402 }
403
404 // Create the API server thread once the app event loop exists
405 m_api_server = std::make_unique<KICAD_API_SERVER>();
406
407 // Use KIWAY to create a top window, which registers its existence also.
408 // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
409 // and is one of the types in FRAME_T.
410 KIWAY_PLAYER* frame = Kiway.Player( TOP_FRAME, true );
411
412 if( frame == nullptr )
413 {
414 // Clean up
415 OnPgmExit();
416 return false;
417 }
418
419 Kiway.SetTop( frame );
420
421 STARTWIZARD startWizard;
422 startWizard.CheckAndRun( frame );
423
424 // Load library tables after startup wizard
425 GetLibraryManager().LoadGlobalTables();
426
427 App().SetTopWindow( frame ); // wxApp gets a face.
428 App().SetAppDisplayName( frame->GetAboutTitle() );
429
430 wxString relaunchDisplayName = frame->GetAboutTitle() + " " + GetMajorMinorVersion();
432
433 // Allocate a slice of time to show the frame and update wxWidgets widgets
434 // (especially setting valid sizes) after creating frame and before calling
435 // OpenProjectFiles() that can update/use some widgets.
436 // The 2 calls to wxSafeYield are needed on wxGTK for best results.
437 wxSafeYield();
438 HideSplash();
439 frame->Show();
440 wxSafeYield();
441
442 // Now after the frame processing, the rest of the positional args are files
443 std::vector<wxString> fileArgs;
444
445
446 static const wxCmdLineEntryDesc desc[] = {
447 { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
448 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
449 { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
450 };
451
452 wxCmdLineParser parser( App().argc, App().argv );
453 parser.SetDesc( desc );
454 parser.Parse( false );
455
456 if( parser.GetParamCount() )
457 {
458 /*
459 gerbview handles multiple project data files, i.e. gerber files on
460 cmd line. Others currently do not, they handle only one. For common
461 code simplicity we simply pass all the arguments in however, each
462 program module can do with them what they want, ignore, complain
463 whatever. We don't establish policy here, as this is a multi-purpose
464 launcher.
465 */
466
467 for( size_t i = 0; i < parser.GetParamCount(); i++ )
468 fileArgs.push_back( parser.GetParam( i ) );
469
470 // special attention to a single argument: argv[1] (==argSet[0])
471 if( fileArgs.size() == 1 )
472 {
473 wxFileName argv1( fileArgs[0] );
474
475#if defined(PGM_DATA_FILE_EXT)
476 // PGM_DATA_FILE_EXT, if present, may be different for each compile,
477 // it may come from CMake on the compiler command line, but often does not.
478 // This facility is mostly useful for those program footprints
479 // supporting a single argv[1].
480 if( !argv1.GetExt() )
481 argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
482#endif
483 argv1.MakeAbsolute();
484
485 fileArgs[0] = argv1.GetFullPath();
486 }
487
488 frame->OpenProjectFiles( fileArgs );
489 }
490
491 if( KIFACE* topFrame = Kiway.KiFACE( KIWAY::KifaceType( TOP_FRAME ) ) )
492 topFrame->PreloadLibraries( &Kiway );
493
495
496 m_api_server->SetReadyToReply();
497
498 return true;
499}
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
wxString GetAboutTitle() const
virtual void Init()=0
virtual bool OnInit() override
virtual void OnExit() override
wxDECLARE_DYNAMIC_CLASS(HtmlModule)
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:311
static FACE_T KifaceType(FRAME_T aFrameType)
A simple mapping function which returns the FACE_T which is known to implement aFrameType.
Definition kiway.cpp:339
wxWindow * GetBlockingDialog()
Gets the window pointer to the blocking dialog (to send it signals)
Definition kiway.cpp:686
static const wxString & GetExecutablePath()
Definition paths.cpp:671
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:528
virtual wxApp & App()
Return a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition pgm_base.cpp:204
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:874
virtual void MacOpenFile(const wxString &aFileName)=0
Specific to MacOSX (not used under Linux or Windows).
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition pgm_base.h:403
void Destroy()
Definition pgm_base.cpp:183
void HandleException(std::exception_ptr aPtr, bool aUnhandled=false)
A exception handler to be used at the top level if exceptions bubble up that for.
Definition pgm_base.cpp:789
std::vector< void * > m_ModalDialogs
Definition pgm_base.h:384
bool InitPgm(bool aHeadless=false, bool aIsUnitTest=false)
Initialize this program.
Definition pgm_base.cpp:320
std::unique_ptr< KICAD_API_SERVER > m_api_server
Definition pgm_base.h:412
void HandleAssert(const wxString &aFile, int aLine, const wxString &aFunc, const wxString &aCond, const wxString &aMsg)
A common assert handler to be used between single_top and kicad.
Definition pgm_base.cpp:826
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
void HideSplash()
Definition pgm_base.cpp:309
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:126
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition pgm_base.cpp:519
void CheckAndRun(wxWindow *parent)
This file is part of the common library.
void SetGitBackend(GIT_BACKEND *aBackend)
GIT_BACKEND * GetGitBackend()
KIFACE * KIFACE_GETTER_FUNC(int *aKIFACEversion, int aKIWAYversion, PGM_BASE *aProgram)
Point to the one and only KIFACE export.
Definition kiway.h:549
#define KIFACE_GETTER
Definition kiway.h:109
#define KIFACE_VERSION
Definition kiway.h:108
#define KFCTL_STANDALONE
Running as a standalone Top.
Definition kiway.h:159
This file contains miscellaneous commonly used macros and functions.
bool Init()
Perform application-specific initialization tasks.
Definition unix/app.cpp:40
void EnableDarkMode(bool aForce)
Definition unix/app.cpp:58
void Init()
Perform environment initialization tasks.
void SetAppDetailsForWindow(wxWindow *aWindow, const wxString &aRelaunchCommand, const wxString &aRelaunchDisplayName)
Sets the relaunch command for taskbar pins, this is intended for Windows.
void SetPgm(PGM_BASE *pgm)
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
PGM_SINGLE_TOP program
wxIMPLEMENT_DYNAMIC_CLASS(HtmlModule, wxModule)
KIWAY Kiway(KFCTL_STANDALONE)
Implement a bare naked wxApp (so that we don't become dependent on functionality in a wxApp derivativ...
int OnRun() override
int OnExit() override
void OnUnhandledException() override
bool OnInit() override
int FilterEvent(wxEvent &aEvent) override
Implement a participant in the KIWAY alchemy.
Definition kiway.h:152
Implement PGM_BASE with its own OnPgmInit() and OnPgmExit().
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
IFACE KIFACE_BASE kiface("pcb_test_frame", KIWAY::FACE_PCB)
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.