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 <[email protected]>
5 * Copyright (C) 2020-2022 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( ".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 aPluginList.push_back( wxpath );
298
299 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] found 3D plugin '%s'\n" ), wxpath.GetData() );
300}
301
302
303void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
304 std::list< wxString >& aSearchList )
305{
306 if( aPath.empty() )
307 return;
308
309 wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] checking if valid plugin directory '%s'\n" ),
310 aPath.GetData() );
311
312 wxFileName path;
313 path.AssignDir( aPath );
314 path.Normalize( FN_NORMALIZE_FLAGS );
315
316 if( !wxFileName::DirExists( path.GetFullPath() ) )
317 return;
318
319 // determine if the directory is already in the list
320 wxString wxpath = path.GetFullPath();
321 std::list< wxString >::iterator bl = aSearchList.begin();
322 std::list< wxString >::iterator el = aSearchList.end();
323
324 while( bl != el )
325 {
326 if( 0 == (*bl).Cmp( wxpath ) )
327 return;
328
329 ++bl;
330 }
331
332 aSearchList.push_back( wxpath );
333}
334
335
336void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
337{
338 // add an entry to the file filter list
339 if( aFilterString.empty() )
340 return;
341
342 std::list< wxString >::iterator sFF = m_FileFilters.begin();
343 std::list< wxString >::iterator eFF = m_FileFilters.end();
344
345 while( sFF != eFF )
346 {
347 if( 0 == (*sFF).Cmp( aFilterString ) )
348 return;
349
350 ++sFF;
351 }
352
353 m_FileFilters.push_back( aFilterString );
354 return;
355}
356
357
359{
360 // add entries to the extension map
361 if( nullptr == aPlugin )
362 return;
363
364 int nExt = aPlugin->GetNExtensions();
365
366 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] adding %d extensions" ),
367 __FILE__, __FUNCTION__, __LINE__, nExt );
368
369 for( int i = 0; i < nExt; ++i )
370 {
371 char const* cp = aPlugin->GetModelExtension( i );
372 wxString ws;
373
374 if( cp )
375 ws = wxString::FromUTF8Unchecked( cp );
376
377 if( !ws.empty() )
378 {
379 m_ExtMap.emplace( ws, aPlugin );
380 }
381
382 }
383}
384
385
386std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const noexcept
387{
388 return &m_FileFilters;
389}
390
391
392SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
393{
394 wxFileName raw( aFileName );
395 wxString ext_to_find = raw.GetExt();
396
397#ifdef _WIN32
398 // note: plugins only have a lowercase filter within Windows; including an uppercase
399 // filter will result in duplicate file entries and should be avoided.
400 ext_to_find.MakeLower();
401#endif
402
403 // .gz files are compressed versions that may have additional information in the previous extension
404 if( ext_to_find == wxT( "gz" ) )
405 {
406 wxFileName second( raw.GetName() );
407 ext_to_find = second.GetExt() + wxT( ".gz" );
408 }
409
410 std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
411 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
412
413 items = m_ExtMap.equal_range( ext_to_find );
414 std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
415
416 while( sL != items.second )
417 {
418 if( sL->second->CanRender() )
419 {
420 SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
421
422 if( nullptr != sp )
423 {
424 sL->second->GetPluginInfo( aPluginInfo );
425 return sp;
426 }
427 }
428
429 ++sL;
430 }
431
432 return nullptr;
433}
434
435
437{
438 std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
439 std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
440
441 wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] closing %d extensions" ),
442 __FILE__, __FUNCTION__, __LINE__, static_cast<int>( m_Plugins.size() ) );
443
444 while( sP != eP )
445 {
446 (*sP)->Close();
447 ++sP;
448 }
449}
450
451
452bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
453{
454 if( nullptr == aTag || aTag[0] == 0 || m_Plugins.empty() )
455 return false;
456
457 std::string tname = aTag;
458 std::string pname; // plugin name
459
460 size_t cpos = tname.find( ':' );
461
462 // if there is no colon or plugin name then the tag is bad
463 if( cpos == std::string::npos || cpos == 0 )
464 return false;
465
466 pname = tname.substr( 0, cpos );
467 std::string ptag; // tag from the plugin
468
469 std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
470 std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
471
472 while( pS != pE )
473 {
474 ptag.clear();
475 (*pS)->GetPluginInfo( ptag );
476
477 // if the plugin name matches then the version
478 // must also match
479 if( !ptag.compare( 0, pname.size(), pname ) )
480 {
481 if( ptag.compare( tname ) )
482 return false;
483
484 return true;
485 }
486
487 ++pS;
488 }
489
490 return true;
491}
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:278
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, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:267
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:38