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-2020 KiCad Developers, see CHANGELOG.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  if( !InitPgm() )
285  {
286  // Clean up
287  OnPgmExit();
288  return false;
289  }
290 
291 #if !defined(BUILD_KIWAY_DLL)
292 
293  // Only bitmap2component and pcb_calculator use this code currently, as they
294  // are not split to use single_top as a link image separate from a *.kiface.
295  // i.e. they are single part link images so don't need to load a *.kiface.
296 
297  // Get the getter, it is statically linked into this binary image.
298  KIFACE_GETTER_FUNC* ki_getter = &KIFACE_GETTER;
299 
300  int kiface_version;
301 
302  // Get the KIFACE.
303  KIFACE* kiface = ki_getter( &kiface_version, KIFACE_VERSION, this );
304 
305  // Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE
306  // in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways.
307  Kiway.set_kiface( KIWAY::KifaceType( TOP_FRAME ), kiface );
308 #endif
309 
310  static const wxCmdLineEntryDesc desc[] = {
311  { wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
312  { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
313  wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
314  { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
315  };
316 
317  wxCmdLineParser parser( App().argc, App().argv );
318  parser.SetDesc( desc );
319  parser.Parse( false );
320 
321  FRAME_T appType = TOP_FRAME;
322 
323  const struct
324  {
325  wxString name;
326  FRAME_T type;
327  } frameTypes[] = {
328  { wxT( "pcb" ), FRAME_PCB_EDITOR },
329  { wxT( "fpedit" ), FRAME_FOOTPRINT_EDITOR },
330  { wxT( "" ), FRAME_T_COUNT }
331  };
332 
333  wxString frameName;
334 
335  if( parser.Found( "frame", &frameName ) )
336  {
337  appType = FRAME_T_COUNT;
338 
339  for( const auto& it : frameTypes )
340  {
341  if( it.name == frameName )
342  appType = it.type;
343  }
344 
345  if( appType == FRAME_T_COUNT )
346  {
347  wxLogError( wxT( "Unknown frame: %s" ), frameName );
348  // Clean up
349  OnPgmExit();
350  return false;
351  }
352  }
353 
354  // Tell the settings manager about the current Kiway
356 
357  // Use KIWAY to create a top window, which registers its existence also.
358  // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
359  // and is one of the types in FRAME_T.
360  KIWAY_PLAYER* frame = Kiway.Player( appType, true );
361 
362  if( frame == nullptr )
363  {
364  return false;
365  }
366 
367  Kiway.SetTop( frame );
368 
369  App().SetTopWindow( frame ); // wxApp gets a face.
370  App().SetAppDisplayName( frame->GetAboutTitle() );
371 
372  // Allocate a slice of time to show the frame and update wxWidgets widgets
373  // (especially setting valid sizes) after creating frame and before calling
374  // OpenProjectFiles() that can update/use some widgets.
375  // The 2 calls to wxSafeYield are needed on wxGTK for best results.
376  wxSafeYield();
377  frame->Show();
378  wxSafeYield();
379 
380  // Individual frames may provide additional option/switch processing, but for compatibility,
381  // any positional arguments are treated as a list of files to pass to OpenProjectFiles
382  frame->ParseArgs( parser );
383 
384  // Now after the frame processing, the rest of the positional args are files
385  std::vector<wxString> fileArgs;
386 
387  if( parser.GetParamCount() )
388  {
389  /*
390  gerbview handles multiple project data files, i.e. gerber files on
391  cmd line. Others currently do not, they handle only one. For common
392  code simplicity we simply pass all the arguments in however, each
393  program module can do with them what they want, ignore, complain
394  whatever. We don't establish policy here, as this is a multi-purpose
395  launcher.
396  */
397 
398  for( size_t i = 0; i < parser.GetParamCount(); i++ )
399  fileArgs.push_back( parser.GetParam( i ) );
400 
401  // special attention to a single argument: argv[1] (==argSet[0])
402  if( fileArgs.size() == 1 )
403  {
404  wxFileName argv1( fileArgs[0] );
405 
406 #if defined(PGM_DATA_FILE_EXT)
407  // PGM_DATA_FILE_EXT, if present, may be different for each compile,
408  // it may come from CMake on the compiler command line, but often does not.
409  // This facility is mostly useful for those program footprints
410  // supporting a single argv[1].
411  if( !argv1.GetExt() )
412  argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
413 #endif
414  argv1.MakeAbsolute();
415 
416  fileArgs[0] = argv1.GetFullPath();
417  }
418 
419  // Use the KIWAY_PLAYER::OpenProjectFiles() API function:
420  if( !frame->OpenProjectFiles( fileArgs ) )
421  {
422  // OpenProjectFiles() API asks that it report failure to the UI.
423  // Nothing further to say here.
424 
425  // We've already initialized things at this point, but wx won't call OnExit if
426  // we fail out. Call our own cleanup routine here to ensure the relevant resources
427  // are freed at the right time (if they aren't, segfaults will occur).
428  OnPgmExit();
429 
430  // Fail the process startup if the file could not be opened,
431  // although this is an optional choice, one that can be reversed
432  // also in the KIFACE specific OpenProjectFiles() return value.
433  return false;
434  }
435  }
436 
437  return true;
438 }
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:367
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:107
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:373
void OnKiwayEnd()
Definition: kiway.cpp:630
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:402
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition: pgm_base.h:307
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:456
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:258
#define KIFACE_GETTER
Definition: kiway.h:108
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:82
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:59
PGM_SINGLE_TOP program
const wxString & GetAboutTitle() const
void Init()
Perform environment initialization tasks.
Implement a participant in the KIWAY alchemy.
Definition: kiway.h:146
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:154
KIWAY Kiway