mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-10-14 02:24:58 +08:00
With this change module loading in the application is managed by the plugin manager and allows the application to explicitly load core modules independantly from 3rd party (plugin) modules. The implementation is split into separate files for different platforms to allow for cleaner code handling the different possible locations and path schemes used, including all possible legacy locations and plugin bundle formats. This change introduces new locations and directory formats for plugins: * WINDOWS: Portable plugins need to be placed in a directory called "plugins" adjacent to the "bin" directory of an OBS Studio installation. * WINDOWS: Plugins need to be packaged into their own directory "bundles", with the associated DLL in the root of the bundle and the associated "data" directory next to it. * WINDOWS: The "obs-plugins" directory adjacent to the "bin" directory is deprecated and considered a legacy location for all plugins. * MACOS: No changes in locations or formats. * LINUX: Plugins distributed via system package managers now need to use the "obs-modules/plugins" directory in the system library directory for binaries, and the "obs/obs-modules/plugins" directory in the system data directory for resources. * LINUX: The "obs-plugins" directories in the system library and system data directories are deprecated. * LINUX: Plugins distributed outside of system packages need to use the same directory format as plugins for Windows (see above). * LINUX: A "portable" plugin location like on Windows is not supported. * LINUX: Proper XDG directory locations are scanned for plugins, using XDG_CONFIG_HOME for the legacy plugin format and XDG_DATA_HOME for the new plugin structure. * LINUX: Changes Flatpak plugin loading to use the new plugin structure (while still supporting the legacy structure), and moves Flatpak specific plugin loading to its own source files. * ALL PLATFORMS: A single environment variable "OBS_PLUGINS_PATH" can now be used to provide a highest-priority location from which to load plugins. This directory needs to contain plugin bundles per the new format. The one exception is Flatpak which does not support loading plugins from an env provided location. * ALL PLATFORMS: A custom legacy plugin locations can still be provided via the "OBS_LEGACY_PLUGINS_PATH" and "OBS_LEGACY_PLUGINS_DATA_PATH" environment variables. These are only compatible with plugins using the legacy format and are considered deprecated. The one exception is Flatpak which does not support loading plugins from an env provided location. Co-authored-by: PatTheMav <PatTheMav@users.noreply.github.com>
225 lines
7.8 KiB
C++
225 lines
7.8 KiB
C++
/******************************************************************************
|
|
Copyright (C) 2026 by FiniteSingularity <finitesingularityttv@gmail.com>
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
#include "PluginModuleLoader.hpp"
|
|
#include "PluginManager.hpp"
|
|
#include <LoaderPaths_Linux.hpp>
|
|
|
|
#include <obs.h>
|
|
#include <util/dstr.h>
|
|
#include <util/platform.h>
|
|
|
|
#include <array>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
// Linux Third-Party Plugin Locations
|
|
//
|
|
// * User Path: <Environment Variable>
|
|
// * Binary: <Environment Variable>/<Plugin>/<Plugin>.so
|
|
// * Data: <Environment Variable>/<Plugin>/data
|
|
//
|
|
// * System Root Path: /usr
|
|
// * Binary: <System Root Path>/<System Library>/obs-modules/plugins/<Plugin>.so
|
|
// * Data: <System Root Path>/share/obs/obs-modules/plugins/<Plugin>
|
|
//
|
|
// * User Root Path: $XDG_DATA_HOME
|
|
// * Binary: <User Root Path>/obs-studio/plugins/<Plugin>/<Plugin>.so
|
|
// * Data: <User Root Path>/obs-studio/plugins/<Plugin>/data
|
|
//
|
|
// * Legacy User Path: <Environment Variable> + <Environment Data Variable>
|
|
// * Binary: <Environment Variable>/<Plugin>.so
|
|
// * Data: <Environment Data Variable>/<Plugin>
|
|
//
|
|
// * Legacy System Root Path: /usr
|
|
// * Binary: <Legacy System Root Path>/<System Library>/obs-plugins/<Plugin>.so
|
|
// * Data: <Legacy System Root Path>/share/obs/obs-plugins/<Plugin>
|
|
//
|
|
// * Legacy User Root Path: $XDG_CONFIG_HOME
|
|
// * Binary: <Legacy User Root Path>/obs-studio/plugins/<Plugin>/bin/64bit/<Plugin>.so
|
|
// * Data: <Legacy User Root Path>/obs-studio/plugins/<Plugin>/data
|
|
//
|
|
// * Legacy Fallback Path: <OBS Binary Location>
|
|
// * Binary: <Legacy Fallback Path>/../../obs-plugins/64bit/<Plugin>.so
|
|
// * Data: <Legacy System Root Path>/share/obs/obs-plugins/<Plugin>
|
|
|
|
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
|