You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

491 lines
14 KiB

3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
  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. #include <utility>
  25. #include <iostream>
  26. #include <sstream>
  27. #include <wx/dir.h>
  28. #include <wx/dynlib.h>
  29. #include <wx/log.h>
  30. #include <wx/stdpaths.h>
  31. #include <wx/string.h>
  32. #include <common.h>
  33. #include <paths.h>
  34. #include <wx_filename.h>
  35. #include "3d_plugin_manager.h"
  36. #include "plugins/3d/3d_plugin.h"
  37. #include "3d_cache/sg/scenegraph.h"
  38. #include "plugins/ldr/3d/pluginldr3D.h"
  39. /**
  40. * Flag to enable 3D plugin manager debug tracing.
  41. *
  42. * Use "KI_TRACE_EDA_3D_VIEWER" to enable.
  43. *
  44. * @ingroup trace_env_vars
  45. */
  46. #define MASK_3D_PLUGINMGR "3D_PLUGIN_MANAGER"
  47. S3D_PLUGIN_MANAGER::S3D_PLUGIN_MANAGER()
  48. {
  49. // create the initial file filter list entry
  50. m_FileFilters.emplace_back( _( "All Files" ) + wxT( " (*.*)|*.*" ) );
  51. // discover and load plugins
  52. loadPlugins();
  53. #ifdef DEBUG
  54. if( !m_ExtMap.empty() )
  55. {
  56. std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator sM = m_ExtMap.begin();
  57. std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::const_iterator eM = m_ExtMap.end();
  58. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * Extension [plugin name]:\n" ) );
  59. while( sM != eM )
  60. {
  61. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s' [%s]\n" ), sM->first.GetData(),
  62. sM->second->GetKicadPluginName() );
  63. ++sM;
  64. }
  65. }
  66. else
  67. {
  68. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No plugins available\n" ) );
  69. }
  70. if( !m_FileFilters.empty() )
  71. {
  72. /// list of file filters
  73. std::list< wxString >::const_iterator sFF = m_FileFilters.begin();
  74. std::list< wxString >::const_iterator eFF = m_FileFilters.end();
  75. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * File filters:\n" ) );
  76. while( sFF != eFF )
  77. {
  78. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " + '%s'\n" ), (*sFF).GetData() );
  79. ++sFF;
  80. }
  81. }
  82. else
  83. {
  84. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * No file filters available\n" ) );
  85. }
  86. #endif // DEBUG
  87. }
  88. S3D_PLUGIN_MANAGER::~S3D_PLUGIN_MANAGER()
  89. {
  90. std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
  91. std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
  92. while( sP != eP )
  93. {
  94. (*sP)->Close();
  95. delete *sP;
  96. ++sP;
  97. }
  98. m_Plugins.clear();
  99. }
  100. void S3D_PLUGIN_MANAGER::loadPlugins( void )
  101. {
  102. std::list<wxString> searchpaths;
  103. std::list<wxString> pluginlist;
  104. wxFileName fn;
  105. #ifndef __WXMAC__
  106. if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
  107. {
  108. // set up to work from the build directory
  109. fn.Assign( wxStandardPaths::Get().GetExecutablePath() );
  110. fn.AppendDir( wxT( ".." ) );
  111. fn.AppendDir( wxT( "plugins" ) );
  112. fn.AppendDir( wxT( "3d" ) );
  113. std::string testpath = std::string( fn.GetPathWithSep().ToUTF8() );
  114. checkPluginPath( testpath, searchpaths );
  115. // add subdirectories too
  116. wxDir debugPluginDir;
  117. wxString subdir;
  118. debugPluginDir.Open( testpath );
  119. if( debugPluginDir.IsOpened()
  120. && debugPluginDir.GetFirst( &subdir, wxEmptyString, wxDIR_DIRS ) )
  121. {
  122. checkPluginPath( testpath + subdir, searchpaths );
  123. while( debugPluginDir.GetNext( &subdir ) )
  124. checkPluginPath( testpath + subdir, searchpaths );
  125. }
  126. }
  127. fn.AssignDir( PATHS::GetStockPlugins3DPath() );
  128. checkPluginPath( std::string( fn.GetPathWithSep().ToUTF8() ), searchpaths );
  129. #else
  130. // Search path on OS X is
  131. // (1) Machine /Library/Application Support/kicad/PlugIns/3d
  132. checkPluginPath( PATHS::GetOSXKicadMachineDataDir() + wxT( "/PlugIns/3d" ), searchpaths );
  133. // (2) Bundle kicad.app/Contents/PlugIns/3d
  134. fn.AssignDir( PATHS::GetStockPlugins3DPath() );
  135. checkPluginPath( fn.GetPathWithSep(), searchpaths );
  136. #endif
  137. std::list< wxString >::iterator sPL = searchpaths.begin();
  138. std::list< wxString >::iterator ePL = searchpaths.end();
  139. while( sPL != ePL )
  140. {
  141. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] searching path: '%s'" ),
  142. __FILE__, __FUNCTION__, __LINE__, (*sPL).ToUTF8() );
  143. listPlugins( *sPL, pluginlist );
  144. ++sPL;
  145. }
  146. if( pluginlist.empty() )
  147. return;
  148. sPL = pluginlist.begin();
  149. ePL = pluginlist.end();
  150. while( sPL != ePL )
  151. {
  152. KICAD_PLUGIN_LDR_3D* pp = new KICAD_PLUGIN_LDR_3D;
  153. if( pp->Open( sPL->ToUTF8() ) )
  154. {
  155. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding plugin" ),
  156. __FILE__, __FUNCTION__, __LINE__ );
  157. m_Plugins.push_back( pp );
  158. int nf = pp->GetNFilters();
  159. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] adding %d filters" ),
  160. __FILE__, __FUNCTION__, __LINE__, nf );
  161. for( int i = 0; i < nf; ++i )
  162. {
  163. char const* cp = pp->GetFileFilter( i );
  164. if( cp )
  165. addFilterString( wxString::FromUTF8Unchecked( cp ) );
  166. }
  167. addExtensionMap( pp );
  168. // close the loaded library
  169. pp->Close();
  170. }
  171. else
  172. {
  173. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] deleting plugin" ),
  174. __FILE__, __FUNCTION__, __LINE__ );
  175. delete pp;
  176. }
  177. ++sPL;
  178. }
  179. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [DEBUG] plugins loaded" ),
  180. __FILE__, __FUNCTION__, __LINE__ );
  181. }
  182. void S3D_PLUGIN_MANAGER::listPlugins( const wxString& aPath, std::list< wxString >& aPluginList )
  183. {
  184. // list potential plugins given a search path
  185. wxString nameFilter; // filter for user-loadable libraries (aka footprints)
  186. wxString lName; // stores name of enumerated files
  187. wxString fName; // full name of file
  188. wxDir wd;
  189. wd.Open( aPath );
  190. if( !wd.IsOpened() )
  191. return;
  192. nameFilter = wxT( "*" );
  193. #ifndef __WXMAC__
  194. nameFilter.Append( wxDynamicLibrary::GetDllExt( wxDL_MODULE ) );
  195. #else
  196. // wxDynamicLibrary::GetDllExt( wxDL_MODULE ) will return ".bundle" on OS X.
  197. // This might be correct, but cmake builds a ".so" for a library MODULE.
  198. // Per definition a loadable "xxx.bundle" is similar to an "xxx.app" app
  199. // bundle being a folder with some special content in it. We obviously don't
  200. // want to have that here for our loadable module, so just use ".so".
  201. nameFilter.Append( wxS( ".so" ) );
  202. #endif
  203. wxString lp = wd.GetNameWithSep();
  204. if( wd.GetFirst( &lName, nameFilter, wxDIR_FILES ) )
  205. {
  206. fName = lp + lName;
  207. checkPluginName( fName, aPluginList );
  208. while( wd.GetNext( &lName ) )
  209. {
  210. fName = lp + lName;
  211. checkPluginName( fName, aPluginList );
  212. }
  213. }
  214. wd.Close();
  215. }
  216. void S3D_PLUGIN_MANAGER::checkPluginName( const wxString& aPath,
  217. std::list< wxString >& aPluginList )
  218. {
  219. // check the existence of a plugin name and add it to the list
  220. if( aPath.empty() || !wxFileName::FileExists( aPath ) )
  221. return;
  222. wxFileName path( ExpandEnvVarSubstitutions( aPath, nullptr ) );
  223. path.Normalize( FN_NORMALIZE_FLAGS );
  224. // determine if the path is already in the list
  225. wxString wxpath = path.GetFullPath();
  226. std::list< wxString >::iterator bl = aPluginList.begin();
  227. std::list< wxString >::iterator el = aPluginList.end();
  228. while( bl != el )
  229. {
  230. if( 0 == (*bl).Cmp( wxpath ) )
  231. return;
  232. ++bl;
  233. }
  234. aPluginList.push_back( wxpath );
  235. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] found 3D plugin '%s'\n" ), wxpath.GetData() );
  236. }
  237. void S3D_PLUGIN_MANAGER::checkPluginPath( const wxString& aPath,
  238. std::list< wxString >& aSearchList )
  239. {
  240. if( aPath.empty() )
  241. return;
  242. wxLogTrace( MASK_3D_PLUGINMGR, wxT( " * [INFO] checking if valid plugin directory '%s'\n" ),
  243. aPath.GetData() );
  244. wxFileName path;
  245. path.AssignDir( aPath );
  246. path.Normalize( FN_NORMALIZE_FLAGS );
  247. if( !wxFileName::DirExists( path.GetFullPath() ) )
  248. return;
  249. // determine if the directory is already in the list
  250. wxString wxpath = path.GetFullPath();
  251. std::list< wxString >::iterator bl = aSearchList.begin();
  252. std::list< wxString >::iterator el = aSearchList.end();
  253. while( bl != el )
  254. {
  255. if( 0 == (*bl).Cmp( wxpath ) )
  256. return;
  257. ++bl;
  258. }
  259. aSearchList.push_back( wxpath );
  260. }
  261. void S3D_PLUGIN_MANAGER::addFilterString( const wxString& aFilterString )
  262. {
  263. // add an entry to the file filter list
  264. if( aFilterString.empty() )
  265. return;
  266. std::list< wxString >::iterator sFF = m_FileFilters.begin();
  267. std::list< wxString >::iterator eFF = m_FileFilters.end();
  268. while( sFF != eFF )
  269. {
  270. if( 0 == (*sFF).Cmp( aFilterString ) )
  271. return;
  272. ++sFF;
  273. }
  274. m_FileFilters.push_back( aFilterString );
  275. return;
  276. }
  277. void S3D_PLUGIN_MANAGER::addExtensionMap( KICAD_PLUGIN_LDR_3D* aPlugin )
  278. {
  279. // add entries to the extension map
  280. if( nullptr == aPlugin )
  281. return;
  282. int nExt = aPlugin->GetNExtensions();
  283. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] adding %d extensions" ),
  284. __FILE__, __FUNCTION__, __LINE__, nExt );
  285. for( int i = 0; i < nExt; ++i )
  286. {
  287. char const* cp = aPlugin->GetModelExtension( i );
  288. wxString ws;
  289. if( cp )
  290. ws = wxString::FromUTF8Unchecked( cp );
  291. if( !ws.empty() )
  292. {
  293. m_ExtMap.emplace( ws, aPlugin );
  294. }
  295. }
  296. }
  297. std::list< wxString > const* S3D_PLUGIN_MANAGER::GetFileFilters( void ) const noexcept
  298. {
  299. return &m_FileFilters;
  300. }
  301. SCENEGRAPH* S3D_PLUGIN_MANAGER::Load3DModel( const wxString& aFileName, std::string& aPluginInfo )
  302. {
  303. wxFileName raw( aFileName );
  304. wxString ext_to_find = raw.GetExt();
  305. #ifdef _WIN32
  306. // note: plugins only have a lowercase filter within Windows; including an uppercase
  307. // filter will result in duplicate file entries and should be avoided.
  308. ext_to_find.MakeLower();
  309. #endif
  310. // .gz files are compressed versions that may have additional information in the previous extension
  311. if( ext_to_find == wxT( "gz" ) )
  312. {
  313. wxFileName second( raw.GetName() );
  314. ext_to_find = second.GetExt() + wxT( ".gz" );
  315. }
  316. std::pair < std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator,
  317. std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator > items;
  318. items = m_ExtMap.equal_range( ext_to_find );
  319. std::multimap< const wxString, KICAD_PLUGIN_LDR_3D* >::iterator sL = items.first;
  320. while( sL != items.second )
  321. {
  322. if( sL->second->CanRender() )
  323. {
  324. SCENEGRAPH* sp = sL->second->Load( aFileName.ToUTF8() );
  325. if( nullptr != sp )
  326. {
  327. sL->second->GetPluginInfo( aPluginInfo );
  328. return sp;
  329. }
  330. }
  331. ++sL;
  332. }
  333. return nullptr;
  334. }
  335. void S3D_PLUGIN_MANAGER::ClosePlugins( void )
  336. {
  337. std::list< KICAD_PLUGIN_LDR_3D* >::iterator sP = m_Plugins.begin();
  338. std::list< KICAD_PLUGIN_LDR_3D* >::iterator eP = m_Plugins.end();
  339. wxLogTrace( MASK_3D_PLUGINMGR, wxT( "%s:%s:%d * [INFO] closing %d extensions" ),
  340. __FILE__, __FUNCTION__, __LINE__, static_cast<int>( m_Plugins.size() ) );
  341. while( sP != eP )
  342. {
  343. (*sP)->Close();
  344. ++sP;
  345. }
  346. }
  347. bool S3D_PLUGIN_MANAGER::CheckTag( const char* aTag )
  348. {
  349. if( nullptr == aTag || aTag[0] == 0 || m_Plugins.empty() )
  350. return false;
  351. std::string tname = aTag;
  352. std::string pname; // plugin name
  353. size_t cpos = tname.find( ':' );
  354. // if there is no colon or plugin name then the tag is bad
  355. if( cpos == std::string::npos || cpos == 0 )
  356. return false;
  357. pname = tname.substr( 0, cpos );
  358. std::string ptag; // tag from the plugin
  359. std::list< KICAD_PLUGIN_LDR_3D* >::iterator pS = m_Plugins.begin();
  360. std::list< KICAD_PLUGIN_LDR_3D* >::iterator pE = m_Plugins.end();
  361. while( pS != pE )
  362. {
  363. ptag.clear();
  364. (*pS)->GetPluginInfo( ptag );
  365. // if the plugin name matches then the version
  366. // must also match
  367. if( !ptag.compare( 0, pname.size(), pname ) )
  368. {
  369. if( ptag.compare( tname ) )
  370. return false;
  371. return true;
  372. }
  373. ++pS;
  374. }
  375. return true;
  376. }