TuttleOFX
1
|
00001 /* 00002 * Software License : 00003 * 00004 * Copyright (c) 2007-2009, The Open Effects Association Ltd. All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions are met: 00008 * 00009 * Redistributions of source code must retain the above copyright notice, 00010 * this list of conditions and the following disclaimer. 00011 * Redistributions in binary form must reproduce the above copyright notice, 00012 * this list of conditions and the following disclaimer in the documentation 00013 * and/or other materials provided with the distribution. 00014 * Neither the name The Open Effects Association Ltd, nor the names of its 00015 * contributors may be used to endorse or promote products derived from this 00016 * software without specific prior written permission. 00017 * 00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00019 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00020 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00021 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 00022 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00023 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00024 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 00025 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00026 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00027 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00028 */ 00029 00030 // ofx host 00031 #include "property/OfxhSet.hpp" 00032 #include "OfxhBinary.hpp" 00033 #include "OfxhMemory.hpp" 00034 #include "OfxhPluginAPICache.hpp" 00035 #include "OfxhPluginCache.hpp" 00036 #include "OfxhHost.hpp" 00037 #include "OfxhUtilities.hpp" 00038 00039 // ofx 00040 #include <ofxCore.h> 00041 #include <ofxImageEffect.h> 00042 00043 #include <map> 00044 #include <string> 00045 #include <iostream> 00046 #include <fstream> 00047 #include <sstream> 00048 #include <cstring> 00049 #include <cstdlib> 00050 00051 // Define this to enable ofx plugin cache debug messages. 00052 //#define CACHE_DEBUG 00053 00054 #if defined ( __linux__ ) 00055 00056 #define DIRLIST_SEP_CHARS ":;" 00057 #define DIRSEP "/" 00058 #include <dirent.h> 00059 00060 #define ARCHSTR getArchStr() 00061 00062 #elif defined ( __APPLE__ ) 00063 00064 #define DIRLIST_SEP_CHARS ";:" 00065 #define ARCHSTR "MacOS" 00066 #define DIRSEP "/" 00067 #include <dirent.h> 00068 00069 #elif defined ( WINDOWS ) 00070 #define DIRLIST_SEP_CHARS ";" 00071 #ifdef _WIN64 00072 #define ARCHSTR "win64" 00073 #else 00074 #define ARCHSTR "win32" 00075 #endif 00076 #define DIRSEP "\\" 00077 00078 // CINTERFACE needs to be declared if compiling with VC++ 00079 #include <shlobj.h> 00080 #include <tchar.h> 00081 #ifndef _MSC_VER 00082 #define SHGFP_TYPE_CURRENT 0 00083 #endif 00084 00085 #endif 00086 00087 namespace tuttle { 00088 namespace host { 00089 namespace ofx { 00090 00091 #if defined ( __linux__ ) 00092 00093 static const char* getArchStr() 00094 { 00095 if( sizeof( void* ) == 4 ) 00096 { 00097 return "Linux-x86"; 00098 } 00099 else 00100 { 00101 return "Linux-x86-64"; 00102 } 00103 } 00104 00105 #endif 00106 #if defined ( WINDOWS ) 00107 00108 const TCHAR* getStdOFXPluginPath( const std::string& hostId = "Plugins" ) 00109 { 00110 static TCHAR buffer[MAX_PATH]; 00111 static int gotIt = 0; 00112 00113 if( !gotIt ) 00114 { 00115 gotIt = 1; 00116 SHGetFolderPath( NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, SHGFP_TYPE_CURRENT, buffer ); 00117 strncat( buffer, "\\OFX\\Plugins", MAX_PATH ); 00118 } 00119 return buffer; 00120 } 00121 00122 #endif 00123 00124 std::string OFXGetEnv( const char* e ) 00125 { 00126 #if !defined( __GNUC__ ) && defined( WINDOWS ) 00127 std::size_t requiredSize; 00128 getenv_s( &requiredSize, 0, 0, e ); 00129 std::vector<char> buffer( requiredSize ); 00130 if( requiredSize > 0 ) 00131 { 00132 getenv_s( &requiredSize, &buffer.front(), requiredSize, e ); 00133 return &buffer.front(); 00134 } 00135 return ""; 00136 #else 00137 const char* env_value = getenv( e ); 00138 if( env_value == NULL ) 00139 return ""; 00140 return env_value; 00141 #endif 00142 } 00143 00144 OfxhPluginCache::OfxhPluginCache() 00145 : _ignoreCache( false ) 00146 , _cacheVersion( "" ) 00147 , _dirty( false ) 00148 , _enablePluginSeek( true ) 00149 { 00150 std::string s = OFXGetEnv( "OFX_PLUGIN_PATH" ); 00151 00152 while( s.length() ) 00153 { 00154 int spos = int(s.find_first_of( DIRLIST_SEP_CHARS ) ); 00155 00156 std::string path; 00157 00158 if( spos != -1 ) 00159 { 00160 path = s.substr( 0, spos ); 00161 s = s.substr( spos + 1 ); 00162 } 00163 else 00164 { 00165 path = s; 00166 s = ""; 00167 } 00168 00169 _pluginPath.push_back( path ); 00170 } 00171 00172 #if defined( WINDOWS ) 00173 _pluginPath.push_back( getStdOFXPluginPath() ); 00174 _pluginPath.push_back( "C:\\Program Files\\Common Files\\OFX\\Plugins" ); 00175 #endif 00176 #if defined( __linux__ ) 00177 _pluginPath.push_back( "/usr/OFX/Plugins" ); 00178 #endif 00179 #if defined( __APPLE__ ) 00180 _pluginPath.push_back( "/Library/OFX/Plugins" ); 00181 #endif 00182 } 00183 00184 OfxhPluginCache::~OfxhPluginCache() 00185 {} 00186 00187 void OfxhPluginCache::setPluginHostPath( const std::string& hostId ) 00188 { 00189 #if defined( WINDOWS ) 00190 _pluginPath.push_back( getStdOFXPluginPath( hostId ) ); 00191 _pluginPath.push_back( "C:\\Program Files\\Common Files\\OFX\\" + hostId ); 00192 #endif 00193 #if defined( __linux__ ) 00194 _pluginPath.push_back( "/usr/OFX/" + hostId ); 00195 #endif 00196 #if defined( __APPLE__ ) 00197 _pluginPath.push_back( "/Library/OFX/" + hostId ); 00198 #endif 00199 } 00200 00201 void OfxhPluginCache::scanDirectory( std::set<std::string>& foundBinFiles, const std::string& dir, bool recurse ) 00202 { 00203 #ifdef CACHE_DEBUG 00204 TUTTLE_TLOG( TUTTLE_INFO, "looking in " << dir << " for plugins" ); 00205 #endif 00206 00207 #if defined ( WINDOWS ) 00208 WIN32_FIND_DATA findData; 00209 HANDLE findHandle; 00210 #else 00211 DIR* d = opendir( dir.c_str() ); 00212 if( !d ) 00213 { 00214 return; 00215 } 00216 #endif 00217 00218 _pluginDirs.push_back( dir.c_str() ); 00219 00220 #if defined ( UNIX ) 00221 while( dirent * de = readdir( d ) ) 00222 #elif defined ( WINDOWS ) 00223 findHandle = FindFirstFile( ( dir + "\\*" ).c_str(), &findData ); 00224 00225 if( findHandle == INVALID_HANDLE_VALUE ) 00226 { 00227 return; 00228 } 00229 00230 while( 1 ) 00231 #endif 00232 { 00233 #if defined ( UNIX ) 00234 std::string name = de->d_name; 00235 bool isdir = true; 00236 #else 00237 std::string name = findData.cFileName; 00238 bool isdir = ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0; 00239 #endif 00240 if( name.find( ".ofx.bundle" ) != std::string::npos ) 00241 { 00242 std::string barename = name.substr( 0, name.length() - strlen( ".bundle" ) ); 00243 std::string bundlepath = dir + DIRSEP + name; 00244 std::string binpath = bundlepath + DIRSEP "Contents" DIRSEP + ARCHSTR + DIRSEP + barename; 00245 00246 foundBinFiles.insert( binpath ); 00247 00248 if( _knownBinFiles.find( binpath ) == _knownBinFiles.end() ) 00249 { 00250 #ifdef CACHE_DEBUG 00251 TUTTLE_TLOG( TUTTLE_INFO, "found non-cached binary " << binpath ); 00252 #endif 00253 setDirty(); 00254 try 00255 { 00256 // the binary was not in the cache 00257 OfxhPluginBinary* pb = new OfxhPluginBinary( binpath, bundlepath, this ); 00258 //TUTTLE_LOG_WARNING( binpath ); 00259 _binaries.push_back( pb ); 00260 _knownBinFiles.insert( binpath ); 00261 //TUTTLE_LOG_WARNING( binpath << " (" << pb->getNPlugins() << ")" ); 00262 for( int j = 0; j < pb->getNPlugins(); ++j ) 00263 { 00264 OfxhPlugin& plug = pb->getPlugin( j ); 00265 APICache::OfxhPluginAPICacheI& api = plug.getApiHandler(); 00266 api.loadFromPlugin( plug ); 00267 } 00268 } 00269 catch(... ) 00270 { 00271 TUTTLE_LOG_WARNING( "Can't load " << binpath ); 00272 TUTTLE_LOG_WARNING( boost::current_exception_diagnostic_information() ); 00273 TUTTLE_LOG_WARNING( "LD_LIBRARY_PATH: " << std::getenv("LD_LIBRARY_PATH") ); 00274 } 00275 } 00276 else 00277 { 00278 #ifdef CACHE_DEBUG 00279 TUTTLE_TLOG( TUTTLE_INFO, "found cached binary " << binpath ); 00280 #endif 00281 } 00282 } 00283 else 00284 { 00285 if( isdir && ( recurse && name[0] != '@' && name != "." && name != ".." ) ) 00286 { 00287 scanDirectory( foundBinFiles, dir + DIRSEP + name, recurse ); 00288 } 00289 } 00290 #if defined( WINDOWS ) 00291 int rval = FindNextFile( findHandle, &findData ); 00292 00293 if( rval == 0 ) 00294 { 00295 break; 00296 } 00297 #endif 00298 } 00299 00300 #if defined( UNIX ) 00301 closedir( d ); 00302 #else 00303 FindClose( findHandle ); 00304 #endif 00305 } 00306 00307 std::string OfxhPluginCache::seekPluginFile( const std::string& baseName ) const 00308 { 00309 // Exit early if disabled 00310 if( !_enablePluginSeek ) 00311 return ""; 00312 00313 for( std::list<std::string>::const_iterator paths = _pluginDirs.begin(); 00314 paths != _pluginDirs.end(); 00315 ++paths ) 00316 { 00317 std::string candidate = *paths + DIRSEP + baseName; 00318 FILE* f = fopen( candidate.c_str(), "r" ); 00319 if( f ) 00320 { 00321 fclose( f ); 00322 return candidate; 00323 } 00324 } 00325 return ""; 00326 } 00327 00328 void OfxhPluginCache::scanPluginFiles() 00329 { 00330 std::set<std::string> foundBinFiles; 00331 00332 for( std::list<std::string>::iterator paths = _pluginPath.begin(); 00333 paths != _pluginPath.end(); 00334 ++paths ) 00335 { 00336 scanDirectory( foundBinFiles, *paths, _nonrecursePath.find( *paths ) == _nonrecursePath.end() ); 00337 } 00338 00339 OfxhPluginBinaryList::iterator i = _binaries.begin(); 00340 while( i != _binaries.end() ) 00341 { 00342 if( foundBinFiles.find( i->getFilePath() ) == foundBinFiles.end() ) 00343 { 00344 // the binary was in the cache, but was not on the path 00345 setDirty(); 00346 i = _binaries.erase( i ); 00347 } 00348 else 00349 { 00350 const bool binChanged = i->hasBinaryChanged(); 00351 00352 // the binary was in the cache, but the binary has changed and thus we need to reload 00353 if( binChanged ) 00354 { 00355 i->loadPluginInfo( this ); 00356 setDirty(); 00357 } 00358 00359 for( int j = 0; j < i->getNPlugins(); ++j ) 00360 { 00361 OfxhPlugin& plug = i->getPlugin( j ); 00362 try 00363 { 00364 APICache::OfxhPluginAPICacheI& api = plug.getApiHandler(); 00365 00366 if( binChanged ) 00367 { 00368 api.loadFromPlugin( plug ); // may throw 00369 } 00370 00371 std::string reason; 00372 00373 if( api.pluginSupported( plug, reason ) ) 00374 { 00375 addPlugin( &plug ); 00376 api.confirmPlugin( plug ); 00377 } 00378 else 00379 { 00380 TUTTLE_LOG_ERROR( 00381 "Ignoring plugin " << quotes(plug.getIdentifier()) << 00382 ": unsupported, " << reason << "." ); 00383 } 00384 } 00385 catch(...) 00386 { 00387 TUTTLE_LOG_ERROR( 00388 "Ignoring plugin " << quotes(plug.getIdentifier()) << 00389 ": loading error." ); 00390 00391 } 00392 } 00393 00394 ++i; 00395 } 00396 } 00397 } 00398 00399 APICache::OfxhPluginAPICacheI* OfxhPluginCache::findApiHandler( const std::string& api, int version ) 00400 { 00401 std::list<PluginCacheSupportedApi>::iterator i = _apiHandlers.begin(); 00402 while( i != _apiHandlers.end() ) 00403 { 00404 if( i->matches( api, version ) ) 00405 { 00406 return i->_handler; 00407 } 00408 ++i; 00409 } 00410 return 0; 00411 } 00412 00413 /** 00414 * get the plugin by id. vermaj and vermin can be specified. if they are not it will 00415 * pick the highest found version. 00416 */ 00417 OfxhPlugin* OfxhPluginCache::getPluginById( const std::string& id, int vermaj, int vermin ) 00418 { 00419 if( vermaj == -1 && vermin == -1 ) 00420 return _pluginsByID[id]; 00421 00422 // return the highest version one, which fits the pattern provided 00423 OfxhPlugin* sofar = 0; 00424 00425 for( std::list<OfxhPlugin*>::iterator i = _plugins.begin(); i != _plugins.end(); ++i ) 00426 { 00427 OfxhPlugin* p = *i; 00428 00429 if( p->getIdentifier() != id ) 00430 { 00431 continue; 00432 } 00433 00434 if( vermaj != -1 && p->getVersionMajor() != vermaj ) 00435 { 00436 continue; 00437 } 00438 00439 if( vermin != -1 && p->getVersionMinor() != vermin ) 00440 { 00441 continue; 00442 } 00443 00444 if( !sofar || p->trumps( *sofar ) ) 00445 { 00446 sofar = p; 00447 } 00448 } 00449 return sofar; 00450 } 00451 00452 std::ostream& operator<<( std::ostream& os, const OfxhPluginCache& v ) 00453 { 00454 os << "OfxhPluginCache {" << std::endl; 00455 00456 if( v._pluginsByID.empty() ) 00457 os << "No Plug-ins Found." << std::endl; 00458 00459 os << "________________________________________________________________________________" << std::endl; 00460 for( std::map<std::string, OfxhPlugin*>::const_iterator it = v._pluginsByID.begin(); it != v._pluginsByID.end(); ++it ) 00461 { 00462 os << "Plug-in:" << it->first << std::endl; 00463 os << " " << "Filepath: " << it->second->getBinary().getFilePath(); 00464 os << "(" << it->second->getIndex() << ")" << std::endl; 00465 00466 // os << "Contexts:" << std::endl; 00467 // const std::set<std::string>& contexts = it->second->getContexts(); 00468 // for( std::set<std::string>::const_iterator it2 = contexts.begin(); it2 != contexts.end(); ++it2 ) 00469 // os << " * " << *it2 << std::endl; 00470 // const OfxhImageEffectNodeDescriptor& d = it->second->getDescriptor(); 00471 // os << "Inputs:" << std::endl; 00472 // const std::map<std::string, attribute::OfxhClipImageDescriptor*>& inputs = d.getClips(); 00473 // for( std::map<std::string, attribute::OfxhClipImageDescriptor*>::const_iterator it2 = inputs.begin(); it2 != inputs.end(); ++it2 ) 00474 // os << " * " << it2->first << std::endl; 00475 os << "________________________________________________________________________________" << std::endl; 00476 } 00477 os << "}" << std::endl; 00478 return os; 00479 } 00480 00481 } 00482 } 00483 } 00484 00485