KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kicad.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) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2004-2021 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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
31#include <wx/filename.h>
32#include <wx/log.h>
33#include <wx/app.h>
34#include <wx/stdpaths.h>
35#include <wx/msgdlg.h>
36#include <wx/cmdline.h>
37
38#include <file_history.h>
39#include <hotkeys_basic.h>
40#include <kiway.h>
41#include <macros.h>
42#include <paths.h>
43#include <richio.h>
46#include <systemdirsappend.h>
47#include <trace_helpers.h>
49
50#include <git2.h>
51#include <stdexcept>
52
53#include "pgm_kicad.h"
54#include "kicad_manager_frame.h"
55
56#include <kiplatform/app.h>
58
59
60// a dummy to quiet linking with EDA_BASE_FRAME::config();
61#include <kiface_base.h>
63{
64 // This function should never be called. It is only referenced from
65 // EDA_BASE_FRAME::config() and this is only provided to satisfy the linker,
66 // not to be actually called.
67 wxLogFatalError( wxT( "Unexpected call to Kiface() in kicad/kicad.cpp" ) );
68
69 throw std::logic_error( "Unexpected call to Kiface() in kicad/kicad.cpp" );
70}
71
72
74
75
77{
78 return program;
79}
80
81
82// Similar to PGM_BASE& Pgm(), but return nullptr when a *.ki_face is run from a python script.
84{
85 return &program;
86}
87
88
90{
91 return program;
92}
93
94
96{
97 App().SetAppDisplayName( wxT( "KiCad" ) );
98
99#if defined(DEBUG)
100 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
101
102 if( !wxIsAbsolutePath( absoluteArgv0 ) )
103 {
104 wxLogError( wxT( "No meaningful argv[0]" ) );
105 return false;
106 }
107#endif
108
109 // Initialize the git library before trying to initialize individual programs
110 git_libgit2_init();
111
112 static const wxCmdLineEntryDesc desc[] = {
113 { wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
114 { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
115 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
116 { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
117 };
118
119 wxCmdLineParser parser( App().argc, App().argv );
120 parser.SetDesc( desc );
121 parser.Parse( false );
122
123 FRAME_T appType = KICAD_MAIN_FRAME_T;
124
125 const struct
126 {
127 wxString name;
128 FRAME_T type;
129 } frameTypes[] = { { wxT( "pcb" ), FRAME_PCB_EDITOR },
130 { wxT( "fpedit" ), FRAME_FOOTPRINT_EDITOR },
131 { wxT( "sch" ), FRAME_SCH },
132 { wxT( "calc" ), FRAME_CALC },
133 { wxT( "bm2cmp" ), FRAME_BM2CMP },
134 { wxT( "ds" ), FRAME_PL_EDITOR },
135 { wxT( "gerb" ), FRAME_GERBER },
136 { wxT( "" ), FRAME_T_COUNT } };
137
138 wxString frameName;
139
140 if( parser.Found( "frame", &frameName ) )
141 {
142 appType = FRAME_T_COUNT;
143
144 for( const auto& it : frameTypes )
145 {
146 if( it.name == frameName )
147 appType = it.type;
148 }
149
150 if( appType == FRAME_T_COUNT )
151 {
152 wxLogError( wxT( "Unknown frame: %s" ), frameName );
153 // Clean up
154 OnPgmExit();
155 return false;
156 }
157 }
158
159 if( appType == KICAD_MAIN_FRAME_T )
160 {
162 }
163 else
164 {
166 }
167
168 bool skipPythonInit = false;
169
170 if( appType == FRAME_BM2CMP || appType == FRAME_PL_EDITOR || appType == FRAME_GERBER
171 || appType == FRAME_CALC )
172 skipPythonInit = true;
173
174 if( !InitPgm( false, skipPythonInit ) )
175 return false;
176
180 m_bm.Init();
181
182 // Add search paths to feed the PGM_KICAD::SysSearch() function,
183 // currently limited in support to only look for project templates
184 {
185 SEARCH_STACK bases;
186
187 SystemDirsAppend( &bases );
188
189 for( unsigned i = 0; i < bases.GetCount(); ++i )
190 {
191 wxFileName fn( bases[i], wxEmptyString );
192
193 // Add KiCad template file path to search path list.
194 fn.AppendDir( wxT( "template" ) );
195
196 // Only add path if exists and can be read by the user.
197 if( fn.DirExists() && fn.IsDirReadable() )
198 m_bm.m_search.AddPaths( fn.GetPath() );
199 }
200
201 // The KICAD7_TEMPLATE_DIR takes precedence over the search stack template path.
202 ENV_VAR_MAP_CITER it = GetLocalEnvVariables().find( "KICAD7_TEMPLATE_DIR" );
203
204 if( it != GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
205 m_bm.m_search.Insert( it->second.GetValue(), 0 );
206
207 // We've been adding system (installed default) search paths so far, now for user paths
208 // The default user search path is inside KIPLATFORM::ENV::GetDocumentsPath()
210
211 // ...but the user can override that default with the KICAD_USER_TEMPLATE_DIR env var
212 it = GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" );
213
214 if( it != GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
215 m_bm.m_search.Insert( it->second.GetValue(), 0 );
216 }
217
218 wxFrame* frame = nullptr;
219 KIWAY_PLAYER* playerFrame = nullptr;
220 KICAD_MANAGER_FRAME* managerFrame = nullptr;
221
222 if( appType == KICAD_MAIN_FRAME_T )
223 {
224 managerFrame = new KICAD_MANAGER_FRAME( nullptr, wxT( "KiCad" ), wxDefaultPosition,
225 wxSize( 775, -1 ) );
226 frame = managerFrame;
227 }
228 else
229 {
230 // Use KIWAY to create a top window, which registers its existence also.
231 // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
232 // and is one of the types in FRAME_T.
233 playerFrame = Kiway.Player( appType, true );
234 frame = playerFrame;
235
236 if( frame == nullptr )
237 {
238 return false;
239 }
240 }
241
242 App().SetTopWindow( frame );
243
244 if( playerFrame )
245 App().SetAppDisplayName( playerFrame->GetAboutTitle() );
246
247 Kiway.SetTop( frame );
248
249 KICAD_SETTINGS* settings = static_cast<KICAD_SETTINGS*>( PgmSettings() );
250
251 wxString projToLoad;
252
253 HideSplash();
254
255 if( playerFrame && parser.GetParamCount() )
256 {
257 // Now after the frame processing, the rest of the positional args are files
258 std::vector<wxString> fileArgs;
259 /*
260 gerbview handles multiple project data files, i.e. gerber files on
261 cmd line. Others currently do not, they handle only one. For common
262 code simplicity we simply pass all the arguments in however, each
263 program module can do with them what they want, ignore, complain
264 whatever. We don't establish policy here, as this is a multi-purpose
265 launcher.
266 */
267
268 for( size_t i = 0; i < parser.GetParamCount(); i++ )
269 fileArgs.push_back( parser.GetParam( i ) );
270
271 // special attention to a single argument: argv[1] (==argSet[0])
272 if( fileArgs.size() == 1 )
273 {
274 wxFileName argv1( fileArgs[0] );
275
276#if defined( PGM_DATA_FILE_EXT )
277 // PGM_DATA_FILE_EXT, if present, may be different for each compile,
278 // it may come from CMake on the compiler command line, but often does not.
279 // This facility is mostly useful for those program footprints
280 // supporting a single argv[1].
281 if( !argv1.GetExt() )
282 argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
283#endif
284 argv1.MakeAbsolute();
285
286 fileArgs[0] = argv1.GetFullPath();
287 }
288
289 // Use the KIWAY_PLAYER::OpenProjectFiles() API function:
290 if( !playerFrame->OpenProjectFiles( fileArgs ) )
291 {
292 // OpenProjectFiles() API asks that it report failure to the UI.
293 // Nothing further to say here.
294
295 // We've already initialized things at this point, but wx won't call OnExit if
296 // we fail out. Call our own cleanup routine here to ensure the relevant resources
297 // are freed at the right time (if they aren't, segfaults will occur).
298 OnPgmExit();
299
300 // Fail the process startup if the file could not be opened,
301 // although this is an optional choice, one that can be reversed
302 // also in the KIFACE specific OpenProjectFiles() return value.
303 return false;
304 }
305 }
306 else if( managerFrame )
307 {
308 if( App().argc > 1 )
309 {
310 wxFileName tmp = App().argv[1];
311
312 if( tmp.GetExt() != ProjectFileExtension && tmp.GetExt() != LegacyProjectFileExtension )
313 {
314 wxString msg;
315
316 msg.Printf( _( "File '%s'\ndoes not appear to be a valid KiCad project file." ),
317 tmp.GetFullPath() );
318 wxMessageDialog dlg( nullptr, msg, _( "Error" ), wxOK | wxICON_EXCLAMATION );
319 dlg.ShowModal();
320 }
321 else
322 {
323 projToLoad = tmp.GetFullPath();
324 }
325 }
326
327 // If no file was given as an argument, check that there was a file open.
328 if( projToLoad.IsEmpty() && settings->m_OpenProjects.size() )
329 {
330 wxString last_pro = settings->m_OpenProjects.front();
331 settings->m_OpenProjects.erase( settings->m_OpenProjects.begin() );
332
333 if( wxFileExists( last_pro ) )
334 {
335 // Try to open the last opened project,
336 // if a project name is not given when starting Kicad
337 projToLoad = last_pro;
338 }
339 }
340
341 // Do not attempt to load a non-existent project file.
342 if( !projToLoad.empty() )
343 {
344 wxFileName fn( projToLoad );
345
346 if( fn.Exists() )
347 {
348 fn.MakeAbsolute();
349
350 if( appType == KICAD_MAIN_FRAME_T )
351 {
352 managerFrame->LoadProject( fn );
353 }
354 }
355 }
356 }
357
358 frame->Show( true );
359 frame->Raise();
360
361 return true;
362}
363
364
366{
367 return 0;
368}
369
370
372{
374
376 {
378 m_settings_manager->Save();
379 }
380
381 // Destroy everything in PGM_KICAD,
382 // especially wxSingleInstanceCheckerImpl earlier than wxApp and earlier
383 // than static destruction would.
384 Destroy();
385 git_libgit2_shutdown();
386}
387
388
389void PGM_KICAD::MacOpenFile( const wxString& aFileName )
390{
391#if defined(__WXMAC__)
392
393 KICAD_MANAGER_FRAME* frame = (KICAD_MANAGER_FRAME*) App().GetTopWindow();
394
395 if( !aFileName.empty() && wxFileExists( aFileName ) )
396 frame->LoadProject( wxFileName( aFileName ) );
397
398#endif
399}
400
401
403{
404 // unlike a normal destructor, this is designed to be called more
405 // than once safely:
406
407 m_bm.End();
408
410}
411
412
414
415#ifdef NDEBUG
416// Define a custom assertion handler
417void CustomAssertHandler(const wxString& file,
418 int line,
419 const wxString& func,
420 const wxString& cond,
421 const wxString& msg)
422{
423 Pgm().HandleAssert( file, line, func, cond, msg );
424}
425#endif
426
430struct APP_KICAD : public wxApp
431{
432 APP_KICAD() : wxApp()
433 {
434 // Init the environment each platform wants
436 }
437
438
439 bool OnInit() override
440 {
441#ifdef NDEBUG
442 // These checks generate extra assert noise
443 wxSizerFlags::DisableConsistencyChecks();
444 wxDISABLE_DEBUG_SUPPORT();
445 wxSetAssertHandler( CustomAssertHandler );
446#endif
447
448 // Perform platform-specific init tasks
449 if( !KIPLATFORM::APP::Init() )
450 return false;
451
452 if( !program.OnPgmInit() )
453 {
455 return false;
456 }
457
458 return true;
459 }
460
461 int OnExit() override
462 {
464
465 // Avoid wxLog crashing when used in destructors.
466 wxLog::EnableLogging( false );
467
468 return wxApp::OnExit();
469 }
470
471 int OnRun() override
472 {
473 try
474 {
475 return wxApp::OnRun();
476 }
477 catch(...)
478 {
479 Pgm().HandleException( std::current_exception() );
480 }
481
482 return -1;
483 }
484
485 int FilterEvent( wxEvent& aEvent ) override
486 {
487 if( aEvent.GetEventType() == wxEVT_SHOW )
488 {
489 wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
490 wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
491
492 std::vector<void*>& dlgs = Pgm().m_ModalDialogs;
493
494 if( dialog )
495 {
496 if( event.IsShown() && dialog->IsModal() )
497 {
498 dlgs.push_back( dialog );
499 }
500 // Under GTK, sometimes the modal flag is cleared before hiding
501 else if( !event.IsShown() && !dlgs.empty() )
502 {
503 // If we close the expected dialog, remove it from our stack
504 if( dlgs.back() == dialog )
505 dlgs.pop_back();
506 // If an out-of-order, remove all dialogs added after the closed one
507 else if( auto it = std::find( dlgs.begin(), dlgs.end(), dialog ) ; it != dlgs.end() )
508 dlgs.erase( it, dlgs.end() );
509 }
510 }
511 }
512
513 return Event_Skip;
514 }
515
516#if defined( DEBUG )
520 bool ProcessEvent( wxEvent& aEvent ) override
521 {
522 if( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
523 {
524 wxKeyEvent* keyEvent = static_cast<wxKeyEvent*>( &aEvent );
525
526 if( keyEvent )
527 {
528 wxLogTrace( kicadTraceKeyEvent, "APP_KICAD::ProcessEvent %s", dump( *keyEvent ) );
529 }
530 }
531
532 aEvent.Skip();
533 return false;
534 }
535
543 bool OnExceptionInMainLoop() override
544 {
545 try
546 {
547 throw;
548 }
549 catch(...)
550 {
551 Pgm().HandleException( std::current_exception() );
552 }
553
554 return false; // continue on. Return false to abort program
555 }
556#endif
557
563#if defined( __WXMAC__ )
564 void MacOpenFile( const wxString& aFileName ) override
565 {
566 Pgm().MacOpenFile( aFileName );
567 }
568#endif
569};
570
571IMPLEMENT_APP( APP_KICAD )
572
573
574// The C++ project manager supports one open PROJECT, so Prj() calls within
575// this link image need this function.
577{
578 return Kiway.Prj();
579}
580
const char * name
Definition: DXF_plotter.cpp:57
const wxString & GetAboutTitle() const
The main KiCad project manager frame.
void LoadProject(const wxFileName &aProjectFileName)
std::vector< wxString > m_OpenProjects
A KIFACE implementation.
Definition: kiface_base.h:39
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:67
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
Definition: kiway_player.h:115
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:279
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:432
void SetCtlBits(int aCtlBits)
Overwrites previously set ctl bits, only for use in kicad.cpp to flip between standalone and manager ...
Definition: kiway.h:412
void OnKiwayEnd()
Definition: kiway.cpp:752
void SetTop(wxFrame *aTop)
Tell this KIWAY about the top most frame in the program and optionally allows it to play the role of ...
Definition: kiway.cpp:88
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition: kiway.cpp:196
static wxString GetUserTemplatesPath()
Gets the user path for custom templates.
Definition: paths.cpp:87
Container for data for KiCad programs.
Definition: pgm_base.h:99
virtual wxApp & App()
Returns a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition: pgm_base.cpp:173
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:875
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:397
void Destroy()
Definition: pgm_base.cpp:157
bool InitPgm(bool aHeadless=false, bool aSkipPyInit=false, bool aIsUnitTest=false)
Initialize this program.
Definition: pgm_base.cpp:441
std::vector< void * > m_ModalDialogs
Definition: pgm_base.h:379
void HandleException(std::exception_ptr aPtr)
A exception handler to be used at the top level if exceptions bubble up that for.
Definition: pgm_base.cpp:890
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:947
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:139
void HideSplash()
Definition: pgm_base.cpp:430
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition: pgm_base.cpp:625
PGM_KICAD extends PGM_BASE to bring in FileHistory() and PdfBrowser() which were moved from EDA_APP i...
Definition: pgm_kicad.h:38
bool OnPgmInit()
Definition: kicad.cpp:95
void Destroy()
Definition: kicad.cpp:402
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
Definition: kicad.cpp:389
void OnPgmExit()
Definition: kicad.cpp:371
APP_SETTINGS_BASE * PgmSettings()
Definition: pgm_kicad.h:55
int OnPgmRun()
Definition: kicad.cpp:365
BIN_MOD m_bm
Definition: pgm_kicad.h:68
Container for project specific data.
Definition: project.h:62
Look for files in a number of paths.
Definition: search_stack.h:42
void AddPaths(const wxString &aPaths, int aIndex=-1)
Insert or append path(s).
T * RegisterSettings(T *aSettings, bool aLoadNow=true)
Takes ownership of the pointer passed in.
void SetKiway(KIWAY *aKiway)
Associate this setting manager with the given Kiway.
#define _(s)
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:33
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_CALC
Definition: frame_type.h:63
@ FRAME_BM2CMP
Definition: frame_type.h:61
@ FRAME_SCH
Definition: frame_type.h:34
@ FRAME_T_COUNT
Definition: frame_type.h:70
@ FRAME_PL_EDITOR
Definition: frame_type.h:59
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
@ FRAME_GERBER
Definition: frame_type.h:57
@ KICAD_MAIN_FRAME_T
Definition: frame_type.h:68
const std::string LegacyProjectFileExtension
const std::string ProjectFileExtension
const wxChar *const kicadTraceKeyEvent
Flag to enable wxKeyEvent debug tracing.
std::map< wxString, ENV_VAR_ITEM >::const_iterator ENV_VAR_MAP_CITER
PGM_KICAD & PgmTop()
Definition: kicad.cpp:89
PROJECT & Prj()
Definition: kicad.cpp:576
static PGM_KICAD program
Definition: kicad.cpp:73
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:76
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition: kicad.cpp:62
PGM_BASE * PgmOrNull()
similar to PGM_BASE& Pgm(), but return a reference that can be nullptr when running a shared lib from...
Definition: kicad.cpp:83
#define KFCTL_CPP_PROJECT_SUITE
Running under C++ project mgr, possibly with others.
Definition: kiway.h:159
KIWAY Kiway
#define KFCTL_STANDALONE
Running as a standalone Top.
Definition: kiway.h:158
This file contains miscellaneous commonly used macros and functions.
bool Init()
Perform application-specific initialization tasks.
Definition: gtk/app.cpp:40
void Init()
Perform environment initialization tasks.
Not publicly visible because most of the action is in PGM_KICAD these days.
Definition: kicad.cpp:431
int OnRun() override
Definition: kicad.cpp:471
APP_KICAD()
Definition: kicad.cpp:432
bool OnInit() override
Definition: kicad.cpp:439
int OnExit() override
Definition: kicad.cpp:461
int FilterEvent(wxEvent &aEvent) override
Definition: kicad.cpp:485
void End()
Definition: bin_mod.cpp:50
void Init()
Definition: bin_mod.cpp:38
SEARCH_STACK m_search
Definition: bin_mod.h:60
void InitSettings(APP_SETTINGS_BASE *aPtr)
Takes ownership of a new application settings object.
Definition: bin_mod.h:53
void SystemDirsAppend(SEARCH_STACK *aSearchStack)
Append system places to aSearchStack in a platform specific way and pertinent to KiCad programs.
System directories search utilities.
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.