KiCad PCB EDA Suite
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 <dick@softplc.com>
5  * Copyright (C) 2014-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 
25 
26 /*
27 
28  This is a program launcher for a single KIFACE DSO. It only mimics a KIWAY,
29  not actually implements one, since only a single DSO is supported by it.
30 
31  It is compiled multiple times, once for each standalone program and as such
32  gets different compiler command line supplied #defines from CMake.
33 
34 */
35 
36 
37 #include <typeinfo>
38 #include <wx/cmdline.h>
39 #include <wx/filename.h>
40 #include <wx/stdpaths.h>
41 #include <wx/snglinst.h>
42 #include <wx/html/htmlwin.h>
43 
44 #include <kiway.h>
45 #include <pgm_base.h>
46 #include <kiway_player.h>
47 #include <macros.h>
48 #include <confirm.h>
50 
51 #include <kiplatform/app.h>
52 #include <kiplatform/environment.h>
53 
54 
55 // Only a single KIWAY is supported in this single_top top level component,
56 // which is dedicated to loading only a single DSO.
58 
59 
60 // implement a PGM_BASE and a wxApp side by side:
61 
66 static struct PGM_SINGLE_TOP : public PGM_BASE
67 {
68  bool OnPgmInit();
69 
70  void OnPgmExit()
71  {
72  Kiway.OnKiwayEnd();
73 
74  if( m_settings_manager && m_settings_manager->IsOK() )
75  {
77  m_settings_manager->Save();
78  }
79 
80  // Destroy everything in PGM_BASE, especially wxSingleInstanceCheckerImpl
81  // earlier than wxApp and earlier than static destruction would.
83  }
84 
85  void MacOpenFile( const wxString& aFileName ) override
86  {
87  wxFileName filename( aFileName );
88 
89  if( filename.FileExists() )
90  {
91  #if 0
92  // this pulls in EDA_DRAW_FRAME type info, which we don't want in
93  // the single_top link image.
94  KIWAY_PLAYER* frame = dynamic_cast<KIWAY_PLAYER*>( App().GetTopWindow() );
95  #else
96  KIWAY_PLAYER* frame = (KIWAY_PLAYER*) App().GetTopWindow();
97  #endif
98  if( frame )
99  frame->OpenProjectFiles( std::vector<wxString>( 1, aFileName ) );
100  }
101  }
102 
103 } program;
104 
105 
107 {
108  return program;
109 }
110 
111 // A module to allow Html module initialization/cleanup
112 // When a wxHtmlWindow is used *only* in a dll/so module, the Html text is displayed
113 // as plain text.
114 // This helper class is just used to force wxHtmlWinParser initialization
115 // see https://groups.google.com/forum/#!topic/wx-users/FF0zv5qGAT0
116 class HtmlModule: public wxModule
117 {
118 public:
120  virtual bool OnInit() override { AddDependency( CLASSINFO( wxHtmlWinParser ) ); return true; };
121  virtual void OnExit() override {};
122 private:
124 };
126 
132 struct APP_SINGLE_TOP : public wxApp
133 {
134  APP_SINGLE_TOP() : wxApp()
135  {
136  // Init the environment each platform wants
138  }
139 
140 
141  bool OnInit() override
142  {
143  // Perform platform-specific init tasks
144  if( !KIPLATFORM::APP::Init() )
145  return false;
146 
147  // Force wxHtmlWinParser initialization when a wxHtmlWindow is used only
148  // in a shared library (.so or .dll file)
149  // Otherwise the Html text is displayed as plain text.
150  HtmlModule html_init;
151 
152  try
153  {
154  return program.OnPgmInit();
155  }
156  catch( const std::exception& e )
157  {
158  wxLogError( wxT( "Unhandled exception class: %s what: %s" ),
159  FROM_UTF8( typeid( e ).name() ), FROM_UTF8( e.what() ) );
160  }
161  catch( const IO_ERROR& ioe )
162  {
163  wxLogError( ioe.What() );
164  }
165  catch(...)
166  {
167  wxLogError( wxT( "Unhandled exception of unknown type" ) );
168  }
169 
170  program.OnPgmExit();
171 
172  return false;
173  }
174 
175  int OnExit() override
176  {
177  program.OnPgmExit();
178  return wxApp::OnExit();
179  }
180 
181  int OnRun() override
182  {
183  int ret = -1;
184 
185  try
186  {
187  ret = wxApp::OnRun();
188  }
189  catch( const std::exception& e )
190  {
191  wxLogError( wxT( "Unhandled exception class: %s what: %s" ),
192  FROM_UTF8( typeid( e ).name() ), FROM_UTF8( e.what() ) );
193  }
194  catch( const IO_ERROR& ioe )
195  {
196  wxLogError( ioe.What() );
197  }
198  catch(...)
199  {
200  wxLogError( wxT( "Unhandled exception of unknown type" ) );
201  }
202 
203  return ret;
204  }
205 
206  int FilterEvent( wxEvent& aEvent ) override
207  {
208  if( aEvent.GetEventType() == wxEVT_SHOW )
209  {
210  wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
211  wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
212 
213  if( dialog && dialog->IsModal() )
214  Pgm().m_ModalDialogCount += event.IsShown() ? 1 : -1;
215  }
216 
217  return Event_Skip;
218  }
219 
220 #if defined( DEBUG )
221 
228  virtual bool OnExceptionInMainLoop() override
229  {
230  try
231  {
232  throw;
233  }
234  catch( const std::exception& e )
235  {
236  wxLogError( "Unhandled exception class: %s what: %s",
237  FROM_UTF8( typeid(e).name() ),
238  FROM_UTF8( e.what() ) );
239  }
240  catch( const IO_ERROR& ioe )
241  {
242  wxLogError( ioe.What() );
243  }
244  catch(...)
245  {
246  wxLogError( "Unhandled exception of unknown type" );
247  }
248 
249  return false; // continue on. Return false to abort program
250  }
251 #endif
252 
253 #ifdef __WXMAC__
254 
261  void MacOpenFile( const wxString& aFileName ) override
262  {
263  Pgm().MacOpenFile( aFileName );
264  }
265 
266 #endif
267 };
268 
269 IMPLEMENT_APP( APP_SINGLE_TOP )
270 
271 
272 bool PGM_SINGLE_TOP::OnPgmInit()
273 {
274 #if defined(DEBUG)
275  wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
276 
277  if( !wxIsAbsolutePath( absoluteArgv0 ) )
278  {
279  wxLogError( wxT( "No meaningful argv[0]" ) );
280  return false;
281  }
282 #endif
283 
284  // Not all kicad applications use the python stuff. skip python init
285  // for these apps.
286  bool skip_python_initialization = false;
287 #if defined( BITMAP_2_CMP ) || defined( PL_EDITOR ) || defined( GERBVIEW ) ||\
288  defined( PCB_CALCULATOR_BUILD )
289  skip_python_initialization = true;
290 #endif
291 
292  if( !InitPgm( false, skip_python_initialization ) )
293  {
294  // Clean up
295  OnPgmExit();
296  return false;
297  }
298 
299 #if !defined(BUILD_KIWAY_DLL)
300 
301  // Only bitmap2component and pcb_calculator use this code currently, as they
302  // are not split to use single_top as a link image separate from a *.kiface.
303  // i.e. they are single part link images so don't need to load a *.kiface.
304 
305  // Get the getter, it is statically linked into this binary image.
306  KIFACE_GETTER_FUNC* ki_getter = &KIFACE_GETTER;
307 
308  int kiface_version;
309 
310  // Get the KIFACE.
311  KIFACE* kiface = ki_getter( &kiface_version, KIFACE_VERSION, this );
312 
313  // Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE
314  // in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways.
315  Kiway.set_kiface( KIWAY::KifaceType( TOP_FRAME ), kiface );
316 #endif
317 
318  static const wxCmdLineEntryDesc desc[] = {
319  { wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
320  { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
321  wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
322  { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
323  };
324 
325  wxCmdLineParser parser( App().argc, App().argv );
326  parser.SetDesc( desc );
327  parser.Parse( false );
328 
329  FRAME_T appType = TOP_FRAME;
330 
331  const struct
332  {
333  wxString name;
334  FRAME_T type;
335  } frameTypes[] = {
336  { wxT( "pcb" ), FRAME_PCB_EDITOR },
337  { wxT( "fpedit" ), FRAME_FOOTPRINT_EDITOR },
338  { wxT( "" ), FRAME_T_COUNT }
339  };
340 
341  wxString frameName;
342 
343  if( parser.Found( "frame", &frameName ) )
344  {
345  appType = FRAME_T_COUNT;
346 
347  for( const auto& it : frameTypes )
348  {
349  if( it.name == frameName )
350  appType = it.type;
351  }
352 
353  if( appType == FRAME_T_COUNT )
354  {
355  wxLogError( wxT( "Unknown frame: %s" ), frameName );
356  // Clean up
357  OnPgmExit();
358  return false;
359  }
360  }
361 
362  // Tell the settings manager about the current Kiway
364 
365  // Use KIWAY to create a top window, which registers its existence also.
366  // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
367  // and is one of the types in FRAME_T.
368  KIWAY_PLAYER* frame = Kiway.Player( appType, true );
369 
370  if( frame == nullptr )
371  {
372  return false;
373  }
374 
375  Kiway.SetTop( frame );
376 
377  App().SetTopWindow( frame ); // wxApp gets a face.
378  App().SetAppDisplayName( frame->GetAboutTitle() );
379 
380  // Allocate a slice of time to show the frame and update wxWidgets widgets
381  // (especially setting valid sizes) after creating frame and before calling
382  // OpenProjectFiles() that can update/use some widgets.
383  // The 2 calls to wxSafeYield are needed on wxGTK for best results.
384  wxSafeYield();
385  frame->Show();
386  wxSafeYield();
387 
388  // Individual frames may provide additional option/switch processing, but for compatibility,
389  // any positional arguments are treated as a list of files to pass to OpenProjectFiles
390  frame->ParseArgs( parser );
391 
392  // Now after the frame processing, the rest of the positional args are files
393  std::vector<wxString> fileArgs;
394 
395  if( parser.GetParamCount() )
396  {
397  /*
398  gerbview handles multiple project data files, i.e. gerber files on
399  cmd line. Others currently do not, they handle only one. For common
400  code simplicity we simply pass all the arguments in however, each
401  program module can do with them what they want, ignore, complain
402  whatever. We don't establish policy here, as this is a multi-purpose
403  launcher.
404  */
405 
406  for( size_t i = 0; i < parser.GetParamCount(); i++ )
407  fileArgs.push_back( parser.GetParam( i ) );
408 
409  // special attention to a single argument: argv[1] (==argSet[0])
410  if( fileArgs.size() == 1 )
411  {
412  wxFileName argv1( fileArgs[0] );
413 
414 #if defined(PGM_DATA_FILE_EXT)
415  // PGM_DATA_FILE_EXT, if present, may be different for each compile,
416  // it may come from CMake on the compiler command line, but often does not.
417  // This facility is mostly useful for those program footprints
418  // supporting a single argv[1].
419  if( !argv1.GetExt() )
420  argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
421 #endif
422  argv1.MakeAbsolute();
423 
424  fileArgs[0] = argv1.GetFullPath();
425  }
426 
427  // Use the KIWAY_PLAYER::OpenProjectFiles() API function:
428  if( !frame->OpenProjectFiles( fileArgs ) )
429  {
430  // OpenProjectFiles() API asks that it report failure to the UI.
431  // Nothing further to say here.
432 
433  // We've already initialized things at this point, but wx won't call OnExit if
434  // we fail out. Call our own cleanup routine here to ensure the relevant resources
435  // are freed at the right time (if they aren't, segfaults will occur).
436  OnPgmExit();
437 
438  // Fail the process startup if the file could not be opened,
439  // although this is an optional choice, one that can be reversed
440  // also in the KIFACE specific OpenProjectFiles() return value.
441  return false;
442  }
443  }
444 
445  return true;
446 }
int OnRun() override
Definition: single_top.cpp:181
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
BITMAP2CMP_SETTINGS kiface
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:64
void MacOpenFile(const wxString &aFileName) override
Specific to MacOSX (not used under Linux or Windows).
Definition: single_top.cpp:85
int OnExit() override
Definition: single_top.cpp:175
Struct PGM_SINGLE_TOP implements PGM_BASE with its own OnPgmInit() and OnPgmExit().
Definition: single_top.cpp:66
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
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:320
void OnPgmExit()
Definition: single_top.cpp:70
This file is part of the common library.
void SaveCommonSettings()
Save the program (process) settings subset which are stored .kicad_common.
Definition: pgm_base.cpp:371
Container for data for KiCad programs.
Definition: pgm_base.h:93
#define KIFACE_VERSION
The KIWAY and KIFACE classes are used to communicate between various process modules,...
Definition: kiway.h:109
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:32
wxDECLARE_DYNAMIC_CLASS(HtmlModule)
virtual wxApp & App()
Returns a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition: pgm_base.cpp:131
void Destroy()
Definition: pgm_base.cpp:123
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:382
void OnKiwayEnd()
Definition: kiway.cpp:640
bool OnInit() override
Definition: single_top.cpp:141
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
This file contains miscellaneous commonly used macros and functions.
bool set_kiface(FACE_T aFaceType, KIFACE *aKiface)
Definition: kiway.h:404
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition: pgm_base.h:310
void SetKiway(KIWAY *aKiway)
Associate this setting manager with the given Kiway.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
KIFACE * KIFACE_GETTER_FUNC(int *aKIFACEversion, int aKIWAYversion, PGM_BASE *aProgram)
Point to the one and only KIFACE export.
Definition: kiway.h:458
virtual bool OnInit() override
Definition: single_top.cpp:120
virtual void OnExit() override
Definition: single_top.cpp:121
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:260
#define KIFACE_GETTER
Definition: kiway.h:110
wxIMPLEMENT_DYNAMIC_CLASS(HtmlModule, wxModule)
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:81
SETTINGS_MANAGER * GetSettingsManager()
bool Init()
Perform application-specific initialization tasks.
Definition: gtk/app.cpp:40
see class PGM_BASE
virtual const wxString & GetExecutablePath() const
Definition: pgm_base.h:160
const char * name
Definition: DXF_plotter.cpp:56
PGM_SINGLE_TOP program
const wxString & GetAboutTitle() const
void Init()
Perform environment initialization tasks.
Implement a participant in the KIWAY alchemy.
Definition: kiway.h:148
int FilterEvent(wxEvent &aEvent) override
Definition: single_top.cpp:206
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
virtual void ParseArgs(wxCmdLineParser &aParser)
Handle command-line arguments in a frame-specific way.
Definition: kiway_player.h:139
Struct APP_SINGLE_TOP implements a bare naked wxApp (so that we don't become dependent on functionali...
Definition: single_top.cpp:132
#define KFCTL_STANDALONE
Running as a standalone Top.
Definition: kiway.h:156
KIWAY Kiway