|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com> * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
#include <utility>
#include <iostream>
#include <sstream>
#include <wx/dir.h>
#include <wx/dynlib.h>
#include <wx/log.h>
#include <wx/stdpaths.h>
#include <wx/string.h>
#include <common.h>
#include <paths.h>
#include <wx_filename.h>
#include "3d_plugin_manager.h"
#include "plugins/3d/3d_plugin.h"
#include "3d_cache/sg/scenegraph.h"
#include "plugins/ldr/3d/pluginldr3D.h"
/**
* Flag to enable 3D plugin manager debug tracing. * * Use "KI_TRACE_EDA_3D_VIEWER" to enable. * * @ingroup trace_env_vars */#define MASK_3D_PLUGINMGR "3D_PLUGIN_MANAGER"
S3D_PLUGIN_MANAGER::S3D_PLUGIN_MANAGER(){ // create the initial file filter list entry
m_FileFilters.emplace_back( _( "All Files" ) + wxT( " (*.*)|*.*" ) );
// discover and load plugins
loadPlugins();
#ifdef DEBUG
if( !m_ExtMap.empty() ) { std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator sM = m_ExtMap.begin(); std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator eM = m_ExtMap.end(); wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * Extension [plugin name]:\n" ) );
while( sM != eM ) { wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s' [%s]\n" ), sM->first.GetData(), sM->second->GetKicadPluginName() ); ++sM; }
} else { wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No plugins available\n" ) ); }
if( !m_FileFilters.empty() ) { /// list of file filters
std::list< wxString >::const_iterator sFF = m_FileFilters.begin(); std::list< wxString >::const_iterator eFF = m_FileFilters.end(); wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * File filters:\n" ) );
while( sFF != eFF ) { wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s'\n" ), (*sFF).GetData() ); ++sFF; } } else { wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No file filters available\n" ) ); }#endif // DEBUG
}
S3D_PLUGIN_MANAGER::~S3D_PLUGIN_MANAGER(){ std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin(); std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
while( sP != eP ) { (*sP)->Close(); delete *sP; ++sP; }
m_Plugins.clear();}
void S3D_PLUGIN_MANAGER::loadPlugins( void ){ std::list<wxString> searchpaths; std::list<wxString> pluginlist; wxFileName fn;
#ifndef __WXMAC__
if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) ) { // set up to work from the build directory
fn.Assign( wxStandardPaths::Get().GetExecutablePath() ); fn.AppendDir( wxT( ".." ) ); fn.AppendDir( wxT( "plugins" ) ); fn.AppendDir( wxT( "3d" ) );
std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() ); checkPluginPath( testpath, searchpaths );
// add subdirectories too
wxDir debugPluginDir; wxString subdir;
debugPluginDir.Open( testpath );
if( debugPluginDir.IsOpened() && debugPluginDir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) ) { checkPluginPath( testpath + subdir, searchpaths );
while( debugPluginDir.GetNext( &subdir ) ) checkPluginPath( testpath + subdir, searchpaths ); } }
fn.AssignDir( PATHS::GetStockPlugins3DPath() ); checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );#else
// Search path on OS X is
// (1) Machine /Library/Application Support/kicad/PlugIns/3d
checkPluginPath( PATHS::GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
// (2) Bundle kicad.app/Contents/PlugIns/3d
fn.AssignDir( PATHS::GetStockPlugins3DPath() ); checkPluginPath( fn.GetPathWithSep(), searchpaths );#endif
std::list< wxString >::iterator sPL = searchpaths.begin(); std::list< wxString >::iterator ePL = searchpaths.end();
while( sPL != ePL ) { wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] searching path: '%s'" ), __FILE__, __FUNCTION__, __LINE__, (*sPL).ToUTF8() );
listPlugins( *sPL, pluginlist ); ++sPL; }
if( pluginlist.empty() ) return;
sPL = pluginlist.begin(); ePL = pluginlist.end();
while( sPL != ePL ) { KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
if( pp->Open( sPL->ToUTF8() ) ) { wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding plugin" ), __FILE__, __FUNCTION__, __LINE__ );
m_Plugins.push_back( pp ); int nf = pp->GetNFilters();
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding %d filters" ), __FILE__, __FUNCTION__, __LINE__, nf );
for( int i = 0; i < nf; ++i ) { char const* cp = pp->GetFileFilter( i );
if( cp ) addFilterString( wxString::FromUTF8Unchecked( cp ) ); }
addExtensionMap( pp );
// close the loaded library
pp->Close(); } else { wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] deleting plugin" ), __FILE__, __FUNCTION__, __LINE__ );
delete pp; }
++sPL; }
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] plugins loaded" ), __FILE__, __FUNCTION__, __LINE__ );}
void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath, std::list< wxString >& aPluginList ){ // list potential plugins given a search path
wxString nameFilter; // filter for user-loadable libraries (aka footprints)
wxString lName; // stores name of enumerated files
wxString fName; // full name of file
wxDir wd; wd.Open( aPath );
if( !wd.IsOpened() ) return;
nameFilter = wxT( "*" );
#ifndef __WXMAC__
nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );#else
// wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
// This might be correct, but cmake builds a ".so" for a library MODULE.
// Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
// bundle being a folder with some special content in it. We obviously don't
// want to have that here for our loadable module, so just use ".so".
nameFilter.Append( wxS( ".so" ) );#endif
wxString lp = wd.GetNameWithSep();
if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) ) { fName = lp + lName; checkPluginName( fName, aPluginList );
while( wd.GetNext( &lName ) ) { fName = lp + lName; checkPluginName( fName, aPluginList ); } }
wd.Close();}
void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath, std::list< wxString >& aPluginList ){ // check the existence of a plugin name and add it to the list
if( aPath.empty() || !wxFileName::FileExists( aPath ) ) return;
wxFileName path( ExpandEnvVarSubstitutions( aPath, nullptr ) );
path.Normalize( FN_NORMALIZE_FLAGS );
// determine if the path is already in the list
wxString wxpath = path.GetFullPath(); std::list< wxString >::iterator bl = aPluginList.begin(); std::list< wxString >::iterator el = aPluginList.end();
while( bl != el ) { if( 0 == (*bl).Cmp( wxpath ) ) return;
++bl; }
aPluginList.push_back( wxpath );
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] found 3D plugin '%s'\n" ), wxpath.GetData() );}
void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath, std::list< wxString >& aSearchList ){ if( aPath.empty() ) return;
wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] checking if valid plugin directory '%s'\n" ), aPath.GetData() );
wxFileName path; path.AssignDir( aPath ); path.Normalize( FN_NORMALIZE_FLAGS );
if( !wxFileName::DirExists( path.GetFullPath() ) ) return;
// determine if the directory is already in the list
wxString wxpath = path.GetFullPath(); std::list< wxString >::iterator bl = aSearchList.begin(); std::list< wxString >::iterator el = aSearchList.end();
while( bl != el ) { if( 0 == (*bl).Cmp( wxpath ) ) return;
++bl; }
aSearchList.push_back( wxpath );}
void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString ){ // add an entry to the file filter list
if( aFilterString.empty() ) return;
std::list< wxString >::iterator sFF = m_FileFilters.begin(); std::list< wxString >::iterator eFF = m_FileFilters.end();
while( sFF != eFF ) { if( 0 == (*sFF).Cmp( aFilterString ) ) return;
++sFF; }
m_FileFilters.push_back( aFilterString ); return;}
void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin ){ // add entries to the extension map
if( nullptr == aPlugin ) return;
int nExt = aPlugin->GetNExtensions();
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] adding %d extensions" ), __FILE__, __FUNCTION__, __LINE__, nExt );
for( int i = 0; i < nExt; ++i ) { char const* cp = aPlugin->GetModelExtension( i ); wxString ws;
if( cp ) ws = wxString::FromUTF8Unchecked( cp );
if( !ws.empty() ) { m_ExtMap.emplace( ws, aPlugin ); }
}}
std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const noexcept{ return &m_FileFilters;}
SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo ){ wxFileName raw( aFileName ); wxString ext_to_find = raw.GetExt();
#ifdef _WIN32
// note: plugins only have a lowercase filter within Windows; including an uppercase
// filter will result in duplicate file entries and should be avoided.
ext_to_find.MakeLower();#endif
// .gz files are compressed versions that may have additional information in the previous extension
if( ext_to_find == wxT( "gz" ) ) { wxFileName second( raw.GetName() ); ext_to_find = second.GetExt() + wxT( ".gz" ); }
std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator, std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
items = m_ExtMap.equal_range( ext_to_find ); std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
while( sL != items.second ) { if( sL->second->CanRender() ) { SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
if( nullptr != sp ) { sL->second->GetPluginInfo( aPluginInfo ); return sp; } }
++sL; }
return nullptr;}
void S3D_PLUGIN_MANAGER::ClosePlugins( void ){ std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin(); std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] closing %d extensions" ), __FILE__, __FUNCTION__, __LINE__, static_cast<int>( m_Plugins.size() ) );
while( sP != eP ) { (*sP)->Close(); ++sP; }}
bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag ){ if( nullptr == aTag || aTag[0] == 0 || m_Plugins.empty() ) return false;
std::string tname = aTag; std::string pname; // plugin name
size_t cpos = tname.find( ':' );
// if there is no colon or plugin name then the tag is bad
if( cpos == std::string::npos || cpos == 0 ) return false;
pname = tname.substr( 0, cpos ); std::string ptag; // tag from the plugin
std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin(); std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
while( pS != pE ) { ptag.clear(); (*pS)->GetPluginInfo( ptag );
// if the plugin name matches then the version
// must also match
if( !ptag.compare( 0, pname.size(), pname ) ) { if( ptag.compare( tname ) ) return false;
return true; }
++pS; }
return true;}
|