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  #endif
135 
136  #ifndef _WIN32
137  // multiarch friendly determination of the plugin directory: the executable dir
138  // is first determined via wxStandardPaths::Get().GetExecutablePath() and then
139  // the CMAKE_INSTALL_LIBDIR path is appended relative to the executable dir.
140 
141  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
142  fn.RemoveLastDir();
143  wxString tfname = fn.GetPathWithSep();
144  tfname.Append( wxString::FromUTF8Unchecked( PLUGINDIR ) );
145  fn.Assign( tfname, "");
146  fn.AppendDir( "kicad" );
147  #else
148  // on windows the plugins directory is within the executable's directory
149  fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
150  #endif
151 
152  fn.AppendDir( wxT( "plugins" ) );
153  fn.AppendDir( wxT( "3d" ) );
154 
155  // checks plugin directory relative to executable path
156  checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
157 
158  // check for per-user third party plugins
159  // note: GetUserDataDir() gives '.pcbnew' rather than '.kicad' since it uses the exe name;
160  fn.Assign( wxStandardPaths::Get().GetUserDataDir(), "" );
161  fn.RemoveLastDir();
162  #ifdef _WIN32
163  fn.AppendDir( wxT( "kicad" ) );
164  #else
165  fn.AppendDir( wxT( ".kicad" ) );
166  #endif
167  fn.AppendDir( wxT( "plugins" ) );
168  fn.AppendDir( wxT( "3d" ) );
169  checkPluginPath( fn.GetPathWithSep(), searchpaths );
170 
171 #else
172 
173  // Search path on OS X is
174  // (1) User ~/Library/Application Support/kicad/PlugIns/3d
175  checkPluginPath( GetOSXKicadUserDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
176  // (2) Machine /Library/Application Support/kicad/PlugIns/3d
177  checkPluginPath( GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
178  // (3) Bundle kicad.app/Contents/PlugIns/3d
179  fn.Assign( Pgm().GetExecutablePath() );
180  fn.AppendDir( wxT( "Contents" ) );
181  fn.AppendDir( wxT( "PlugIns" ) );
182  fn.AppendDir( wxT( "3d" ) );
183  checkPluginPath( fn.GetPathWithSep(), searchpaths );
184 
185 #endif
186 
187  std::list< wxString >::iterator sPL = searchpaths.begin();
188  std::list< wxString >::iterator ePL = searchpaths.end();
189 
190  while( sPL != ePL )
191  {
192 #ifdef DEBUG
193  do {
194  std::ostringstream ostr;
195  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
196  ostr << " * [DEBUG] searching path: '" << (*sPL).ToUTF8() << "'";
197  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
198  } while( 0 );
199 #endif
200  listPlugins( *sPL, pluginlist );
201  ++sPL;
202  }
203 
204  if( pluginlist.empty() )
205  return;
206 
207  sPL = pluginlist.begin();
208  ePL = pluginlist.end();
209 
210  while( sPL != ePL )
211  {
212  KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
213 
214  if( pp->Open( sPL->ToUTF8() ) )
215  {
216 #ifdef DEBUG
217  do {
218  std::ostringstream ostr;
219  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
220  ostr << "* [DEBUG] adding plugin";
221  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
222  } while( 0 );
223 #endif
224  m_Plugins.push_back( pp );
225  int nf = pp->GetNFilters();
226 
227  #ifdef DEBUG
228  do {
229  std::ostringstream ostr;
230  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
231  ostr << " * [INFO] adding " << nf << " filters";
232  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
233  } while( 0 );
234  #endif
235 
236  for( int i = 0; i < nf; ++i )
237  {
238  char const* cp = pp->GetFileFilter( i );
239 
240  if( cp )
241  addFilterString( wxString::FromUTF8Unchecked( cp ) );
242  }
243 
244  addExtensionMap( pp );
245 
246  // close the loaded library
247  pp->Close();
248  }
249  else
250  {
251 #ifdef DEBUG
252  do {
253  std::ostringstream ostr;
254  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
255  ostr << "* [DEBUG] deleting plugin";
256  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
257  } while( 0 );
258 #endif
259  delete pp;
260  }
261 
262  ++sPL;
263  }
264 
265 #ifdef DEBUG
266  do {
267  std::ostringstream ostr;
268  ostr << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << ":\n";
269  ostr << "* [DEBUG] plugins loaded";
270  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
271  } while( 0 );
272 #endif
273 
274  return;
275 }
276 
277 
278 void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath,
279  std::list< wxString >& aPluginList )
280 {
281  // list potential plugins given a search path
282 
283  wxString nameFilter; // filter for user-loadable libraries (aka modules)
284  wxString lName; // stores name of enumerated files
285  wxString fName; // full name of file
286  wxDir wd;
287  wd.Open( aPath );
288 
289  if( !wd.IsOpened() )
290  return;
291 
292  nameFilter = wxT( "*" );
293 
294 #ifndef __WXMAC__
295  nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
296 #else
297  // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
298  // This might be correct, but cmake builds a ".so" for a library MODULE.
299  // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
300  // bundle being a folder with some special content in it. We obviously don't
301  // want to have that here for our loadable module, so just use ".so".
302  nameFilter.Append( ".so" );
303 #endif
304 
305  wxString lp = wd.GetNameWithSep();
306 
307  if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
308  {
309  fName = lp + lName;
310  checkPluginName( fName, aPluginList );
311 
312  while( wd.GetNext( &lName ) )
313  {
314  fName = lp + lName;
315  checkPluginName( fName, aPluginList );
316  }
317  }
318 
319  wd.Close();
320 
321  return;
322 }
323 
324 
325 void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
326  std::list< wxString >& aPluginList )
327 {
328  // check the existence of a plugin name and add it to the list
329 
330  if( aPath.empty() || !wxFileName::FileExists( aPath ) )
331  return;
332 
333  wxFileName path;
334 
335  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
336  path.Assign( ExpandEnvVarSubstitutions( aPath ) );
337  else
338  path.Assign( aPath );
339 
340  path.Normalize();
341 
342  // determine if the path is already in the list
343  wxString wxpath = path.GetFullPath();
344  std::list< wxString >::iterator bl = aPluginList.begin();
345  std::list< wxString >::iterator el = aPluginList.end();
346 
347  while( bl != el )
348  {
349  if( 0 == (*bl).Cmp( wxpath ) )
350  return;
351 
352  ++bl;
353  }
354 
355  aPluginList.push_back( wxpath );
356 
357  #ifdef DEBUG
358  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] found 3D plugin '%s'\n",
359  wxpath.GetData() );
360  #endif
361 
362  return;
363 }
364 
365 
366 void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
367  std::list< wxString >& aSearchList )
368 {
369  // check the existence of a path and add it to the path search list
370  if( aPath.empty() )
371  return;
372 
373  #ifdef DEBUG
374  wxLogTrace( MASK_3D_PLUGINMGR, " * [INFO] checking for 3D plugins in '%s'\n",
375  aPath.GetData() );
376  #endif
377 
378  wxFileName path;
379 
380  if( aPath.StartsWith( "${" ) || aPath.StartsWith( "$(" ) )
381  path.Assign( ExpandEnvVarSubstitutions( aPath ), "" );
382  else
383  path.Assign( aPath, "" );
384 
385  path.Normalize();
386 
387  if( !wxFileName::DirExists( path.GetFullPath() ) )
388  return;
389 
390  // determine if the directory is already in the list
391  wxString wxpath = path.GetFullPath();
392  std::list< wxString >::iterator bl = aSearchList.begin();
393  std::list< wxString >::iterator el = aSearchList.end();
394 
395  while( bl != el )
396  {
397  if( 0 == (*bl).Cmp( wxpath ) )
398  return;
399 
400  ++bl;
401  }
402 
403  aSearchList.push_back( wxpath );
404 
405  return;
406 }
407 
408 
409 void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
410 {
411  // add an entry to the file filter list
412  if( aFilterString.empty() )
413  return;
414 
415  std::list< wxString >::iterator sFF = m_FileFilters.begin();
416  std::list< wxString >::iterator eFF = m_FileFilters.end();
417 
418  while( sFF != eFF )
419  {
420  if( 0 == (*sFF).Cmp( aFilterString ) )
421  return;
422 
423  ++sFF;
424  }
425 
426  m_FileFilters.push_back( aFilterString );
427  return;
428 }
429 
430 
431 void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin )
432 {
433  // add entries to the extension map
434  if( NULL == aPlugin )
435  return;
436 
437  int nExt = aPlugin->GetNExtensions();
438 
439  #ifdef DEBUG
440  do {
441  std::ostringstream ostr;
442  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
443  ostr << " * [INFO] adding " << nExt << " extensions";
444  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
445  } while( 0 );
446  #endif
447 
448  for( int i = 0; i < nExt; ++i )
449  {
450  char const* cp = aPlugin->GetModelExtension( i );
451  wxString ws;
452 
453  if( cp )
454  ws = wxString::FromUTF8Unchecked( cp );
455 
456  if( !ws.empty() )
457  {
458  m_ExtMap.insert( std::pair< const wxString, KICAD_PLUGIN_LDR_3D* >( ws, aPlugin ) );
459  }
460 
461  }
462 
463  return;
464 }
465 
466 
467 std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const
468 {
469  return &m_FileFilters;
470 }
471 
472 
473 SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
474 {
475  wxFileName raw( aFileName );
476  wxString ext = raw.GetExt();
477 
478  #ifdef WIN32
479  // note: plugins only have a lowercase filter within Windows; including an uppercase
480  // filter will result in duplicate file entries and should be avoided.
481  ext.LowerCase();
482  #endif
483 
484  std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
485  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
486 
487  items = m_ExtMap.equal_range( ext );
488  std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
489 
490  while( sL != items.second )
491  {
492  if( sL->second->CanRender() )
493  {
494  SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
495 
496  if( NULL != sp )
497  {
498  sL->second->GetPluginInfo( aPluginInfo );
499  return sp;
500  }
501  }
502 
503  ++sL;
504  }
505 
506  return NULL;
507 }
508 
509 
511 {
512  std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
513  std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
514 
515  #ifdef DEBUG
516  do {
517  std::ostringstream ostr;
518  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
519  ostr << " * [INFO] closing " << m_Plugins.size() << " plugins";
520  wxLogTrace( MASK_3D_PLUGINMGR, "%s\n", ostr.str().c_str() );
521  } while( 0 );
522  #endif
523 
524  while( sP != eP )
525  {
526  (*sP)->Close();
527  ++sP;
528  }
529 
530  return;
531 }
532 
533 
534 bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
535 {
536  if( NULL == aTag || aTag[0] == 0 || m_Plugins.empty() )
537  return false;
538 
539  std::string tname = aTag;
540  std::string pname; // plugin name
541 
542  size_t cpos = tname.find( ':' );
543 
544  // if there is no colon or plugin name then the tag is bad
545  if( cpos == std::string::npos || cpos == 0 )
546  return false;
547 
548  pname = tname.substr( 0, cpos );
549  std::string ptag; // tag from the plugin
550 
551  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
552  std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
553 
554  while( pS != pE )
555  {
556  ptag.clear();
557  (*pS)->GetPluginInfo( ptag );
558 
559  // if the plugin name matches then the version
560  // must also match
561  if( !ptag.compare( 0, pname.size(), pname ) )
562  {
563  if( ptag.compare( tname ) )
564  return false;
565 
566  return true;
567  }
568 
569  ++pS;
570  }
571 
572  return true;
573 }
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:65
#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< KICAD_PLUGIN_LDR_3D * > m_Plugins
list of discovered plugins
const wxString ExpandEnvVarSubstitutions(const wxString &aString)
Function ExpandEnvVarSubstitutions replaces any environment variable references with their values...
Definition: common.cpp:254
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
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
std::list< wxString > const * GetFileFilters(void) const
Function GetFileFilters returns the list of file filters; this will contain at least the default "All...