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/app.h>
35#include <wx/cmdline.h>
36#include <wx/dialog.h>
37#include <wx/filename.h>
38#include <wx/stdpaths.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 PGM_BASE earlier than wxApp and static destruction would
103
104 if( GIT_BACKEND* backend = GetGitBackend() )
105 {
106 backend->Shutdown();
107 delete backend;
108 SetGitBackend( nullptr );
109 }
110 }
111
112 void MacOpenFile( const wxString& aFileName ) override
113 {
114 wxFileName filename( aFileName );
115
116 if( filename.FileExists() )
117 {
118 #if 0
119 // this pulls in EDA_DRAW_FRAME type info, which we don't want in
120 // the single_top link image.
121 KIWAY_PLAYER* frame = dynamic_cast<KIWAY_PLAYER*>( App().GetTopWindow() );
122 #else
123 KIWAY_PLAYER* frame = (KIWAY_PLAYER*) App().GetTopWindow();
124 #endif
125 if( frame )
126 {
127 if( wxWindow* blocking_win = frame->Kiway().GetBlockingDialog() )
128 blocking_win->Close( true );
129
130 frame->OpenProjectFiles( std::vector<wxString>( 1, aFileName ) );
131 }
132 }
133 }
134
136
137
138// A module to allow Html module initialization/cleanup
139// When a wxHtmlWindow is used *only* in a dll/so module, the Html text is displayed
140// as plain text.
141// This helper class is just used to force wxHtmlWinParser initialization
142// see https://groups.google.com/forum/#!topic/wx-users/FF0zv5qGAT0
143class HtmlModule: public wxModule
144{
145public:
147 virtual bool OnInit() override { AddDependency( CLASSINFO( wxHtmlWinParser ) ); return true; };
148 virtual void OnExit() override {};
149
150private:
152};
153
155
156
157#ifdef NDEBUG
158// Define a custom assertion handler
159void CustomAssertHandler( const wxString& file,
160 int line,
161 const wxString& func,
162 const wxString& cond,
163 const wxString& msg )
164{
165 Pgm().HandleAssert( file, line, func, cond, msg );
166}
167#endif
168
169
174struct APP_SINGLE_TOP : public wxApp
175{
176 APP_SINGLE_TOP() : wxApp()
177 {
178 SetPgm( &program );
179
180 // Init the environment each platform wants
182 }
183
184
185 bool OnInit() override
186 {
187#ifdef NDEBUG
188 // These checks generate extra assert noise
189 wxSizerFlags::DisableConsistencyChecks();
190 wxDISABLE_DEBUG_SUPPORT();
191 wxSetAssertHandler( CustomAssertHandler );
192#endif
193
194 // Perform platform-specific init tasks
195 if( !KIPLATFORM::APP::Init() )
196 return false;
197
198#ifndef DEBUG
199 // Enable logging traces to the console in release build.
200 // This is usually disabled, but it can be useful for users to run to help
201 // debug issues and other problems.
202 if( wxGetEnv( wxS( "KICAD_ENABLE_WXTRACE" ), nullptr ) )
203 {
204 wxLog::EnableLogging( true );
205 wxLog::SetLogLevel( wxLOG_Trace );
206 }
207#endif
208
209 // Force wxHtmlWinParser initialization when a wxHtmlWindow is used only
210 // in a shared library (.so or .dll file)
211 // Otherwise the Html text is displayed as plain text.
212 HtmlModule html_init;
213
214 try
215 {
216 return program.OnPgmInit();
217 }
218 catch( ... )
219 {
220 Pgm().HandleException( std::current_exception() );
221 }
222
223 program.OnPgmExit();
224
225 return false;
226 }
227
228 int OnExit() override
229 {
230 // Drain wxPendingDelete (frames deferred via Destroy()) before tearing down
231 // PGM_BASE singletons. On macOS the dock-quit path leaves frames in this
232 // queue at OnExit() time, and their canvas destructors call into
233 // Pgm().GetGLContextManager(). Running OnPgmExit() first would null that
234 // pointer out from under them. See https://gitlab.com/kicad/code/kicad/-/issues/23373
235 int ret = wxApp::OnExit();
236 program.OnPgmExit();
237 return ret;
238 }
239
240 int OnRun() override
241 {
242 int ret = -1;
243
244 try
245 {
246 ret = wxApp::OnRun();
247 }
248 catch(...)
249 {
250 Pgm().HandleException( std::current_exception() );
251 }
252
253 return ret;
254 }
255
256 int FilterEvent( wxEvent& aEvent ) override
257 {
258 if( aEvent.GetEventType() == wxEVT_SHOW )
259 {
260 wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
261 wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
262
263 std::vector<void*>& dlgs = Pgm().m_ModalDialogs;
264
265 if( dialog )
266 {
267 if( event.IsShown() && dialog->IsModal() )
268 {
269 dlgs.push_back( dialog );
270 }
271 // Under GTK, sometimes the modal flag is cleared before hiding
272 else if( !event.IsShown() && !dlgs.empty() )
273 {
274 // If we close the expected dialog, remove it from our stack
275 if( dlgs.back() == dialog )
276 dlgs.pop_back();
277 // If an out-of-order, remove all dialogs added after the closed one
278 else if( auto it = std::find( dlgs.begin(), dlgs.end(), dialog ); it != dlgs.end() )
279 dlgs.erase( it, dlgs.end() );
280 }
281 }
282 }
283
284 return Event_Skip;
285 }
286
287 void OnUnhandledException() override
288 {
289 Pgm().HandleException( std::current_exception(), true );
290 }
291
292#if defined( DEBUG )
300 virtual bool OnExceptionInMainLoop() override
301 {
302 try
303 {
304 throw;
305 }
306 catch( ... )
307 {
308 Pgm().HandleException( std::current_exception() );
309 }
310
311 return false; // continue on. Return false to abort program
312 }
313#endif
314
315#ifdef __WXMAC__
316
324 void MacOpenFile( const wxString& aFileName ) override
325 {
326 Pgm().MacOpenFile( aFileName );
327 }
328
329#endif
330};
331
332IMPLEMENT_APP( APP_SINGLE_TOP )
333
334
336{
337#if defined(DEBUG)
338 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
339
340 if( !wxIsAbsolutePath( absoluteArgv0 ) )
341 {
342 wxLogError( wxT( "No meaningful argv[0]" ) );
343 return false;
344 }
345#endif
346
347 // Initialize the git backend before trying to initialize individual programs
349 GetGitBackend()->Init();
350
351 if( !GetGitBackend()->IsLibraryAvailable() )
352 {
353 const git_error* err = git_error_last();
354 wxString msg = wxS( "Failed to initialize git library" );
355
356 if( err && err->message )
357 msg += wxS( ": " ) + wxString::FromUTF8( err->message );
358
359 wxLogError( msg );
360 return false;
361 }
362
363 if( !InitPgm( false ) )
364 {
365 // Clean up
366 OnPgmExit();
367 return false;
368 }
369
370#if !defined(BUILD_KIWAY_DLL)
371
372 // Only bitmap2component and pcb_calculator use this code currently, as they
373 // are not split to use single_top as a link image separate from a *.kiface.
374 // i.e. they are single part link images so don't need to load a *.kiface.
375
376 // Get the getter, it is statically linked into this binary image.
377 KIFACE_GETTER_FUNC* ki_getter = &KIFACE_GETTER;
378
379 int kiface_version;
380
381 // Get the KIFACE.
382 KIFACE* kiface = ki_getter( &kiface_version, KIFACE_VERSION, this );
383
384 // Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE
385 // in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways.
386 Kiway.set_kiface( KIWAY::KifaceType( TOP_FRAME ), kiface );
387#endif
388
389 // Tell the settings manager about the current Kiway
390 GetSettingsManager().SetKiway( &Kiway );
391
392 GetSettingsManager().RegisterSettings( new KICAD_SETTINGS );
393
394
395 if( const COMMON_SETTINGS* cfg = Pgm().GetCommonSettings() )
396 {
397 if( cfg->m_Appearance.app_theme == APP_THEME::DARK )
399 else if( cfg->m_Appearance.app_theme == APP_THEME::AUTO )
401 }
402
403 // Create the API server thread once the app event loop exists
404 m_api_server = std::make_unique<KICAD_API_SERVER>();
405
406 // Use KIWAY to create a top window, which registers its existence also.
407 // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
408 // and is one of the types in FRAME_T.
409 KIWAY_PLAYER* frame = Kiway.Player( TOP_FRAME, true );
410
411 if( frame == nullptr )
412 {
413 // Clean up
414 OnPgmExit();
415 return false;
416 }
417
418 Kiway.SetTop( frame );
419
420 STARTWIZARD startWizard;
421 startWizard.CheckAndRun( frame );
422
423 // Load library tables after startup wizard
424 GetLibraryManager().LoadGlobalTables();
425
426 App().SetTopWindow( frame ); // wxApp gets a face.
427 App().SetAppDisplayName( frame->GetAboutTitle() );
428
429 wxString relaunchDisplayName = frame->GetAboutTitle() + " " + GetMajorMinorVersion();
431
432 // Allocate a slice of time to show the frame and update wxWidgets widgets
433 // (especially setting valid sizes) after creating frame and before calling
434 // OpenProjectFiles() that can update/use some widgets.
435 // The 2 calls to wxSafeYield are needed on wxGTK for best results.
436 wxSafeYield();
437 HideSplash();
438 frame->Show();
439 wxSafeYield();
440
441 // Now after the frame processing, the rest of the positional args are files
442 std::vector<wxString> fileArgs;
443
444
445 static const wxCmdLineEntryDesc desc[] = {
446 { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
447 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
448 { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
449 };
450
451 wxCmdLineParser parser( App().argc, App().argv );
452 parser.SetDesc( desc );
453 parser.Parse( false );
454
455 if( parser.GetParamCount() )
456 {
457 /*
458 gerbview handles multiple project data files, i.e. gerber files on
459 cmd line. Others currently do not, they handle only one. For common
460 code simplicity we simply pass all the arguments in however, each
461 program module can do with them what they want, ignore, complain
462 whatever. We don't establish policy here, as this is a multi-purpose
463 launcher.
464 */
465
466 for( size_t i = 0; i < parser.GetParamCount(); i++ )
467 fileArgs.push_back( parser.GetParam( i ) );
468
469 // special attention to a single argument: argv[1] (==argSet[0])
470 if( fileArgs.size() == 1 )
471 {
472 wxFileName argv1( fileArgs[0] );
473
474#if defined(PGM_DATA_FILE_EXT)
475 // PGM_DATA_FILE_EXT, if present, may be different for each compile,
476 // it may come from CMake on the compiler command line, but often does not.
477 // This facility is mostly useful for those program footprints
478 // supporting a single argv[1].
479 if( !argv1.GetExt() )
480 argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
481#endif
482 argv1.MakeAbsolute();
483
484 fileArgs[0] = argv1.GetFullPath();
485 }
486
487 frame->OpenProjectFiles( fileArgs );
488 }
489
490 if( KIFACE* topFrame = Kiway.KiFACE( KIWAY::KifaceType( TOP_FRAME ) ) )
491 topFrame->PreloadLibraries( &Kiway );
492
494
495 m_api_server->SetReadyToReply();
496
497 return true;
498}
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:340
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:329
wxWindow * GetBlockingDialog()
Gets the window pointer to the blocking dialog (to send it signals)
Definition kiway.cpp:680
static const wxString & GetExecutablePath()
Definition paths.cpp:661
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
virtual wxApp & App()
Return a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition pgm_base.cpp:202
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:892
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:405
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:807
std::vector< void * > m_ModalDialogs
Definition pgm_base.h:386
bool InitPgm(bool aHeadless=false, bool aIsUnitTest=false)
Initialize this program.
Definition pgm_base.cpp:340
std::unique_ptr< KICAD_API_SERVER > m_api_server
Definition pgm_base.h:411
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:844
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:123
void HideSplash()
Definition pgm_base.cpp:307
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition pgm_base.cpp:537
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:579
#define KIFACE_GETTER
Definition kiway.h:110
#define KIFACE_VERSION
Definition kiway.h:109
#define KFCTL_STANDALONE
Running as a standalone Top.
Definition kiway.h:174
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:153
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.