KiCad PCB EDA Suite
3d_plugin_manager.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) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 
25 #include <utility>
26 #include <iostream>
27 #include <sstream>
28 
29 #include <wx/config.h>
30 #include <wx/dir.h>
31 #include <wx/dynlib.h>
32 #include <wx/filename.h>
33 #include <wx/log.h>
34 #include <wx/stdpaths.h>
35 #include <wx/string.h>
36 
37 #include "common.h"
38 #include "pgm_base.h"
39 #include "3d_plugin_dir.h"
40 #include "3d_plugin_manager.h"
41 #include "plugins/3d/3d_plugin.h"
42 #include "3d_cache/sg/scenegraph.h"
43 #include "plugins/ldr/3d/pluginldr3D.h"
44 
45 #define MASK_3D_PLUGINMGR "3D_PLUGIN_MANAGER"
46 
47 
49 {
50  // create the initial file filter list entry
51  m_FileFilters.push_back( _( "All Files (*.*)|*.*" ) );
52 
53  // discover and load plugins
54  loadPlugins();
55 
56 #ifdef DEBUG
57  if( !m_ExtMap.empty() )
58  {
59  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator sM = m_ExtMap.begin();
60  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator eM = m_ExtMap.end();
61  wxLogTrace( MASK_3D_PLUGINMGR, " * Extension [plugin name]:\n" );
62 
63  while( sM != eM )
64  {
65  wxLogTrace( MASK_3D_PLUGINMGR, " + '%s' [%s]\n", sM->first.GetData(),
66  sM->second->GetKicadPluginName() );
67  ++sM;
68  }
69 
70  }
71  else
72  {
73  wxLogTrace( MASK_3D_PLUGINMGR, " * No plugins available\n" );
74  }
75 
76 
77  if( !m_FileFilters.empty() )
78  {
80  std::list< wxString >::const_iterator sFF = m_FileFilters.begin();
81  std::list< wxString >::const_iterator eFF = m_FileFilters.end();
82  wxLogTrace( MASK_3D_PLUGINMGR, " * File filters:\n" );
83 
84  while( sFF != eFF )
85  {
86  wxLogTrace( MASK_3D_PLUGINMGR, " + '%s'\n", (*sFF).GetData() );
87  ++sFF;
88  }
89  }
90  else
91  {
92  wxLogTrace( MASK_3D_PLUGINMGR, " * No file filters available\n" );
93  }
94 #endif // DEBUG
95 
96  return;
97 }
98 
99 
101 {
102  std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
103  std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
104 
105  while( sP != eP )
106  {
107  (*sP)->Close();
108  delete *sP;
109  ++sP;
110  }
111 
112  m_Plugins.clear();
113  return;
114 }
115 
116 
118 {
119  std::list< wxString > searchpaths;
120  std::list< wxString > pluginlist;
121  wxFileName fn;
122 
123 #ifndef __WXMAC__
124 
125  #ifdef DEBUG
126  // set up to work from the build directory
127  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
128  fn.AppendDir( wxT("..") );
129  fn.AppendDir( wxT("plugins") );
130  fn.AppendDir( wxT("3d") );
131 
132  std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() );
133  checkPluginPath( testpath, searchpaths );
134 
135  // add subdirectories too
136  wxDir debugPluginDir;
137  wxString subdir;
138 
139  debugPluginDir.Open( testpath );
140 
141  if( debugPluginDir.IsOpened() &&
142  debugPluginDir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
143  {
144  checkPluginPath( testpath + subdir, searchpaths );
145 
146  while( debugPluginDir.GetNext( &subdir ) )
147  checkPluginPath( testpath + subdir, searchpaths );
148  }
149  #endif
150 
151  #ifndef _WIN32
152  // PLUGINDIR = CMAKE_INSTALL_FULL_LIBDIR path is the absolute path
153  // corresponding to the install path used for constructing KICAD_USER_PLUGIN
154 
155  wxString tfname = wxString::FromUTF8Unchecked( PLUGINDIR );
156  fn.Assign( tfname, "");
157  fn.AppendDir( "kicad" );
158  #else
159  // on windows the plugins directory is within the executable's directory
160  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
161  #endif
162 
163  fn.AppendDir( wxT( "plugins" ) );
164  fn.AppendDir( wxT( "3d" ) );
165 
166  // checks plugin directory relative to executable path
167  checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
168 
169  // check for per-user third party plugins
170  // note: GetUserDataDir() gives '.pcbnew' rather than '.kicad' since it uses the exe name;
171  fn.Assign( wxStandardPaths::Get().GetUserDataDir(), "" );
172  fn.RemoveLastDir();
173  #ifdef _WIN32
174  fn.AppendDir( wxT( "kicad" ) );
175  #else
176  fn.AppendDir( wxT( ".kicad" ) );
177  #endif
178  fn.AppendDir( wxT( "plugins" ) );
179  fn.AppendDir( wxT( "3d" ) );
180  checkPluginPath( fn.GetPathWithSep(), searchpaths );
181 
182 #else
183 
184  // Search path on OS X is
185  // (1) User ~/Library/Application Support/kicad/PlugIns/3d
186  checkPluginPath( GetOSXKicadUserDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
187  // (2) Machine /Library/Application Support/kicad/PlugIns/3d
188  checkPluginPath( GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
189  // (3) Bundle kicad.app/Contents/PlugIns/3d
190  fn.Assign( Pgm().GetExecutablePath() );
191  fn.AppendDir( wxT( "Contents" ) );
192  fn.AppendDir( wxT( "PlugIns" ) );
193  fn.AppendDir( wxT( "3d" ) );
194  checkPluginPath( fn.GetPathWithSep(), searchpaths );
195 
196 #endif
197 
198  std::list< wxString >::iterator sPL = searchpaths.begin();
199  std::list< wxString >::iterator ePL = searchpaths.end();
200 
201  while( sPL != ePL )
202  {
203 #ifdef DEBUG
204  do {
205  std::ostringstream ostr;
206  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
207  ostr << " * [DEBUG] searching path: '" << (*sPL).ToUTF8() << "'";
208  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
209  } while( 0 );
210 #endif
211  listPlugins( *sPL, pluginlist );
212  ++sPL;
213  }
214 
215  if( pluginlist.empty() )
216  return;
217 
218  sPL = pluginlist.begin();
219  ePL = pluginlist.end();
220 
221  while( sPL != ePL )
222  {
223  KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
224 
225  if( pp->Open( sPL->ToUTF8() ) )
226  {
227 #ifdef DEBUG
228  do {
229  std::ostringstream ostr;
230  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
231  ostr << "* [DEBUG] adding plugin";
232  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
233  } while( 0 );
234 #endif
235  m_Plugins.push_back( pp );
236  int nf = pp->GetNFilters();
237 
238  #ifdef DEBUG
239  do {
240  std::ostringstream ostr;
241  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
242  ostr << " * [INFO] adding " << nf << " filters";
243  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
244  } while( 0 );
245  #endif
246 
247  for( int i = 0; i < nf; ++i )
248  {
249  char const* cp = pp->GetFileFilter( i );
250 
251  if( cp )
252  addFilterString( wxString::FromUTF8Unchecked( cp ) );
253  }
254 
255  addExtensionMap( pp );
256 
257  // close the loaded library
258  pp->Close();
259  }
260  else
261  {
262 #ifdef DEBUG
263  do {
264  std::ostringstream ostr;
265  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
266  ostr << "* [DEBUG] deleting plugin";
267  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
268  } while( 0 );
269 #endif
270  delete pp;
271  }
272 
273  ++sPL;
274  }
275 
276 #ifdef DEBUG
277  do {
278  std::ostringstream ostr;
279  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
280  ostr << "* [DEBUG] plugins loaded";
281  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
282  } while( 0 );
283 #endif
284 
285  return;
286 }
287 
288 
289 void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath,
290  std::list< wxString >& aPluginList )
291 {
292  // list potential plugins given a search path
293 
294  wxString nameFilter; // filter for user-loadable libraries (aka modules)
295  wxString lName; // stores name of enumerated files
296  wxString fName; // full name of file
297  wxDir wd;
298  wd.Open( aPath );
299 
300  if( !wd.IsOpened() )
301  return;
302 
303  nameFilter = wxT( "*" );
304 
305 #ifndef __WXMAC__
306  nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
307 #else
308  // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
309  // This might be correct, but cmake builds a ".so" for a library MODULE.
310  // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
311  // bundle being a folder with some special content in it. We obviously don't
312  // want to have that here for our loadable module, so just use ".so".
313  nameFilter.Append( ".so" );
314 #endif
315 
316  wxString lp = wd.GetNameWithSep();
317 
318  if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
319  {
320  fName = lp + lName;
321  checkPluginName( fName, aPluginList );
322 
323  while( wd.GetNext( &lName ) )
324  {
325  fName = lp + lName;
326  checkPluginName( fName, aPluginList );
327  }
328  }
329 
330  wd.Close();
331 
332  return;
333 }
334 
335 
336 void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
337  std::list< wxString >& aPluginList )
338 {
339  // check the existence of a plugin name and add it to the list
340 
341  if( aPath.empty() || !wxFileName::FileExists( aPath ) )
342  return;
343 
344  wxFileName path;
345 
346  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
347  path.Assign( ExpandEnvVarSubstitutions( aPath ) );
348  else
349  path.Assign( aPath );
350 
351  path.Normalize();
352 
353  // determine if the path is already in the list
354  wxString wxpath = path.GetFullPath();
355  std::list< wxString >::iterator bl = aPluginList.begin();
356  std::list< wxString >::iterator el = aPluginList.end();
357 
358  while( bl != el )
359  {
360  if( 0 == (*bl).Cmp( wxpath ) )
361  return;
362 
363  ++bl;
364  }
365 
366  aPluginList.push_back( wxpath );
367 
368  #ifdef DEBUG
369  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] found 3D plugin '%s'\n",
370  wxpath.GetData() );
371  #endif
372 
373  return;
374 }
375 
376 
377 void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
378  std::list< wxString >& aSearchList )
379 {
380  // check the existence of a path and add it to the path search list
381  if( aPath.empty() )
382  return;
383 
384  #ifdef DEBUG
385  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] checking for 3D plugins in '%s'\n",
386  aPath.GetData() );
387  #endif
388 
389  wxFileName path;
390 
391  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
392  path.Assign( ExpandEnvVarSubstitutions( aPath ), "" );
393  else
394  path.Assign( aPath, "" );
395 
396  path.Normalize();
397 
398  if( !wxFileName::DirExists( path.GetFullPath() ) )
399  return;
400 
401  // determine if the directory is already in the list
402  wxString wxpath = path.GetFullPath();
403  std::list< wxString >::iterator bl = aSearchList.begin();
404  std::list< wxString >::iterator el = aSearchList.end();
405 
406  while( bl != el )
407  {
408  if( 0 == (*bl).Cmp( wxpath ) )
409  return;
410 
411  ++bl;
412  }
413 
414  aSearchList.push_back( wxpath );
415 
416  return;
417 }
418 
419 
420 void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
421 {
422  // add an entry to the file filter list
423  if( aFilterString.empty() )
424  return;
425 
426  std::list< wxString >::iterator sFF = m_FileFilters.begin();
427  std::list< wxString >::iterator eFF = m_FileFilters.end();
428 
429  while( sFF != eFF )
430  {
431  if( 0 == (*sFF).Cmp( aFilterString ) )
432  return;
433 
434  ++sFF;
435  }
436 
437  m_FileFilters.push_back( aFilterString );
438  return;
439 }
440 
441 
442 void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin )
443 {
444  // add entries to the extension map
445  if( NULL == aPlugin )
446  return;
447 
448  int nExt = aPlugin->GetNExtensions();
449 
450  #ifdef DEBUG
451  do {
452  std::ostringstream ostr;
453  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
454  ostr << " * [INFO] adding " << nExt << " extensions";
455  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
456  } while( 0 );
457  #endif
458 
459  for( int i = 0; i < nExt; ++i )
460  {
461  char const* cp = aPlugin->GetModelExtension( i );
462  wxString ws;
463 
464  if( cp )
465  ws = wxString::FromUTF8Unchecked( cp );
466 
467  if( !ws.empty() )
468  {
469  m_ExtMap.insert( std::pair< const wxString, KICAD_PLUGIN_LDR_3D* >( ws, aPlugin ) );
470  }
471 
472  }
473 
474  return;
475 }
476 
477 
478 std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const
479 {
480  return &m_FileFilters;
481 }
482 
483 
484 SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
485 {
486  wxFileName raw( aFileName );
487  wxString ext = raw.GetExt();
488 
489  #ifdef _WIN32
490  // note: plugins only have a lowercase filter within Windows; including an uppercase
491  // filter will result in duplicate file entries and should be avoided.
492  ext.LowerCase();
493  #endif
494 
495  std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
496  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
497 
498  items = m_ExtMap.equal_range( ext );
499  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
500 
501  while( sL != items.second )
502  {
503  if( sL->second->CanRender() )
504  {
505  SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
506 
507  if( NULL != sp )
508  {
509  sL->second->GetPluginInfo( aPluginInfo );
510  return sp;
511  }
512  }
513 
514  ++sL;
515  }
516 
517  return NULL;
518 }
519 
520 
522 {
523  std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
524  std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
525 
526  #ifdef DEBUG
527  do {
528  std::ostringstream ostr;
529  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
530  ostr << " * [INFO] closing " << m_Plugins.size() << " plugins";
531  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
532  } while( 0 );
533  #endif
534 
535  while( sP != eP )
536  {
537  (*sP)->Close();
538  ++sP;
539  }
540 
541  return;
542 }
543 
544 
545 bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
546 {
547  if( NULL == aTag || aTag[0] == 0 || m_Plugins.empty() )
548  return false;
549 
550  std::string tname = aTag;
551  std::string pname; // plugin name
552 
553  size_t cpos = tname.find( ':' );
554 
555  // if there is no colon or plugin name then the tag is bad
556  if( cpos == std::string::npos || cpos == 0 )
557  return false;
558 
559  pname = tname.substr( 0, cpos );
560  std::string ptag; // tag from the plugin
561 
562  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
563  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
564 
565  while( pS != pE )
566  {
567  ptag.clear();
568  (*pS)->GetPluginInfo( ptag );
569 
570  // if the plugin name matches then the version
571  // must also match
572  if( !ptag.compare( 0, pname.size(), pname ) )
573  {
574  if( ptag.compare( tname ) )
575  return false;
576 
577  return true;
578  }
579 
580  ++pS;
581  }
582 
583  return true;
584 }
std::list< wxString > m_FileFilters
list of file filters
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D * > m_ExtMap
mapping of extensions to available plugins
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:66
#define MASK_3D_PLUGINMGR
void addFilterString(const wxString &aFilterString)
add an entry to the file filter list
describes the runtime-loadable interface to support loading and parsing of 3D models.
SCENEGRAPH * Load3DModel(const wxString &aFileName, std::string &aPluginInfo)
void listPlugins(const wxString &aPath, std::list< wxString > &aPluginList)
list potential plugins
defines the basic data set required to represent a 3D model; this model must remain compatible with V...
std::list< wxString > const * GetFileFilters(void) const
Function GetFileFilters returns the list of file filters; this will contain at least the default "All...
std::list< KICAD_PLUGIN_LDR_3D * > m_Plugins
list of discovered plugins
const wxString ExpandEnvVarSubstitutions(const wxString &aString)
Replace any environment variable references with their values.
Definition: common.cpp:458
void checkPluginPath(const wxString &aPath, std::list< wxString > &aSearchList)
check the existence of a path and add it to the path search list
bool CheckTag(const char *aTag)
Function CheckTag checks the given tag and returns true if the plugin named in the tag is not loaded ...
void addExtensionMap(KICAD_PLUGIN_LDR_3D *aPlugin)
add entries to the extension map
see class PGM_BASE
manages 3D model plugins
size_t i
Definition: json11.cpp:597
void loadPlugins(void)
load plugins
The common library.
void ClosePlugins(void)
Function ClosePlugins iterates through all discovered plugins and closes them to reclaim memory.
void checkPluginName(const wxString &aPath, std::list< wxString > &aPluginList)
check the existence of a plugin name and add it to the list