KiCad PCB EDA Suite
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 <stdexcept>
51
52#include "pgm_kicad.h"
53#include "kicad_manager_frame.h"
54
55#include <kiplatform/app.h>
57
58
59// a dummy to quiet linking with EDA_BASE_FRAME::config();
60#include <kiface_base.h>
62{
63 // This function should never be called. It is only referenced from
64 // EDA_BASE_FRAME::config() and this is only provided to satisfy the linker,
65 // not to be actually called.
66 wxLogFatalError( wxT( "Unexpected call to Kiface() in kicad/kicad.cpp" ) );
67
68 throw std::logic_error( "Unexpected call to Kiface() in kicad/kicad.cpp" );
69}
70
71
73
74
76{
77 return program;
78}
79
80
81// Similar to PGM_BASE& Pgm(), but return nullptr when a *.ki_face is run from a python script.
83{
84 return &program;
85}
86
87
89{
90 return program;
91}
92
93
95{
96 App().SetAppDisplayName( wxT( "KiCad" ) );
97
98#if defined(DEBUG)
99 wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
100
101 if( !wxIsAbsolutePath( absoluteArgv0 ) )
102 {
103 wxLogError( wxT( "No meaningful argv[0]" ) );
104 return false;
105 }
106#endif
107
108 static const wxCmdLineEntryDesc desc[] = {
109 { wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
110 { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
111 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
112 { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
113 };
114
115 wxCmdLineParser parser( App().argc, App().argv );
116 parser.SetDesc( desc );
117 parser.Parse( false );
118
119 FRAME_T appType = KICAD_MAIN_FRAME_T;
120
121 const struct
122 {
123 wxString name;
124 FRAME_T type;
125 } frameTypes[] = { { wxT( "pcb" ), FRAME_PCB_EDITOR },
126 { wxT( "fpedit" ), FRAME_FOOTPRINT_EDITOR },
127 { wxT( "sch" ), FRAME_SCH },
128 { wxT( "calc" ), FRAME_CALC },
129 { wxT( "bm2cmp" ), FRAME_BM2CMP },
130 { wxT( "ds" ), FRAME_PL_EDITOR },
131 { wxT( "gerb" ), FRAME_GERBER },
132 { wxT( "" ), FRAME_T_COUNT } };
133
134 wxString frameName;
135
136 if( parser.Found( "frame", &frameName ) )
137 {
138 appType = FRAME_T_COUNT;
139
140 for( const auto& it : frameTypes )
141 {
142 if( it.name == frameName )
143 appType = it.type;
144 }
145
146 if( appType == FRAME_T_COUNT )
147 {
148 wxLogError( wxT( "Unknown frame: %s" ), frameName );
149 // Clean up
150 OnPgmExit();
151 return false;
152 }
153 }
154
155 if( appType == KICAD_MAIN_FRAME_T )
156 {
158 }
159 else
160 {
162 }
163
164 bool skipPythonInit = false;
165
166 if( appType == FRAME_BM2CMP || appType == FRAME_PL_EDITOR || appType == FRAME_GERBER
167 || appType == FRAME_CALC )
168 skipPythonInit = true;
169
170 if( !InitPgm( false, skipPythonInit ) )
171 return false;
172
176 m_bm.Init();
177
178 // Add search paths to feed the PGM_KICAD::SysSearch() function,
179 // currently limited in support to only look for project templates
180 {
181 SEARCH_STACK bases;
182
183 SystemDirsAppend( &bases );
184
185 for( unsigned i = 0; i < bases.GetCount(); ++i )
186 {
187 wxFileName fn( bases[i], wxEmptyString );
188
189 // Add KiCad template file path to search path list.
190 fn.AppendDir( wxT( "template" ) );
191
192 // Only add path if exists and can be read by the user.
193 if( fn.DirExists() && fn.IsDirReadable() )
194 m_bm.m_search.AddPaths( fn.GetPath() );
195 }
196
197 // The KICAD7_TEMPLATE_DIR takes precedence over the search stack template path.
198 ENV_VAR_MAP_CITER it = GetLocalEnvVariables().find( "KICAD7_TEMPLATE_DIR" );
199
200 if( it != GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
201 m_bm.m_search.Insert( it->second.GetValue(), 0 );
202
203 // We've been adding system (installed default) search paths so far, now for user paths
204 // The default user search path is inside KIPLATFORM::ENV::GetDocumentsPath()
206
207 // ...but the user can override that default with the KICAD_USER_TEMPLATE_DIR env var
208 it = GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" );
209
210 if( it != GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
211 m_bm.m_search.Insert( it->second.GetValue(), 0 );
212 }
213
214 wxFrame* frame = nullptr;
215 KIWAY_PLAYER* playerFrame = nullptr;
216 KICAD_MANAGER_FRAME* managerFrame = nullptr;
217
218 if( appType == KICAD_MAIN_FRAME_T )
219 {
220 managerFrame = new KICAD_MANAGER_FRAME( nullptr, wxT( "KiCad" ), wxDefaultPosition,
221 wxSize( 775, -1 ) );
222 frame = managerFrame;
223 }
224 else
225 {
226 // Use KIWAY to create a top window, which registers its existence also.
227 // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
228 // and is one of the types in FRAME_T.
229 playerFrame = Kiway.Player( appType, true );
230 frame = playerFrame;
231
232 if( frame == nullptr )
233 {
234 return false;
235 }
236 }
237
238 App().SetTopWindow( frame );
239
240 if( playerFrame )
241 App().SetAppDisplayName( playerFrame->GetAboutTitle() );
242
243 Kiway.SetTop( frame );
244
245 KICAD_SETTINGS* settings = static_cast<KICAD_SETTINGS*>( PgmSettings() );
246
247 wxString projToLoad;
248
249
250 if( playerFrame && parser.GetParamCount() )
251 {
252 // Now after the frame processing, the rest of the positional args are files
253 std::vector<wxString> fileArgs;
254 /*
255 gerbview handles multiple project data files, i.e. gerber files on
256 cmd line. Others currently do not, they handle only one. For common
257 code simplicity we simply pass all the arguments in however, each
258 program module can do with them what they want, ignore, complain
259 whatever. We don't establish policy here, as this is a multi-purpose
260 launcher.
261 */
262
263 for( size_t i = 0; i < parser.GetParamCount(); i++ )
264 fileArgs.push_back( parser.GetParam( i ) );
265
266 // special attention to a single argument: argv[1] (==argSet[0])
267 if( fileArgs.size() == 1 )
268 {
269 wxFileName argv1( fileArgs[0] );
270
271#if defined( PGM_DATA_FILE_EXT )
272 // PGM_DATA_FILE_EXT, if present, may be different for each compile,
273 // it may come from CMake on the compiler command line, but often does not.
274 // This facility is mostly useful for those program footprints
275 // supporting a single argv[1].
276 if( !argv1.GetExt() )
277 argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
278#endif
279 argv1.MakeAbsolute();
280
281 fileArgs[0] = argv1.GetFullPath();
282 }
283
284 // Use the KIWAY_PLAYER::OpenProjectFiles() API function:
285 if( !playerFrame->OpenProjectFiles( fileArgs ) )
286 {
287 // OpenProjectFiles() API asks that it report failure to the UI.
288 // Nothing further to say here.
289
290 // We've already initialized things at this point, but wx won't call OnExit if
291 // we fail out. Call our own cleanup routine here to ensure the relevant resources
292 // are freed at the right time (if they aren't, segfaults will occur).
293 OnPgmExit();
294
295 // Fail the process startup if the file could not be opened,
296 // although this is an optional choice, one that can be reversed
297 // also in the KIFACE specific OpenProjectFiles() return value.
298 return false;
299 }
300 }
301 else if( managerFrame )
302 {
303 if( App().argc > 1 )
304 {
305 wxFileName tmp = App().argv[1];
306
307 if( tmp.GetExt() != ProjectFileExtension && tmp.GetExt() != LegacyProjectFileExtension )
308 {
309 wxString msg;
310
311 msg.Printf( _( "File '%s'\ndoes not appear to be a valid KiCad project file." ),
312 tmp.GetFullPath() );
313 wxMessageDialog dlg( nullptr, msg, _( "Error" ), wxOK | wxICON_EXCLAMATION );
314 dlg.ShowModal();
315 }
316 else
317 {
318 projToLoad = tmp.GetFullPath();
319 }
320 }
321
322 // If no file was given as an argument, check that there was a file open.
323 if( projToLoad.IsEmpty() && settings->m_OpenProjects.size() )
324 {
325 wxString last_pro = settings->m_OpenProjects.front();
326 settings->m_OpenProjects.erase( settings->m_OpenProjects.begin() );
327
328 if( wxFileExists( last_pro ) )
329 {
330 // Try to open the last opened project,
331 // if a project name is not given when starting Kicad
332 projToLoad = last_pro;
333 }
334 }
335
336 // Do not attempt to load a non-existent project file.
337 if( !projToLoad.empty() )
338 {
339 wxFileName fn( projToLoad );
340
341 if( fn.Exists() )
342 {
343 fn.MakeAbsolute();
344
345 if( appType == KICAD_MAIN_FRAME_T )
346 {
347 managerFrame->LoadProject( fn );
348 }
349 }
350 }
351 }
352
353 frame->Show( true );
354 frame->Raise();
355
356 return true;
357}
358
359
361{
362 return 0;
363}
364
365
367{
369
371 {
373 m_settings_manager->Save();
374 }
375
376 // Destroy everything in PGM_KICAD,
377 // especially wxSingleInstanceCheckerImpl earlier than wxApp and earlier
378 // than static destruction would.
379 Destroy();
380}
381
382
383void PGM_KICAD::MacOpenFile( const wxString& aFileName )
384{
385#if defined(__WXMAC__)
386
387 KICAD_MANAGER_FRAME* frame = (KICAD_MANAGER_FRAME*) App().GetTopWindow();
388
389 if( !aFileName.empty() && wxFileExists( aFileName ) )
390 frame->LoadProject( wxFileName( aFileName ) );
391
392#endif
393}
394
395
397{
398 // unlike a normal destructor, this is designed to be called more
399 // than once safely:
400
401 m_bm.End();
402
404}
405
406
408
409
413struct APP_KICAD : public wxApp
414{
415 APP_KICAD() : wxApp()
416 {
417 // Init the environment each platform wants
419 }
420
421
422 bool OnInit() override
423 {
424 // Perform platform-specific init tasks
425 if( !KIPLATFORM::APP::Init() )
426 return false;
427
428 if( !program.OnPgmInit() )
429 {
431 return false;
432 }
433
434 return true;
435 }
436
437 int OnExit() override
438 {
440
441#if defined(__FreeBSD__)
442 // Avoid wxLog crashing when used in destructors.
443 wxLog::EnableLogging( false );
444#endif
445
446 return wxApp::OnExit();
447 }
448
449 int OnRun() override
450 {
451 try
452 {
453 return wxApp::OnRun();
454 }
455 catch( const std::exception& e )
456 {
457 wxLogError( wxT( "Unhandled exception class: %s what: %s" ),
458 FROM_UTF8( typeid( e ).name() ), FROM_UTF8( e.what() ) );
459 }
460 catch( const IO_ERROR& ioe )
461 {
462 wxLogError( ioe.What() );
463 }
464 catch(...)
465 {
466 wxLogError( wxT( "Unhandled exception of unknown type" ) );
467 }
468
469 return -1;
470 }
471
472 int FilterEvent( wxEvent& aEvent ) override
473 {
474 if( aEvent.GetEventType() == wxEVT_SHOW )
475 {
476 wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
477 wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
478
479 std::vector<void*>& dlgs = Pgm().m_ModalDialogs;
480
481 if( dialog )
482 {
483 if( event.IsShown() && dialog->IsModal() )
484 {
485 dlgs.push_back( dialog );
486 }
487 // Under GTK, sometimes the modal flag is cleared before hiding
488 else if( !event.IsShown() && !dlgs.empty() )
489 {
490 // If we close the expected dialog, remove it from our stack
491 if( dlgs.back() == dialog )
492 dlgs.pop_back();
493 // If an out-of-order, remove all dialogs added after the closed one
494 else if( auto it = std::find( dlgs.begin(), dlgs.end(), dialog ) ; it != dlgs.end() )
495 dlgs.erase( it, dlgs.end() );
496 }
497 }
498 }
499
500 return Event_Skip;
501 }
502
503#if defined( DEBUG )
507 bool ProcessEvent( wxEvent& aEvent ) override
508 {
509 if( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
510 {
511 wxKeyEvent* keyEvent = static_cast<wxKeyEvent*>( &aEvent );
512
513 if( keyEvent )
514 {
515 wxLogTrace( kicadTraceKeyEvent, "APP_KICAD::ProcessEvent %s", dump( *keyEvent ) );
516 }
517 }
518
519 aEvent.Skip();
520 return false;
521 }
522
530 bool OnExceptionInMainLoop() override
531 {
532 try
533 {
534 throw;
535 }
536 catch( const std::exception& e )
537 {
538 wxLogError( "Unhandled exception class: %s what: %s",
539 FROM_UTF8( typeid(e).name() ),
540 FROM_UTF8( e.what() ) );
541 }
542 catch( const IO_ERROR& ioe )
543 {
544 wxLogError( ioe.What() );
545 }
546 catch(...)
547 {
548 wxLogError( "Unhandled exception of unknown type" );
549 }
550
551 return false; // continue on. Return false to abort program
552 }
553#endif
554
560#if defined( __WXMAC__ )
561 void MacOpenFile( const wxString& aFileName ) override
562 {
563 Pgm().MacOpenFile( aFileName );
564 }
565#endif
566};
567
568IMPLEMENT_APP( APP_KICAD )
569
570
571// The C++ project manager supports one open PROJECT, so Prj() calls within
572// this link image need this function.
574{
575 return Kiway.Prj();
576}
577
const char * name
Definition: DXF_plotter.cpp:56
const wxString & GetAboutTitle() const
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
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:66
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:118
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:274
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:394
void SetCtlBits(int aCtlBits)
Overwrites previously set ctl bits, only for use in kicad.cpp to flip between standalone and manager ...
Definition: kiway.h:402
void OnKiwayEnd()
Definition: kiway.cpp:683
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:84
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition: kiway.cpp:192
static wxString GetUserTemplatesPath()
Gets the user path for custom templates.
Definition: paths.cpp:86
Container for data for KiCad programs.
Definition: pgm_base.h:95
virtual wxApp & App()
Returns a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition: pgm_base.cpp:163
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:839
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:331
void Destroy()
Definition: pgm_base.cpp:151
bool InitPgm(bool aHeadless=false, bool aSkipPyInit=false, bool aIsUnitTest=false)
Initialize this program.
Definition: pgm_base.cpp:401
std::vector< void * > m_ModalDialogs
Definition: pgm_base.h:306
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:135
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition: pgm_base.cpp:609
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:94
void Destroy()
Definition: kicad.cpp:396
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
Definition: kicad.cpp:383
void OnPgmExit()
Definition: kicad.cpp:366
APP_SETTINGS_BASE * PgmSettings()
Definition: pgm_kicad.h:55
int OnPgmRun()
Definition: kicad.cpp:360
BIN_MOD m_bm
Definition: pgm_kicad.h:68
Container for project specific data.
Definition: project.h:64
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:40
@ FRAME_CALC
Definition: frame_type.h:59
@ FRAME_BM2CMP
Definition: frame_type.h:57
@ FRAME_SCH
Definition: frame_type.h:34
@ FRAME_T_COUNT
Definition: frame_type.h:66
@ FRAME_PL_EDITOR
Definition: frame_type.h:55
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:41
@ FRAME_GERBER
Definition: frame_type.h:53
@ KICAD_MAIN_FRAME_T
Definition: frame_type.h:64
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:88
PROJECT & Prj()
Definition: kicad.cpp:573
static PGM_KICAD program
Definition: kicad.cpp:72
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:75
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition: kicad.cpp:61
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:82
#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.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
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:414
int OnRun() override
Definition: kicad.cpp:449
APP_KICAD()
Definition: kicad.cpp:415
bool OnInit() override
Definition: kicad.cpp:422
int OnExit() override
Definition: kicad.cpp:437
int FilterEvent(wxEvent &aEvent) override
Definition: kicad.cpp:472
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.