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 <confirm.h>
49 
50 #if defined(_WIN32)
51 #include <config.h>
52 #include <VersionHelpers.h>
53 #endif
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 #if defined (__LINUX__)
135  APP_SINGLE_TOP(): wxApp()
136  {
137  // Disable proxy menu in Unity window manager. Only usual menubar works with wxWidgets (at least <= 3.1)
138  // When the proxy menu menubar is enable, some important things for us do not work: menuitems UI events and shortcuts.
139  wxString wm;
140 
141  if( wxGetEnv( wxT( "XDG_CURRENT_DESKTOP" ), &wm ) && wm.CmpNoCase( wxT( "Unity" ) ) == 0 )
142  {
143  wxSetEnv ( wxT("UBUNTU_MENUPROXY" ), wxT( "0" ) );
144  }
145 
146  // Force the use of X11 backend (or wayland-x11 compatibilty layer). This is required until wxWidgets
147  // supports the Wayland compositors
148  wxSetEnv( wxT( "GDK_BACKEND" ), wxT( "x11" ) );
149 
150  // Disable overlay scrollbars as they mess up wxWidgets window sizing and cause excessive redraw requests
151  wxSetEnv( wxT( "GTK_OVERLAY_SCROLLING" ), wxT( "0" ) );
152 
153  // Set GTK2-style input instead of xinput2. This disables touchscreen and smooth scrolling
154  // Needed to ensure that we are not getting multiple mouse scroll events
155  wxSetEnv( wxT( "GDK_CORE_DEVICE_EVENTS" ), wxT( "1" ) );
156  }
157 #endif
158 
159  bool OnInit() override
160  {
161 #if defined( _MSC_VER ) && defined( DEBUG )
162  // wxWidgets turns on leak dumping in debug but its "flawed" and will falsely dump for half a hour
163  // _CRTDBG_ALLOC_MEM_DF is the usual default for MSVC
164  _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF );
165 #endif
166 
167 #if defined( _WIN32 ) && defined( PYTHON_VERSION_MAJOR ) \
168  && ( ( PYTHON_VERSION_MAJOR == 3 && PYTHON_VERSION_MINOR >= 8 ) \
169  || PYTHON_VERSION_MAJOR > 3 )
170 
171  // Python 3.8 switched to Windows 8+ API, we do not support Windows 7 and will not attempt to hack around it
172  // Gracefully inform the user and refuse to start (because python will crash us if we continue)
173  if( !IsWindows8OrGreater() )
174  {
175  wxMessageBox( _( "Windows 7 and older is no longer supported by KiCad and its dependencies." ),
176  _( "Unsupported Operating System" ), wxOK | wxICON_ERROR );
177  return false;
178  }
179 #endif
180 
181  // Force wxHtmlWinParser initialization when a wxHtmlWindow is used only
182  // in a shared library (.so or .dll file)
183  // Otherwise the Html text is displayed as plain text.
184  HtmlModule html_init;
185 
186  try
187  {
188  return program.OnPgmInit();
189  }
190  catch( const std::exception& e )
191  {
192  wxLogError( wxT( "Unhandled exception class: %s what: %s" ),
193  FROM_UTF8( typeid( e ).name() ), FROM_UTF8( e.what() ) );
194  }
195  catch( const IO_ERROR& ioe )
196  {
197  wxLogError( ioe.What() );
198  }
199  catch(...)
200  {
201  wxLogError( wxT( "Unhandled exception of unknown type" ) );
202  }
203 
204  program.OnPgmExit();
205 
206  return false;
207  }
208 
209  int OnExit() override
210  {
211  // Fixes segfault when wxPython scripting is enabled.
212 #if defined( KICAD_SCRIPTING_WXPYTHON )
213  program.OnPgmExit();
214 #endif
215  return wxApp::OnExit();
216  }
217 
218  int OnRun() override
219  {
220  int ret = -1;
221 
222  try
223  {
224  ret = wxApp::OnRun();
225  }
226  catch( const std::exception& e )
227  {
228  wxLogError( wxT( "Unhandled exception class: %s what: %s" ),
229  FROM_UTF8( typeid( e ).name() ), FROM_UTF8( e.what() ) );
230  }
231  catch( const IO_ERROR& ioe )
232  {
233  wxLogError( ioe.What() );
234  }
235  catch(...)
236  {
237  wxLogError( wxT( "Unhandled exception of unknown type" ) );
238  }
239 
240  // Works properly when wxPython scripting is disabled.
241 #if !defined( KICAD_SCRIPTING_WXPYTHON )
242  program.OnPgmExit();
243 #endif
244  return ret;
245  }
246 
247  int FilterEvent( wxEvent& aEvent ) override
248  {
249  if( aEvent.GetEventType() == wxEVT_SHOW )
250  {
251  wxShowEvent& event = static_cast<wxShowEvent&>( aEvent );
252  wxDialog* dialog = dynamic_cast<wxDialog*>( event.GetEventObject() );
253 
254  if( dialog && dialog->IsModal() )
255  Pgm().m_ModalDialogCount += event.IsShown() ? 1 : -1;
256  }
257 
258  return Event_Skip;
259  }
260 
261 #if defined( DEBUG )
262 
269  virtual bool OnExceptionInMainLoop() override
270  {
271  try
272  {
273  throw;
274  }
275  catch( const std::exception& e )
276  {
277  wxLogError( "Unhandled exception class: %s what: %s",
278  FROM_UTF8( typeid(e).name() ),
279  FROM_UTF8( e.what() ) );
280  }
281  catch( const IO_ERROR& ioe )
282  {
283  wxLogError( ioe.What() );
284  }
285  catch(...)
286  {
287  wxLogError( "Unhandled exception of unknown type" );
288  }
289 
290  return false; // continue on. Return false to abort program
291  }
292 #endif
293 
294 #ifdef __WXMAC__
295 
302  void MacOpenFile( const wxString& aFileName ) override
303  {
304  Pgm().MacOpenFile( aFileName );
305  }
306 
307 #endif
308 };
309 
310 IMPLEMENT_APP( APP_SINGLE_TOP )
311 
312 
313 bool PGM_SINGLE_TOP::OnPgmInit()
314 {
315 #if defined(DEBUG)
316  wxString absoluteArgv0 = wxStandardPaths::Get().GetExecutablePath();
317 
318  if( !wxIsAbsolutePath( absoluteArgv0 ) )
319  {
320  wxLogError( wxT( "No meaningful argv[0]" ) );
321  return false;
322  }
323 #endif
324 
325  if( !InitPgm() )
326  {
327  // Clean up
328  OnPgmExit();
329  return false;
330  }
331 
332 #if !defined(BUILD_KIWAY_DLL)
333 
334  // Only bitmap2component and pcb_calculator use this code currently, as they
335  // are not split to use single_top as a link image separate from a *.kiface.
336  // i.e. they are single part link images so don't need to load a *.kiface.
337 
338  // Get the getter, it is statically linked into this binary image.
340 
341  int kiface_version;
342 
343  // Get the KIFACE.
344  KIFACE* kiface = getter( &kiface_version, KIFACE_VERSION, this );
345 
346  // Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE
347  // in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways.
348  Kiway.set_kiface( KIWAY::KifaceType( TOP_FRAME ), kiface );
349 #endif
350 
351  static const wxCmdLineEntryDesc desc[] = {
352  { wxCMD_LINE_OPTION, "f", "frame", "Frame to load", wxCMD_LINE_VAL_STRING, 0 },
353  { wxCMD_LINE_PARAM, nullptr, nullptr, "File to load", wxCMD_LINE_VAL_STRING,
354  wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
355  { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
356  };
357 
358  wxCmdLineParser parser( App().argc, App().argv );
359  parser.SetDesc( desc );
360  parser.Parse( false );
361 
362  FRAME_T appType = TOP_FRAME;
363 
364  const struct
365  {
366  wxString name;
367  FRAME_T type;
368  } frameTypes[] = {
369  { wxT( "pcb" ), FRAME_PCB_EDITOR },
370  { wxT( "fpedit" ), FRAME_FOOTPRINT_EDITOR },
371  { wxT( "" ), FRAME_T_COUNT }
372  };
373 
374  wxString frameName;
375 
376  if( parser.Found( "frame", &frameName ) )
377  {
378  appType = FRAME_T_COUNT;
379 
380  for( const auto& it : frameTypes )
381  {
382  if( it.name == frameName )
383  appType = it.type;
384  }
385 
386  if( appType == FRAME_T_COUNT )
387  {
388  wxLogError( wxT( "Unknown frame: %s" ), frameName );
389  // Clean up
390  OnPgmExit();
391  return false;
392  }
393  }
394 
395  // Tell the settings manager about the current Kiway
397 
398  // Use KIWAY to create a top window, which registers its existence also.
399  // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
400  // and is one of the types in FRAME_T.
401  KIWAY_PLAYER* frame = Kiway.Player( appType, true );
402 
403  if( frame == nullptr )
404  {
405  return false;
406  }
407 
408  Kiway.SetTop( frame );
409 
410  App().SetTopWindow( frame ); // wxApp gets a face.
411 
412  // Allocate a slice of time to show the frame and update wxWidgets widgets
413  // (especially setting valid sizes) after creating frame and before calling
414  // OpenProjectFiles() that can update/use some widgets.
415  // The 2 calls to wxSafeYield are needed on wxGTK for best results.
416  wxSafeYield();
417  frame->Show();
418  wxSafeYield();
419 
420  // Individual frames may provide additional option/switch processing, but for compatibility,
421  // any positional arguments are treated as a list of files to pass to OpenProjectFiles
422  frame->ParseArgs( parser );
423 
424  // Now after the frame processing, the rest of the positional args are files
425  std::vector<wxString> fileArgs;
426 
427  if( parser.GetParamCount() )
428  {
429  /*
430  gerbview handles multiple project data files, i.e. gerber files on
431  cmd line. Others currently do not, they handle only one. For common
432  code simplicity we simply pass all the arguments in however, each
433  program module can do with them what they want, ignore, complain
434  whatever. We don't establish policy here, as this is a multi-purpose
435  launcher.
436  */
437 
438  for( size_t i = 0; i < parser.GetParamCount(); i++ )
439  fileArgs.push_back( parser.GetParam( i ) );
440 
441  // special attention to a single argument: argv[1] (==argSet[0])
442  if( fileArgs.size() == 1 )
443  {
444  wxFileName argv1( fileArgs[0] );
445 
446 #if defined(PGM_DATA_FILE_EXT)
447  // PGM_DATA_FILE_EXT, if present, may be different for each compile,
448  // it may come from CMake on the compiler command line, but often does not.
449  // This facility is mostly useful for those program footprints
450  // supporting a single argv[1].
451  if( !argv1.GetExt() )
452  argv1.SetExt( wxT( PGM_DATA_FILE_EXT ) );
453 #endif
454  argv1.MakeAbsolute();
455 
456  fileArgs[0] = argv1.GetFullPath();
457  }
458 
459  // Use the KIWAY_PLAYER::OpenProjectFiles() API function:
460  if( !frame->OpenProjectFiles( fileArgs ) )
461  {
462  // OpenProjectFiles() API asks that it report failure to the UI.
463  // Nothing further to say here.
464 
465  // We've already initialized things at this point, but wx won't call OnExit if
466  // we fail out. Call our own cleanup routine here to ensure the relevant resources
467  // are freed at the right time (if they aren't, segfaults will occur).
468  OnPgmExit();
469 
470  // Fail the process startup if the file could not be opened,
471  // although this is an optional choice, one that can be reversed
472  // also in the KIFACE specific OpenProjectFiles() return value.
473  return false;
474  }
475  }
476 
477  return true;
478 }
int OnRun() override
Definition: single_top.cpp:218
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
BITMAP2CMP_SETTINGS kiface
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:61
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:209
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:295
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:541
Container for data for KiCad programs.
Definition: pgm_base.h:131
#define KIFACE_VERSION
The KIWAY and KIFACE classes are used to communicate between various process modules,...
Definition: kiway.h:108
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:140
void Destroy()
Definition: pgm_base.cpp:129
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:345
void OnKiwayEnd()
Definition: kiway.cpp:602
bool OnInit() override
Definition: single_top.cpp:159
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
bool set_kiface(FACE_T aFaceType, KIFACE *aKiface)
Definition: kiway.h:402
std::unique_ptr< SETTINGS_MANAGER > m_settings_manager
Definition: pgm_base.h:349
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:29
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:260
#define KIFACE_GETTER
Definition: kiway.h:109
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:80
SETTINGS_MANAGER * GetSettingsManager()
see class PGM_BASE
virtual const wxString & GetExecutablePath() const
Definition: pgm_base.h:198
const char * name
Definition: DXF_plotter.cpp:59
PGM_SINGLE_TOP program
#define _(s)
Definition: 3d_actions.cpp:33
Implement a participant in the KIWAY alchemy.
Definition: kiway.h:147
int FilterEvent(wxEvent &aEvent) override
Definition: single_top.cpp:247
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:136
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:155
KIWAY Kiway