/****************************************************************************** Copyright (C) 2026 by FiniteSingularity 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, see . ******************************************************************************/ #include "PluginModuleLoader.hpp" #include "PluginManager.hpp" #include #include #include #include #include #include #include #include // Linux Third-Party Plugin Locations // // * User Path: // * Binary: //.so // * Data: //data // // * System Root Path: /usr // * Binary: //obs-modules/plugins/.so // * Data: /share/obs/obs-modules/plugins/ // // * User Root Path: $XDG_DATA_HOME // * Binary: /obs-studio/plugins//.so // * Data: /obs-studio/plugins//data // // * Legacy User Path: + // * Binary: /.so // * Data: / // // * Legacy System Root Path: /usr // * Binary: //obs-plugins/.so // * Data: /share/obs/obs-plugins/ // // * Legacy User Root Path: $XDG_CONFIG_HOME // * Binary: /obs-studio/plugins//bin/64bit/.so // * Data: /obs-studio/plugins//data // // * Legacy Fallback Path: // * Binary: /../../obs-plugins/64bit/.so // * Data: /share/obs/obs-plugins/ using State = OBS::PluginManager::State; using ModuleType = obs_runtime_module_type; namespace Constants = OBS::Constants; constexpr std::string_view kModulePathSuffix{"/%module%/"}; constexpr std::string_view kModuleDataPathSuffix{"/%module%/data/"}; constexpr std::string_view kXdgBinaryPath{"/obs-studio/plugins/%module%/"}; constexpr std::string_view kXdgDataPath{"/obs-studio/plugins/%module%/data/"}; constexpr std::string_view kLegacyXdgBinaryPath{"/obs-studio/plugins/%module%/bin/64bit"}; constexpr std::string_view kLegacyXdgDataPath{"/obs-studio/plugins/%module%/data"}; constexpr bool hasSystemPluginPath = !Constants::kPlatformPluginModulePath.empty() && !Constants::kPlatformPluginDataPath.empty(); namespace { State pluginLoadHelper(const ModuleLoadInfo &info, ModuleList &failedModules) { int failedModuleCount = loadPluginsByInfo(info, failedModules); State result = (failedModuleCount > 0) ? State::PartialFailure : State::Success; return result; } } // namespace namespace OBS { State PluginManager::loadPlugins(bool usePortableMode) { State userPluginState = State::Failure; std::string userPluginPath = getEnvironmentVariable(kPathVariable); if (!userPluginPath.empty()) { std::string binaryPath{userPluginPath}; std::string dataPath{userPluginPath}; binaryPath.append(kModulePathSuffix); dataPath.append(kModuleDataPathSuffix); ModuleLoadInfo info = {.path_info = {.binary = binaryPath.c_str(), .data = dataPath.c_str()}, .type = OBS_MODULE_TYPE_PLUGIN, .name = nullptr}; userPluginState = pluginLoadHelper(info, failedModules_); } else { userPluginState = State::Success; } State systemPluginState = State::Failure; if constexpr (hasSystemPluginPath) { std::string binaryPath{Constants::kPlatformPluginModulePath}; std::string dataPath{Constants::kPlatformPluginDataPath}; dataPath.append(kModulePathSuffix); ModuleLoadInfo info = {.path_info = {.binary = binaryPath.c_str(), .data = dataPath.c_str()}, .type = OBS_MODULE_TYPE_PLUGIN, .name = nullptr}; systemPluginState = pluginLoadHelper(info, failedModules_); } else { systemPluginState = State::Success; } State xdgPluginState = State::Failure; if (!usePortableMode) { std::string xdgDataHomePath = getEnvironmentVariable(Constants::kXDGDataHomeVariable); if (xdgDataHomePath.empty()) { std::string homePath = getEnvironmentVariable("HOME"); if (!homePath.empty()) { xdgDataHomePath = homePath + "/.local/share"; } } if (!xdgDataHomePath.empty()) { std::string binaryPath{xdgDataHomePath}; std::string dataPath{xdgDataHomePath}; binaryPath.append(kXdgBinaryPath); dataPath.append(kXdgDataPath); ModuleLoadInfo info = {.path_info = {.binary = binaryPath.c_str(), .data = dataPath.c_str()}, .type = OBS_MODULE_TYPE_PLUGIN, .name = nullptr}; xdgPluginState = pluginLoadHelper(info, failedModules_); } else { xdgPluginState = State::Success; } } else { xdgPluginState = State::Success; } return (userPluginState && systemPluginState && xdgPluginState) ? State::Success : State::PartialFailure; } State PluginManager::loadLegacyPlugins(bool usePortableMode) { State userPluginState = State::Failure; std::string userPluginPath = getEnvironmentVariable(kLegacyBinaryPathVariable); std::string userDataPath = getEnvironmentVariable(kLegacyDataPathVariable); if (!userPluginPath.empty() && !userDataPath.empty()) { std::string binaryPath{userPluginPath}; std::string dataPath{userDataPath}; dataPath.append(kModulePathSuffix); ModuleLoadInfo info = {.path_info = {.binary = binaryPath.c_str(), .data = dataPath.c_str()}, .type = OBS_MODULE_TYPE_LEGACY_PLUGIN, .name = nullptr}; userPluginState = pluginLoadHelper(info, failedModules_); } else { userPluginState = State::Success; } State systemPluginState = State::Failure; { std::string binaryPath{Constants::kPlatformInstallPath}; std::string dataPath{Constants::kPlatformInstallPath}; binaryPath.append(Constants::kPlatformLibraryPath); binaryPath.append("obs-plugins/"); dataPath.append(Constants::kPlatformDataPath); dataPath.append("obs/obs-plugins/%module%"); ModuleLoadInfo info = {.path_info = {.binary = binaryPath.c_str(), .data = dataPath.c_str()}, .type = OBS_MODULE_TYPE_LEGACY_PLUGIN, .name = nullptr}; systemPluginState = pluginLoadHelper(info, failedModules_); } State xdgPluginState = State::Failure; if (!usePortableMode) { std::string xdgConfigHomePath = getEnvironmentVariable(Constants::kXDGConfigHomeVariable); if (xdgConfigHomePath.empty()) { std::string homePath = getEnvironmentVariable("HOME"); if (!homePath.empty()) { xdgConfigHomePath = homePath + "/.config"; } } if (!xdgConfigHomePath.empty()) { std::string binaryPath{xdgConfigHomePath}; std::string dataPath{xdgConfigHomePath}; binaryPath.append(kLegacyXdgBinaryPath); dataPath.append(kLegacyXdgDataPath); ModuleLoadInfo info = {.path_info = {.binary = binaryPath.c_str(), .data = dataPath.c_str()}, .type = OBS_MODULE_TYPE_LEGACY_PLUGIN, .name = nullptr}; xdgPluginState = pluginLoadHelper(info, failedModules_); } else { xdgPluginState = State::Success; } } else { xdgPluginState = State::Success; } return (userPluginState && systemPluginState && xdgPluginState) ? State::Success : State::PartialFailure; } } // namespace OBS