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