KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
5 * Copyright The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include <utility>
23#include <iostream>
24#include <sstream>
25
26#include <wx/dir.h>
27#include <wx/dynlib.h>
28#include <wx/log.h>
29#include <wx/stdpaths.h>
30#include <wx/string.h>
31
32#include <common.h>
33#include <paths.h>
34#include <wx_filename.h>
35#include "3d_plugin_manager.h"
39
40
48#define MASK_3D_PLUGINMGR "3D_PLUGIN_MANAGER"
49
50
52{
53 // create the initial file filter list entry
54 m_FileFilters.emplace_back( _( "All Files" ) + wxT( " (*.*)|*.*" ) );
55
56 // discover and load plugins
58
59#ifdef DEBUG
60 if( !m_ExtMap.empty() )
61 {
62 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator sM = m_ExtMap.begin();
63 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator eM = m_ExtMap.end();
64 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * Extension [plugin name]:\n" ) );
65
66 while( sM != eM )
67 {
68 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s' [%s]\n" ), sM->first.GetData(),
69 sM->second->GetKicadPluginName() );
70 ++sM;
71 }
72
73 }
74 else
75 {
76 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No plugins available\n" ) );
77 }
78
79
80 if( !m_FileFilters.empty() )
81 {
83 std::list< wxString >::const_iterator sFF = m_FileFilters.begin();
84 std::list< wxString >::const_iterator eFF = m_FileFilters.end();
85 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * File filters:\n" ) );
86
87 while( sFF != eFF )
88 {
89 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s'\n" ), (*sFF).GetData() );
90 ++sFF;
91 }
92 }
93 else
94 {
95 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No file filters available\n" ) );
96 }
97#endif // DEBUG
98}
99
100
102{
103 std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
104 std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
105
106 while( sP != eP )
107 {
108 (*sP)->Close();
109 delete *sP;
110 ++sP;
111 }
112
113 m_Plugins.clear();
114}
115
116
118{
119 std::list<wxString> searchpaths;
120 std::list<wxString> pluginlist;
121 wxFileName fn;
122
123#ifndef __WXMAC__
124 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
125 {
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 }
150
151 fn.AssignDir( PATHS::GetStockPlugins3DPath() );
152 checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
153#else
154 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
155 {
156 // Exe will be at <build_dir>/pcbnew/pcbnew.app/Contents/MacOS/pcbnew for standalone
157 // Plugin will be at <build_dir>/kicad/KiCad.app/Contents/PlugIns/3d
158 fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
159
160 if( fn.GetName() == wxT( "kicad" ) )
161 {
162 fn.AppendDir( wxT( ".." ) ); // Contents
163 }
164 else
165 {
166 fn.AppendDir( wxT( ".." ) ); // Contents
167 fn.AppendDir( wxT( ".." ) ); // pcbnew.app
168 fn.AppendDir( wxT( ".." ) ); // pcbnew
169 fn.AppendDir( wxT( ".." ) ); // Build root
170 fn.AppendDir( wxT( "kicad" ) );
171 fn.AppendDir( wxT( "KiCad.app" ) );
172 fn.AppendDir( wxT( "Contents" ) );
173 }
174
175 fn.AppendDir( wxT( "PlugIns" ) );
176 fn.AppendDir( wxT( "3d" ) );
177 fn.MakeAbsolute();
178
179 std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() );
180 checkPluginPath( testpath, searchpaths );
181
182 // Also check when running KiCad manager from build dir
183
184 }
185 else
186 {
187 // Search path on OS X is
188 // (1) Machine /Library/Application Support/kicad/PlugIns/3d
189 checkPluginPath( PATHS::GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
190
191 // (2) Bundle kicad.app/Contents/PlugIns/3d
192 fn.AssignDir( PATHS::GetStockPlugins3DPath() );
193 checkPluginPath( fn.GetPathWithSep(), searchpaths );
194 }
195#endif
196
197 std::list< wxString >::iterator sPL = searchpaths.begin();
198 std::list< wxString >::iterator ePL = searchpaths.end();
199
200 while( sPL != ePL )
201 {
202 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] searching path: '%s'" ),
203 __FILE__, __FUNCTION__, __LINE__, (*sPL).ToUTF8() );
204
205 listPlugins( *sPL, pluginlist );
206 ++sPL;
207 }
208
209 if( pluginlist.empty() )
210 return;
211
212 sPL = pluginlist.begin();
213 ePL = pluginlist.end();
214
215 while( sPL != ePL )
216 {
218
219 if( pp->Open( sPL->ToUTF8() ) )
220 {
221 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding plugin" ),
222 __FILE__, __FUNCTION__, __LINE__ );
223
224 m_Plugins.push_back( pp );
225 int nf = pp->GetNFilters();
226
227 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding %d filters" ),
228 __FILE__, __FUNCTION__, __LINE__, nf );
229
230 for( int i = 0; i < nf; ++i )
231 {
232 char const* cp = pp->GetFileFilter( i );
233
234 if( cp )
235 addFilterString( cp );
236 }
237
238 addExtensionMap( pp );
239
240 // close the loaded library
241 pp->Close();
242 }
243 else
244 {
245 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] deleting plugin" ),
246 __FILE__, __FUNCTION__, __LINE__ );
247
248 delete pp;
249 }
250
251 ++sPL;
252 }
253
254 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] plugins loaded" ),
255 __FILE__, __FUNCTION__, __LINE__ );
256}
257
258
259void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath, std::list< wxString >& aPluginList )
260{
261 // list potential plugins given a search path
262 wxString nameFilter; // filter for user-loadable libraries (aka footprints)
263 wxString lName; // stores name of enumerated files
264 wxString fName; // full name of file
265 wxDir wd;
266 wd.Open( aPath );
267
268 if( !wd.IsOpened() )
269 return;
270
271 nameFilter = wxT( "*" );
272
273#ifndef __WXMAC__
274 nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
275#else
276 // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
277 // This might be correct, but cmake builds a ".so" for a library MODULE.
278 // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
279 // bundle being a folder with some special content in it. We obviously don't
280 // want to have that here for our loadable module, so just use ".so".
281 nameFilter.Append( wxS( ".so" ) );
282#endif
283
284 wxString lp = wd.GetNameWithSep();
285
286 if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
287 {
288 fName = lp + lName;
289 checkPluginName( fName, aPluginList );
290
291 while( wd.GetNext( &lName ) )
292 {
293 fName = lp + lName;
294 checkPluginName( fName, aPluginList );
295 }
296 }
297
298 wd.Close();
299}
300
301
302void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
303 std::list< wxString >& aPluginList )
304{
305 // check the existence of a plugin name and add it to the list
306
307 if( aPath.empty() || !wxFileName::FileExists( aPath ) )
308 return;
309
310 wxFileName path( ExpandEnvVarSubstitutions( aPath, nullptr ) );
311
312 path.Normalize( FN_NORMALIZE_FLAGS );
313
314 // determine if the path is already in the list
315 wxString wxpath = path.GetFullPath();
316 std::list< wxString >::iterator bl = aPluginList.begin();
317 std::list< wxString >::iterator el = aPluginList.end();
318
319 while( bl != el )
320 {
321 if( 0 == (*bl).Cmp( wxpath ) )
322 return;
323
324 ++bl;
325 }
326
327 // prevent loading non-plugin dlls
328 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
329 {
330 if( !path.GetName().StartsWith( "s3d_plugin" )
331 && !path.GetName().StartsWith( "libs3d_plugin" ) )
332 {
333 return;
334 }
335 }
336
337 aPluginList.push_back( wxpath );
338
339 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] found 3D plugin '%s'\n" ), wxpath.GetData() );
340}
341
342
343void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
344 std::list< wxString >& aSearchList )
345{
346 if( aPath.empty() )
347 return;
348
349 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] checking if valid plugin directory '%s'\n" ),
350 aPath.GetData() );
351
352 wxFileName path;
353 path.AssignDir( aPath );
354 path.Normalize( FN_NORMALIZE_FLAGS );
355
356 if( !wxFileName::DirExists( path.GetFullPath() ) )
357 return;
358
359 // determine if the directory is already in the list
360 wxString wxpath = path.GetFullPath();
361 std::list< wxString >::iterator bl = aSearchList.begin();
362 std::list< wxString >::iterator el = aSearchList.end();
363
364 while( bl != el )
365 {
366 if( 0 == (*bl).Cmp( wxpath ) )
367 return;
368
369 ++bl;
370 }
371
372 aSearchList.push_back( wxpath );
373}
374
375
376void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
377{
378 // add an entry to the file filter list
379 if( aFilterString.empty() )
380 return;
381
382 std::list< wxString >::iterator sFF = m_FileFilters.begin();
383 std::list< wxString >::iterator eFF = m_FileFilters.end();
384
385 while( sFF != eFF )
386 {
387 if( 0 == (*sFF).Cmp( aFilterString ) )
388 return;
389
390 ++sFF;
391 }
392
393 m_FileFilters.push_back( aFilterString );
394 return;
395}
396
397
399{
400 // add entries to the extension map
401 if( nullptr == aPlugin )
402 return;
403
404 int nExt = aPlugin->GetNExtensions();
405
406 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] adding %d extensions" ),
407 __FILE__, __FUNCTION__, __LINE__, nExt );
408
409 for( int i = 0; i < nExt; ++i )
410 {
411 char const* cp = aPlugin->GetModelExtension( i );
412 wxString ws;
413
414 if( cp )
415 ws = wxString( cp );
416
417 if( !ws.empty() )
418 {
419 m_ExtMap.emplace( ws, aPlugin );
420 }
421
422 }
423}
424
425
426std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const noexcept
427{
428 return &m_FileFilters;
429}
430
431
432SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
433{
434 wxFileName raw( aFileName );
435 wxString ext_to_find = raw.GetExt();
436
437#ifdef _WIN32
438 // note: plugins only have a lowercase filter within Windows; including an uppercase
439 // filter will result in duplicate file entries and should be avoided.
440 ext_to_find.MakeLower();
441#endif
442
443 // .gz files are compressed versions that may have additional information in the previous
444 // extension.
445 if( ext_to_find == wxT( "gz" ) )
446 {
447 wxFileName second( raw.GetName() );
448 ext_to_find = second.GetExt() + wxT( ".gz" );
449 }
450
451 std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
452 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
453
454 items = m_ExtMap.equal_range( ext_to_find );
455 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
456
457 while( sL != items.second )
458 {
459 if( sL->second->CanRender() )
460 {
461 SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
462
463 if( nullptr != sp )
464 {
465 sL->second->GetPluginInfo( aPluginInfo );
466 return sp;
467 }
468 }
469
470 ++sL;
471 }
472
473 return nullptr;
474}
475
476
478{
479 std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
480 std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
481
482 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] closing %d extensions" ),
483 __FILE__, __FUNCTION__, __LINE__, static_cast<int>( m_Plugins.size() ) );
484
485 while( sP != eP )
486 {
487 (*sP)->Close();
488 ++sP;
489 }
490}
491
492
493bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
494{
495 if( nullptr == aTag || aTag[0] == 0 || m_Plugins.empty() )
496 return false;
497
498 std::string tname = aTag;
499 std::string pname; // plugin name
500
501 size_t cpos = tname.find( ':' );
502
503 // if there is no colon or plugin name then the tag is bad
504 if( cpos == std::string::npos || cpos == 0 )
505 return false;
506
507 pname = tname.substr( 0, cpos );
508 std::string ptag; // tag from the plugin
509
510 std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
511 std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
512
513 while( pS != pE )
514 {
515 ptag.clear();
516 (*pS)->GetPluginInfo( ptag );
517
518 // if the plugin name matches then the version must also match
519 if( !ptag.compare( 0, pname.size(), pname ) )
520 {
521 if( ptag.compare( tname ) )
522 return false;
523
524 return true;
525 }
526
527 ++pS;
528 }
529
530 return true;
531}
Describe the runtime-loadable interface to support loading and parsing of 3D models.
manages 3D model plugins
bool Open(const wxString &aFullFileName) override
Open a plugin of the given class, performs version compatibility checks, and links all required funct...
char const * GetFileFilter(int aIndex)
char const * GetModelExtension(int aIndex)
void Close(void) override
Clean up and closes/unloads the plugin.
static wxString GetStockPlugins3DPath()
Gets the stock (install) 3d viewer plugins path.
Definition paths.cpp:393
void addFilterString(const wxString &aFilterString)
add an entry to the file filter list
std::list< wxString > m_FileFilters
list of file filters
std::list< KICAD_PLUGIN_LDR_3D * > m_Plugins
list of discovered plugins
void ClosePlugins(void)
Iterate through all discovered plugins and closes them to reclaim memory.
void loadPlugins(void)
load plugins
SCENEGRAPH * Load3DModel(const wxString &aFileName, std::string &aPluginInfo)
void checkPluginPath(const wxString &aPath, std::list< wxString > &aSearchList)
check the existence of a path and add it to the path search list
void listPlugins(const wxString &aPath, std::list< wxString > &aPluginList)
list potential plugins
std::list< wxString > const * GetFileFilters(void) const noexcept
Return the list of file filters; this will contain at least the default "All Files (*....
void checkPluginName(const wxString &aPath, std::list< wxString > &aPluginList)
check the existence of a plugin name and add it to the list
std::multimap< const wxString, KICAD_PLUGIN_LDR_3D * > m_ExtMap
mapping of extensions to available plugins
bool CheckTag(const char *aTag)
Check the given tag and returns true if the plugin named in the tag is not loaded or the plugin is lo...
void addExtensionMap(KICAD_PLUGIN_LDR_3D *aPlugin)
add entries to the extension map
Define the basic data set required to represent a 3D model.
Definition scenegraph.h:41
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:704
The common library.
#define _(s)
#define MASK_3D_PLUGINMGR
Flag to enable 3D plugin manager debug tracing.
std::string path
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:35