overte/libraries/plugins/src/plugins/PluginManager.cpp
2019-01-23 21:02:52 -08:00

350 lines
12 KiB
C++

//
// Created by Bradley Austin Davis on 2015/08/08
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PluginManager.h"
#include <mutex>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QPluginLoader>
//#define HIFI_PLUGINMANAGER_DEBUG
#if defined(HIFI_PLUGINMANAGER_DEBUG)
#include <QJsonDocument>
#endif
#include <DependencyManager.h>
#include <UserActivityLogger.h>
#include "RuntimePlugin.h"
#include "CodecPlugin.h"
#include "DisplayPlugin.h"
#include "InputPlugin.h"
#include "PluginLogging.h"
void PluginManager::setDisplayPluginProvider(const DisplayPluginProvider& provider) {
_displayPluginProvider = provider;
}
void PluginManager::setInputPluginProvider(const InputPluginProvider& provider) {
_inputPluginProvider = provider;
}
void PluginManager::setCodecPluginProvider(const CodecPluginProvider& provider) {
_codecPluginProvider = provider;
}
void PluginManager::setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister) {
_inputSettingsPersister = persister;
}
PluginManagerPointer PluginManager::getInstance() {
return DependencyManager::get<PluginManager>();
}
QString getPluginNameFromMetaData(const QJsonObject& object) {
static const char* METADATA_KEY = "MetaData";
static const char* NAME_KEY = "name";
return object[METADATA_KEY][NAME_KEY].toString("");
}
QString getPluginIIDFromMetaData(const QJsonObject& object) {
static const char* IID_KEY = "IID";
return object[IID_KEY].toString("");
}
int getPluginInterfaceVersionFromMetaData(const QJsonObject& object) {
static const QString METADATA_KEY = "MetaData";
static const QString NAME_KEY = "version";
return object[METADATA_KEY][NAME_KEY].toInt(0);
}
QStringList preferredDisplayPlugins;
QStringList disabledDisplays;
QStringList disabledInputs;
bool isDisabled(QJsonObject metaData) {
auto name = getPluginNameFromMetaData(metaData);
auto iid = getPluginIIDFromMetaData(metaData);
if (iid == DisplayProvider_iid) {
return disabledDisplays.contains(name);
} else if (iid == InputProvider_iid) {
return disabledInputs.contains(name);
}
return false;
}
auto PluginManager::getLoadedPlugins() const -> const LoaderList& {
static std::once_flag once;
static LoaderList loadedPlugins;
std::call_once(once, [&] {
#if defined(Q_OS_ANDROID)
QString pluginPath = QCoreApplication::applicationDirPath() + "/";
#elif defined(Q_OS_MAC)
QString pluginPath = QCoreApplication::applicationDirPath() + "/../PlugIns/";
#else
QString pluginPath = QCoreApplication::applicationDirPath() + "/plugins/";
#endif
QDir pluginDir(pluginPath);
pluginDir.setSorting(QDir::Name);
pluginDir.setFilter(QDir::Files);
if (pluginDir.exists()) {
qInfo() << "Loading runtime plugins from " << pluginPath;
#if defined(Q_OS_ANDROID)
// Can be a better filter and those libs may have a better name to destinguish them from qt plugins
pluginDir.setNameFilters(QStringList() << "libplugins_lib*.so");
#endif
auto candidates = pluginDir.entryList();
for (auto plugin : candidates) {
qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin);
QSharedPointer<QPluginLoader> loader(new QPluginLoader(pluginPath + plugin));
const QJsonObject pluginMetaData = loader->metaData();
#if defined(HIFI_PLUGINMANAGER_DEBUG)
QJsonDocument metaDataDoc(pluginMetaData);
qCInfo(plugins) << "Metadata for " << qPrintable(plugin) << ": " << QString(metaDataDoc.toJson());
#endif
if (isDisabled(pluginMetaData)) {
qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "is disabled";
// Skip this one, it's disabled
continue;
}
if (!_pluginFilter(pluginMetaData)) {
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "doesn't pass provided filter";
continue;
}
if (getPluginInterfaceVersionFromMetaData(pluginMetaData) != HIFI_PLUGIN_INTERFACE_VERSION) {
qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "interface version doesn't match, not loading:"
<< getPluginInterfaceVersionFromMetaData(pluginMetaData)
<< "doesn't match" << HIFI_PLUGIN_INTERFACE_VERSION;
continue;
}
if (loader->load()) {
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "loaded successfully";
loadedPlugins.push_back(loader);
} else {
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "failed to load:";
qCDebug(plugins) << " " << qPrintable(loader->errorString());
}
}
}
});
return loadedPlugins;
}
const CodecPluginList& PluginManager::getCodecPlugins() {
static CodecPluginList codecPlugins;
static std::once_flag once;
std::call_once(once, [&] {
codecPlugins = _codecPluginProvider();
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
CodecProvider* codecProvider = qobject_cast<CodecProvider*>(loader->instance());
if (codecProvider) {
for (auto codecPlugin : codecProvider->getCodecPlugins()) {
if (codecPlugin->isSupported()) {
codecPlugins.push_back(codecPlugin);
}
}
}
}
for (auto plugin : codecPlugins) {
plugin->setContainer(_container);
plugin->init();
qCDebug(plugins) << "init codec:" << plugin->getName();
}
});
return codecPlugins;
}
const SteamClientPluginPointer PluginManager::getSteamClientPlugin() {
static SteamClientPluginPointer steamClientPlugin;
static std::once_flag once;
std::call_once(once, [&] {
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
SteamClientProvider* steamClientProvider = qobject_cast<SteamClientProvider*>(loader->instance());
if (steamClientProvider) {
steamClientPlugin = steamClientProvider->getSteamClientPlugin();
break;
}
}
});
return steamClientPlugin;
}
const DisplayPluginList& PluginManager::getDisplayPlugins() {
static std::once_flag once;
static auto deviceAddedCallback = [](QString deviceName) {
qCDebug(plugins) << "Added device: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("display", deviceName);
};
static auto subdeviceAddedCallback = [](QString pluginName, QString deviceName) {
qCDebug(plugins) << "Added subdevice: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("display", pluginName + " | " + deviceName);
};
std::call_once(once, [&] {
// Grab the built in plugins
_displayPlugins = _displayPluginProvider();
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
DisplayProvider* displayProvider = qobject_cast<DisplayProvider*>(loader->instance());
if (displayProvider) {
for (auto displayPlugin : displayProvider->getDisplayPlugins()) {
_displayPlugins.push_back(displayPlugin);
}
}
}
for (auto plugin : _displayPlugins) {
connect(plugin.get(), &Plugin::deviceConnected, this, deviceAddedCallback, Qt::QueuedConnection);
connect(plugin.get(), &Plugin::subdeviceConnected, this, subdeviceAddedCallback, Qt::QueuedConnection);
plugin->setContainer(_container);
plugin->init();
}
});
return _displayPlugins;
}
void PluginManager::disableDisplayPlugin(const QString& name) {
auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){
return plugin->getName() == name;
});
_displayPlugins.erase(it, _displayPlugins.end());
}
const InputPluginList& PluginManager::getInputPlugins() {
static std::once_flag once;
static auto deviceAddedCallback = [&](QString deviceName) {
qCDebug(plugins) << "Added device: " << deviceName;
QStringList runningDevices = getRunningInputDeviceNames();
bool isDeviceRunning = runningDevices.indexOf(deviceName) >= 0;
emit inputDeviceRunningChanged(deviceName, isDeviceRunning, runningDevices);
UserActivityLogger::getInstance().connectedDevice("input", deviceName);
};
static auto subdeviceAddedCallback = [](QString pluginName, QString deviceName) {
qCDebug(plugins) << "Added subdevice: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("input", pluginName + " | " + deviceName);
};
std::call_once(once, [&] {
_inputPlugins = _inputPluginProvider();
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
InputProvider* inputProvider = qobject_cast<InputProvider*>(loader->instance());
if (inputProvider) {
for (auto inputPlugin : inputProvider->getInputPlugins()) {
if (inputPlugin->isSupported()) {
_inputPlugins.push_back(inputPlugin);
}
}
}
}
for (auto plugin : _inputPlugins) {
connect(plugin.get(), &Plugin::deviceConnected, this, deviceAddedCallback, Qt::QueuedConnection);
connect(plugin.get(), &Plugin::subdeviceConnected, this, subdeviceAddedCallback, Qt::QueuedConnection);
connect(plugin.get(), &Plugin::deviceStatusChanged, this, [&](const QString& deviceName, bool isRunning) {
emit inputDeviceRunningChanged(deviceName, isRunning, getRunningInputDeviceNames());
}, Qt::QueuedConnection);
plugin->setContainer(_container);
plugin->init();
}
});
return _inputPlugins;
}
QStringList PluginManager::getRunningInputDeviceNames() const {
QStringList runningDevices;
for (auto plugin: _inputPlugins) {
if (plugin->isRunning()) {
runningDevices << plugin->getName();
}
}
return runningDevices;
}
void PluginManager::setPreferredDisplayPlugins(const QStringList& displays) {
preferredDisplayPlugins = displays;
}
DisplayPluginList PluginManager::getPreferredDisplayPlugins() {
static DisplayPluginList displayPlugins;
static std::once_flag once;
std::call_once(once, [&] {
// Grab the built in plugins
auto plugins = getDisplayPlugins();
for (auto pluginName : preferredDisplayPlugins) {
auto it = std::find_if(plugins.begin(), plugins.end(), [&](DisplayPluginPointer plugin) {
return plugin->getName() == pluginName;
});
if (it != plugins.end()) {
displayPlugins.push_back(*it);
}
}
});
return displayPlugins;
}
void PluginManager::disableDisplays(const QStringList& displays) {
disabledDisplays << displays;
}
void PluginManager::disableInputs(const QStringList& inputs) {
disabledInputs << inputs;
}
void PluginManager::saveSettings() {
_inputSettingsPersister(getInputPlugins());
}
void PluginManager::shutdown() {
for (auto inputPlugin : getInputPlugins()) {
if (inputPlugin->isActive()) {
inputPlugin->deactivate();
}
}
for (auto displayPlugins : getDisplayPlugins()) {
if (displayPlugins->isActive()) {
displayPlugins->deactivate();
}
}
auto loadedPlugins = getLoadedPlugins();
// Now grab the dynamic plugins
for (auto loader : getLoadedPlugins()) {
InputProvider* inputProvider = qobject_cast<InputProvider*>(loader->instance());
if (inputProvider) {
inputProvider->destroyInputPlugins();
}
DisplayProvider* displayProvider = qobject_cast<DisplayProvider*>(loader->instance());
if (displayProvider) {
displayProvider->destroyDisplayPlugins();
}
}
}