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 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
159 {
160 // Exe will be at <build_dir>/pcbnew/pcbnew.app/Contents/MacOS/pcbnew for standalone
161 // Plugin will be at <build_dir>/kicad/KiCad.app/Contents/PlugIns/3d
162 fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
163
164 if( fn.GetName() == wxT( "kicad" ) )
165 {
166 fn.AppendDir( wxT( ".." ) ); // Contents
167 }
168 else
169 {
170 fn.AppendDir( wxT( ".." ) ); // Contents
171 fn.AppendDir( wxT( ".." ) ); // pcbnew.app
172 fn.AppendDir( wxT( ".." ) ); // pcbnew
173 fn.AppendDir( wxT( ".." ) ); // Build root
174 fn.AppendDir( wxT( "kicad" ) );
175 fn.AppendDir( wxT( "KiCad.app" ) );
176 fn.AppendDir( wxT( "Contents" ) );
177 }
178
179 fn.AppendDir( wxT( "PlugIns" ) );
180 fn.AppendDir( wxT( "3d" ) );
181 fn.MakeAbsolute();
182
183 std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() );
184 checkPluginPath( testpath, searchpaths );
185
186 // Also check when running KiCad manager from build dir
187
188 }
189 else
190 {
191 // Search path on OS X is
192 // (1) Machine /Library/Application Support/kicad/PlugIns/3d
193 checkPluginPath( PATHS::GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
194
195 // (2) Bundle kicad.app/Contents/PlugIns/3d
196 fn.AssignDir( PATHS::GetStockPlugins3DPath() );
197 checkPluginPath( fn.GetPathWithSep(), searchpaths );
198 }
199#endif
200
201 std::list< wxString >::iterator sPL = searchpaths.begin();
202 std::list< wxString >::iterator ePL = searchpaths.end();
203
204 while( sPL != ePL )
205 {
206 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] searching path: '%s'" ),
207 __FILE__, __FUNCTION__, __LINE__, (*sPL).ToUTF8() );
208
209 listPlugins( *sPL, pluginlist );
210 ++sPL;
211 }
212
213 if( pluginlist.empty() )
214 return;
215
216 sPL = pluginlist.begin();
217 ePL = pluginlist.end();
218
219 while( sPL != ePL )
220 {
222
223 if( pp->Open( sPL->ToUTF8() ) )
224 {
225 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding plugin" ),
226 __FILE__, __FUNCTION__, __LINE__ );
227
228 m_Plugins.push_back( pp );
229 int nf = pp->GetNFilters();
230
231 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding %d filters" ),
232 __FILE__, __FUNCTION__, __LINE__, nf );
233
234 for( int i = 0; i < nf; ++i )
235 {
236 char const* cp = pp->GetFileFilter( i );
237
238 if( cp )
239 addFilterString( cp );
240 }
241
242 addExtensionMap( pp );
243
244 // close the loaded library
245 pp->Close();
246 }
247 else
248 {
249 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] deleting plugin" ),
250 __FILE__, __FUNCTION__, __LINE__ );
251
252 delete pp;
253 }
254
255 ++sPL;
256 }
257
258 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] plugins loaded" ),
259 __FILE__, __FUNCTION__, __LINE__ );
260}
261
262
263void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath, std::list< wxString >& aPluginList )
264{
265 // list potential plugins given a search path
266 wxString nameFilter; // filter for user-loadable libraries (aka footprints)
267 wxString lName; // stores name of enumerated files
268 wxString fName; // full name of file
269 wxDir wd;
270 wd.Open( aPath );
271
272 if( !wd.IsOpened() )
273 return;
274
275 nameFilter = wxT( "*" );
276
277#ifndef __WXMAC__
278 nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
279#else
280 // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
281 // This might be correct, but cmake builds a ".so" for a library MODULE.
282 // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
283 // bundle being a folder with some special content in it. We obviously don't
284 // want to have that here for our loadable module, so just use ".so".
285 nameFilter.Append( wxS( ".so" ) );
286#endif
287
288 wxString lp = wd.GetNameWithSep();
289
290 if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
291 {
292 fName = lp + lName;
293 checkPluginName( fName, aPluginList );
294
295 while( wd.GetNext( &lName ) )
296 {
297 fName = lp + lName;
298 checkPluginName( fName, aPluginList );
299 }
300 }
301
302 wd.Close();
303}
304
305
306void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
307 std::list< wxString >& aPluginList )
308{
309 // check the existence of a plugin name and add it to the list
310
311 if( aPath.empty() || !wxFileName::FileExists( aPath ) )
312 return;
313
314 wxFileName path( ExpandEnvVarSubstitutions( aPath, nullptr ) );
315
316 path.Normalize( FN_NORMALIZE_FLAGS );
317
318 // determine if the path is already in the list
319 wxString wxpath = path.GetFullPath();
320 std::list< wxString >::iterator bl = aPluginList.begin();
321 std::list< wxString >::iterator el = aPluginList.end();
322
323 while( bl != el )
324 {
325 if( 0 == (*bl).Cmp( wxpath ) )
326 return;
327
328 ++bl;
329 }
330
331 // prevent loading non-plugin dlls
332 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
333 {
334 if( !path.GetName().StartsWith( "s3d_plugin" )
335 && !path.GetName().StartsWith( "libs3d_plugin" ) )
336 {
337 return;
338 }
339 }
340
341 aPluginList.push_back( wxpath );
342
343 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] found 3D plugin '%s'\n" ), wxpath.GetData() );
344}
345
346
347void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
348 std::list< wxString >& aSearchList )
349{
350 if( aPath.empty() )
351 return;
352
353 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] checking if valid plugin directory '%s'\n" ),
354 aPath.GetData() );
355
356 wxFileName path;
357 path.AssignDir( aPath );
358 path.Normalize( FN_NORMALIZE_FLAGS );
359
360 if( !wxFileName::DirExists( path.GetFullPath() ) )
361 return;
362
363 // determine if the directory is already in the list
364 wxString wxpath = path.GetFullPath();
365 std::list< wxString >::iterator bl = aSearchList.begin();
366 std::list< wxString >::iterator el = aSearchList.end();
367
368 while( bl != el )
369 {
370 if( 0 == (*bl).Cmp( wxpath ) )
371 return;
372
373 ++bl;
374 }
375
376 aSearchList.push_back( wxpath );
377}
378
379
380void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
381{
382 // add an entry to the file filter list
383 if( aFilterString.empty() )
384 return;
385
386 std::list< wxString >::iterator sFF = m_FileFilters.begin();
387 std::list< wxString >::iterator eFF = m_FileFilters.end();
388
389 while( sFF != eFF )
390 {
391 if( 0 == (*sFF).Cmp( aFilterString ) )
392 return;
393
394 ++sFF;
395 }
396
397 m_FileFilters.push_back( aFilterString );
398 return;
399}
400
401
403{
404 // add entries to the extension map
405 if( nullptr == aPlugin )
406 return;
407
408 int nExt = aPlugin->GetNExtensions();
409
410 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] adding %d extensions" ),
411 __FILE__, __FUNCTION__, __LINE__, nExt );
412
413 for( int i = 0; i < nExt; ++i )
414 {
415 char const* cp = aPlugin->GetModelExtension( i );
416 wxString ws;
417
418 if( cp )
419 ws = wxString( cp );
420
421 if( !ws.empty() )
422 {
423 m_ExtMap.emplace( ws, aPlugin );
424 }
425
426 }
427}
428
429
430std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const noexcept
431{
432 return &m_FileFilters;
433}
434
435
436SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
437{
438 wxFileName raw( aFileName );
439 wxString ext_to_find = raw.GetExt();
440
441#ifdef _WIN32
442 // note: plugins only have a lowercase filter within Windows; including an uppercase
443 // filter will result in duplicate file entries and should be avoided.
444 ext_to_find.MakeLower();
445#endif
446
447 // .gz files are compressed versions that may have additional information in the previous extension
448 if( ext_to_find == wxT( "gz" ) )
449 {
450 wxFileName second( raw.GetName() );
451 ext_to_find = second.GetExt() + wxT( ".gz" );
452 }
453
454 std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
455 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
456
457 items = m_ExtMap.equal_range( ext_to_find );
458 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
459
460 while( sL != items.second )
461 {
462 if( sL->second->CanRender() )
463 {
464 SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
465
466 if( nullptr != sp )
467 {
468 sL->second->GetPluginInfo( aPluginInfo );
469 return sp;
470 }
471 }
472
473 ++sL;
474 }
475
476 return nullptr;
477}
478
479
481{
482 std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
483 std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
484
485 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] closing %d extensions" ),
486 __FILE__, __FUNCTION__, __LINE__, static_cast<int>( m_Plugins.size() ) );
487
488 while( sP != eP )
489 {
490 (*sP)->Close();
491 ++sP;
492 }
493}
494
495
496bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
497{
498 if( nullptr == aTag || aTag[0] == 0 || m_Plugins.empty() )
499 return false;
500
501 std::string tname = aTag;
502 std::string pname; // plugin name
503
504 size_t cpos = tname.find( ':' );
505
506 // if there is no colon or plugin name then the tag is bad
507 if( cpos == std::string::npos || cpos == 0 )
508 return false;
509
510 pname = tname.substr( 0, cpos );
511 std::string ptag; // tag from the plugin
512
513 std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
514 std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
515
516 while( pS != pE )
517 {
518 ptag.clear();
519 (*pS)->GetPluginInfo( ptag );
520
521 // if the plugin name matches then the version
522 // must also match
523 if( !ptag.compare( 0, pname.size(), pname ) )
524 {
525 if( ptag.compare( tname ) )
526 return false;
527
528 return true;
529 }
530
531 ++pS;
532 }
533
534 return true;
535}
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:304
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:348
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