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