Add capability to load and unload plugins

This commit is contained in:
Adam Honse
2021-08-26 00:52:58 -05:00
parent bd2334c186
commit 6bacee59f5
6 changed files with 300 additions and 128 deletions

View File

@@ -12,6 +12,12 @@ void PluginManager::RegisterAddPluginTabCallback(AddPluginTabCallback new_callba
AddPluginTabCallbackArgs.push_back(new_callback_arg);
}
void PluginManager::RegisterRemovePluginTabCallback(RemovePluginTabCallback new_callback, void * new_callback_arg)
{
RemovePluginTabCallbacks.push_back(new_callback);
RemovePluginTabCallbackArgs.push_back(new_callback_arg);
}
void PluginManager::ScanAndLoadPlugins()
{
LOG_INFO("Loading plugins");
@@ -41,118 +47,172 @@ void PluginManager::ScanAndLoadPlugins()
{
const std::string plugin_path = plugins_dir.absoluteFilePath(QString().fromStdString(plugin_name)).toStdString();
LoadPlugin(plugin_path);
AddPlugin(plugin_path);
}
}
void PluginManager::AddPlugin(std::string path)
{
OpenRGBPluginInterface* plugin = nullptr;
unsigned int plugin_idx;
for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++)
{
if(path == ActivePlugins[plugin_idx].path)
{
break;
}
}
/*---------------------------------------------------------------------*\
| If the path does not match an existing entry, create a new entry |
\*---------------------------------------------------------------------*/
if(plugin_idx == ActivePlugins.size())
{
/*-----------------------------------------------------------------*\
| Create a QPluginLoader and load the plugin |
\*-----------------------------------------------------------------*/
QPluginLoader* loader = new QPluginLoader(QString().fromStdString(path));
QObject* instance = loader->instance();
/*-----------------------------------------------------------------*\
| Check that the plugin is valid, then check the API version |
\*-----------------------------------------------------------------*/
if(instance)
{
plugin = qobject_cast<OpenRGBPluginInterface*>(instance);
if(plugin)
{
if(plugin->GetPluginAPIVersion() == OPENRGB_PLUGIN_API_VERSION)
{
/*-----------------------------------------------------*\
| Get the plugin information |
\*-----------------------------------------------------*/
OpenRGBPluginInfo info = plugin->GetPluginInfo();
/*-----------------------------------------------------*\
| Search the settings to see if it is enabled |
\*-----------------------------------------------------*/
std::string name = "";
std::string description = "";
bool enabled = true;
bool found = false;
unsigned int plugin_ct = 0;
/*-----------------------------------------------------*\
| Open plugin list and check if plugin is in the list |
\*-----------------------------------------------------*/
json plugin_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Plugins");
if(plugin_settings.contains("plugins"))
{
plugin_ct = plugin_settings["plugins"].size();
for(unsigned int plugin_idx = 0; plugin_idx < plugin_settings["plugins"].size(); plugin_idx++)
{
if(plugin_settings["plugins"][plugin_idx].contains("name"))
{
name = plugin_settings["plugins"][plugin_idx]["name"];
}
if(plugin_settings["plugins"][plugin_idx].contains("description"))
{
description = plugin_settings["plugins"][plugin_idx]["description"];
}
if(plugin_settings["plugins"][plugin_idx].contains("enabled"))
{
enabled = plugin_settings["plugins"][plugin_idx]["enabled"];
}
if((info.Name == name)
&&(info.Description == description))
{
found = true;
break;
}
}
}
/*-----------------------------------------------------*\
| If the plugin was not in the list, add it to the list |
| and default it to enabled, then save the settings |
\*-----------------------------------------------------*/
if(!found)
{
plugin_settings["plugins"][plugin_ct]["name"] = info.Name;
plugin_settings["plugins"][plugin_ct]["description"] = info.Description;
plugin_settings["plugins"][plugin_ct]["enabled"] = enabled;
ResourceManager::get()->GetSettingsManager()->SetSettings("Plugins", plugin_settings);
ResourceManager::get()->GetSettingsManager()->SaveSettings();
}
LOG_VERBOSE("Loaded plugin %s", info.Name.c_str());
/*-----------------------------------------------------*\
| Add the plugin to the PluginManager active plugins |
\*-----------------------------------------------------*/
OpenRGBPluginEntry entry;
entry.info = info;
entry.plugin = plugin;
entry.loader = loader;
entry.loaded = false;
entry.path = path;
entry.enabled = enabled;
entry.widget = nullptr;
loader->unload();
PluginManager::ActivePlugins.push_back(entry);
if(entry.enabled)
{
LoadPlugin(path);
}
}
}
}
}
}
void PluginManager::LoadPlugin(std::string path)
{
OpenRGBPluginInterface* plugin = nullptr;
unsigned int plugin_idx;
LOG_VERBOSE("Attempting to load: %s", path.c_str());
/*-----------------------------------------------------------------*\
| Create a QPluginLoader and load the plugin |
\*-----------------------------------------------------------------*/
QPluginLoader loader(QString().fromStdString(path));
QObject* instance = loader.instance();
/*-----------------------------------------------------------------*\
| Check that the plugin is valid, then check the API version |
\*-----------------------------------------------------------------*/
if(instance)
for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++)
{
plugin = qobject_cast<OpenRGBPluginInterface*>(instance);
if(plugin)
if(path == ActivePlugins[plugin_idx].path)
{
if(plugin->GetPluginAPIVersion() == OPENRGB_PLUGIN_API_VERSION)
break;
}
}
if(plugin_idx == ActivePlugins.size())
{
return;
}
if(!ActivePlugins[plugin_idx].loaded)
{
ActivePlugins[plugin_idx].loader->load();
ActivePlugins[plugin_idx].loaded = true;
QObject* instance = ActivePlugins[plugin_idx].loader->instance();
if(instance)
{
OpenRGBPluginInterface* plugin = qobject_cast<OpenRGBPluginInterface*>(instance);
if(plugin)
{
/*-----------------------------------------------------*\
| Get the plugin information |
\*-----------------------------------------------------*/
OpenRGBPluginInfo info = plugin->GetPluginInfo();
/*-----------------------------------------------------*\
| Search the settings to see if it is enabled |
\*-----------------------------------------------------*/
std::string name = "";
std::string description = "";
bool enabled = true;
bool found = false;
unsigned int plugin_ct = 0;
/*-----------------------------------------------------*\
| Open plugin list and check if plugin is in the list |
\*-----------------------------------------------------*/
json plugin_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Plugins");
if(plugin_settings.contains("plugins"))
if(plugin->GetPluginAPIVersion() == OPENRGB_PLUGIN_API_VERSION)
{
plugin_ct = plugin_settings["plugins"].size();
ActivePlugins[plugin_idx].plugin = plugin;
for(unsigned int plugin_idx = 0; plugin_idx < plugin_settings["plugins"].size(); plugin_idx++)
{
if(plugin_settings["plugins"][plugin_idx].contains("name"))
{
name = plugin_settings["plugins"][plugin_idx]["name"];
}
if(plugin_settings["plugins"][plugin_idx].contains("description"))
{
description = plugin_settings["plugins"][plugin_idx]["description"];
}
if(plugin_settings["plugins"][plugin_idx].contains("enabled"))
{
enabled = plugin_settings["plugins"][plugin_idx]["enabled"];
}
if((info.Name == name)
&&(info.Description == description))
{
found = true;
break;
}
}
}
/*-----------------------------------------------------*\
| If the plugin was not in the list, add it to the list |
| and default it to enabled, then save the settings |
\*-----------------------------------------------------*/
if(!found)
{
plugin_settings["plugins"][plugin_ct]["name"] = info.Name;
plugin_settings["plugins"][plugin_ct]["description"] = info.Description;
plugin_settings["plugins"][plugin_ct]["enabled"] = enabled;
ResourceManager::get()->GetSettingsManager()->SetSettings("Plugins", plugin_settings);
ResourceManager::get()->GetSettingsManager()->SaveSettings();
}
LOG_VERBOSE("Loaded plugin %s", info.Name.c_str());
/*-----------------------------------------------------*\
| Add the plugin to the PluginManager active plugins |
\*-----------------------------------------------------*/
OpenRGBPluginEntry entry;
entry.info = info;
entry.plugin = plugin;
entry.path = path;
entry.enabled = enabled;
PluginManager::ActivePlugins.push_back(entry);
/*-----------------------------------------------------*\
| If the plugin is enabled, load it |
\*-----------------------------------------------------*/
if(enabled)
{
/*-------------------------------------------------*\
| Initialize the plugin |
\*-------------------------------------------------*/
plugin->Initialize(dark_theme, ResourceManager::get());
/*-------------------------------------------------*\
@@ -160,19 +220,47 @@ void PluginManager::LoadPlugin(std::string path)
\*-------------------------------------------------*/
for(unsigned int callback_idx = 0; callback_idx < AddPluginTabCallbacks.size(); callback_idx++)
{
AddPluginTabCallbacks[callback_idx](AddPluginTabCallbackArgs[callback_idx], entry);
AddPluginTabCallbacks[callback_idx](AddPluginTabCallbackArgs[callback_idx], &ActivePlugins[plugin_idx]);
}
}
else
{
delete instance;
loader.unload();
}
}
}
}
else
}
void PluginManager::UnloadPlugin(std::string path)
{
unsigned int plugin_idx;
for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++)
{
std::cout << loader.errorString().toStdString() << std::endl;
if(path == ActivePlugins[plugin_idx].path)
{
break;
}
}
if(plugin_idx == ActivePlugins.size())
{
return;
}
if(ActivePlugins[plugin_idx].loaded)
{
/*-------------------------------------------------*\
| Call plugin's Unload function before GUI removal |
\*-------------------------------------------------*/
ActivePlugins[plugin_idx].plugin->Unload();
/*-------------------------------------------------*\
| Call the callbacks |
\*-------------------------------------------------*/
for(unsigned int callback_idx = 0; callback_idx < RemovePluginTabCallbacks.size(); callback_idx++)
{
RemovePluginTabCallbacks[callback_idx](RemovePluginTabCallbackArgs[callback_idx], &ActivePlugins[plugin_idx]);
}
ActivePlugins[plugin_idx].loader->unload();
ActivePlugins[plugin_idx].loaded = false;
}
}

View File

@@ -15,11 +15,15 @@ typedef struct
{
OpenRGBPluginInfo info;
OpenRGBPluginInterface* plugin;
QPluginLoader* loader;
bool loaded;
QWidget* widget;
std::string path;
bool enabled;
} OpenRGBPluginEntry;
typedef void (*AddPluginTabCallback)(void *, OpenRGBPluginEntry plugin);
typedef void (*AddPluginTabCallback)(void *, OpenRGBPluginEntry* plugin);
typedef void (*RemovePluginTabCallback)(void *, OpenRGBPluginEntry* plugin);
class PluginManager
{
@@ -27,14 +31,22 @@ public:
PluginManager(bool dark_theme);
void RegisterAddPluginTabCallback(AddPluginTabCallback new_callback, void * new_callback_arg);
void RegisterRemovePluginTabCallback(RemovePluginTabCallback new_callback, void * new_callback_arg);
void ScanAndLoadPlugins();
void AddPlugin(std::string path);
void LoadPlugin(std::string path);
void UnloadPlugin(std::string path);
std::vector<OpenRGBPluginEntry> ActivePlugins;
private:
bool dark_theme;
std::vector<AddPluginTabCallback> AddPluginTabCallbacks;
std::vector<void *> AddPluginTabCallbackArgs;
std::vector<AddPluginTabCallback> AddPluginTabCallbacks;
std::vector<void *> AddPluginTabCallbackArgs;
std::vector<RemovePluginTabCallback> RemovePluginTabCallbacks;
std::vector<void *> RemovePluginTabCallbackArgs;
};

View File

@@ -96,13 +96,20 @@ static void UpdateDetectionProgressCallback(void * this_ptr)
QMetaObject::invokeMethod(this_obj, "onDetectionProgressUpdated", Qt::QueuedConnection);
}
static void CreatePluginTabCallback(void * this_ptr, OpenRGBPluginEntry plugin)
static void CreatePluginTabCallback(void * this_ptr, OpenRGBPluginEntry* plugin)
{
OpenRGBDialog2 * this_obj = (OpenRGBDialog2 *)this_ptr;
this_obj->AddPluginTab(plugin);
}
static void DeletePluginTabCallback(void * this_ptr, OpenRGBPluginEntry* plugin)
{
OpenRGBDialog2 * this_obj = (OpenRGBDialog2 *)this_ptr;
this_obj->RemovePluginTab(plugin);
}
bool OpenRGBDialog2::IsDarkTheme()
{
#ifdef _WIN32
@@ -414,6 +421,7 @@ OpenRGBDialog2::OpenRGBDialog2(QWidget *parent) : QMainWindow(parent), ui(new Op
\*-----------------------------------------------------*/
plugin_manager = new PluginManager(IsDarkTheme());
plugin_manager->RegisterAddPluginTabCallback(&CreatePluginTabCallback, this);
plugin_manager->RegisterRemovePluginTabCallback(&DeletePluginTabCallback, this);
plugin_manager->ScanAndLoadPlugins();
/*-----------------------------------------------------*\
@@ -690,7 +698,7 @@ void OpenRGBDialog2::AddSerialSettingsPage()
ui->SettingsTabBar->tabBar()->setTabButton(ui->SettingsTabBar->tabBar()->count() - 1, QTabBar::LeftSide, SettingsTabLabel);
}
void OpenRGBDialog2::AddPluginTab(OpenRGBPluginEntry plugin)
void OpenRGBDialog2::AddPluginTab(OpenRGBPluginEntry* plugin)
{
/*-----------------------------------------------------*\
| Create Label for the Tab |
@@ -715,14 +723,16 @@ void OpenRGBDialog2::AddPluginTab(OpenRGBPluginEntry plugin)
/*-----------------------------------------------------*\
| Create the tab label |
\*-----------------------------------------------------*/
PluginTabLabel = (QLabel*)new TabLabel(PluginLabelString, QString::fromStdString(plugin.info.Label));
PluginTabLabel = (QLabel*)new TabLabel(PluginLabelString, QString::fromStdString(plugin->info.Label));
/*-----------------------------------------------------*\
| InformationTab - Place plugin in the Information tab |
\*-----------------------------------------------------*/
if(plugin.info.Location == "InformationTab")
if(plugin->info.Location == "InformationTab")
{
QWidget* NewPluginTab = plugin.plugin->CreateGUI(this);
QWidget* NewPluginTab = plugin->plugin->CreateGUI(this);
plugin->widget = NewPluginTab;
OpenRGBPluginContainer* NewPluginContainer = new OpenRGBPluginContainer(NewPluginTab);
@@ -733,9 +743,11 @@ void OpenRGBDialog2::AddPluginTab(OpenRGBPluginEntry plugin)
/*-----------------------------------------------------*\
| DevicesTab - Place plugin in the Devices tab |
\*-----------------------------------------------------*/
else if(plugin.info.Location == "DevicesTab")
else if(plugin->info.Location == "DevicesTab")
{
QWidget* NewPluginTab = plugin.plugin->CreateGUI(this);
QWidget* NewPluginTab = plugin->plugin->CreateGUI(this);
plugin->widget = NewPluginTab;
OpenRGBPluginContainer* NewPluginContainer = new OpenRGBPluginContainer(NewPluginTab);
@@ -746,20 +758,24 @@ void OpenRGBDialog2::AddPluginTab(OpenRGBPluginEntry plugin)
/*-----------------------------------------------------*\
| TopTabBar - Place plugin as its own top level tab |
\*-----------------------------------------------------*/
else if(plugin.info.Location == "TopTabBar")
else if(plugin->info.Location == "TopTabBar")
{
QWidget* NewPluginTab = plugin.plugin->CreateGUI(this);
QWidget* NewPluginTab = plugin->plugin->CreateGUI(this);
plugin->widget = NewPluginTab;
OpenRGBPluginContainer* NewPluginContainer = new OpenRGBPluginContainer(NewPluginTab);
ui->MainTabBar->addTab(NewPluginContainer,QString().fromStdString(plugin.info.Label));
ui->MainTabBar->addTab(NewPluginContainer,QString().fromStdString(plugin->info.Label));
}
/*-----------------------------------------------------*\
| SettingsTabBar - Place plugin in the Settings tab |
\*-----------------------------------------------------*/
else if(plugin.info.Location == "SettingsTabBar")
else if(plugin->info.Location == "SettingsTabBar")
{
QWidget* NewPluginTab = plugin.plugin->CreateGUI(this);
QWidget* NewPluginTab = plugin->plugin->CreateGUI(this);
plugin->widget = NewPluginTab;
OpenRGBPluginContainer* NewPluginContainer = new OpenRGBPluginContainer(NewPluginTab);
@@ -773,7 +789,51 @@ void OpenRGBDialog2::AddPluginTab(OpenRGBPluginEntry plugin)
\*-----------------------------------------------------*/
else
{
std::cout << ("Cannot load plugin '" + plugin.info.Name + "' as it does not specify a valid location: " + plugin.info.Location + "\n");
std::cout << ("Cannot load plugin '" + plugin->info.Name + "' as it does not specify a valid location: " + plugin->info.Location + "\n");
}
}
void OpenRGBDialog2::RemovePluginTab(OpenRGBPluginEntry* plugin)
{
/*-----------------------------------------------------*\
| InformationTab - Place plugin in the Information tab |
\*-----------------------------------------------------*/
if(plugin->info.Location == "InformationTab")
{
}
/*-----------------------------------------------------*\
| DevicesTab - Place plugin in the Devices tab |
\*-----------------------------------------------------*/
else if(plugin->info.Location == "DevicesTab")
{
}
/*-----------------------------------------------------*\
| TopTabBar - Place plugin as its own top level tab |
\*-----------------------------------------------------*/
else if(plugin->info.Location == "TopTabBar")
{
for(int tab_idx = 0; tab_idx < ui->MainTabBar->count(); tab_idx++)
{
if(dynamic_cast<OpenRGBPluginContainer*>(ui->MainTabBar->widget(tab_idx)) != nullptr)
{
std::cout << "found a plugin tab" << std::endl;
if(dynamic_cast<OpenRGBPluginContainer*>(ui->MainTabBar->widget(tab_idx))->plugin_widget == plugin->widget)
{
std::cout << "found correct plugin tab" << std::endl;
delete ui->MainTabBar->widget(tab_idx);
ui->MainTabBar->removeTab(tab_idx);
}
}
}
}
/*-----------------------------------------------------*\
| SettingsTabBar - Place plugin in the Settings tab |
\*-----------------------------------------------------*/
else if(plugin->info.Location == "SettingsTabBar")
{
}
}

View File

@@ -45,7 +45,8 @@ public:
void AddI2CToolsPage();
void AddServerTab();
void AddPluginTab(OpenRGBPluginEntry plugin);
void AddPluginTab(OpenRGBPluginEntry* plugin);
void RemovePluginTab(OpenRGBPluginEntry* plugin);
void setMode(unsigned char mode_val);

View File

@@ -20,9 +20,10 @@ public:
void Hide();
void Show();
QWidget* plugin_widget;
private:
Ui::OpenRGBPluginContainerUi *ui;
QWidget* plugin_widget;
};
#endif // OPENRGBPLUGINCONTAINER_H

View File

@@ -120,7 +120,7 @@ void Ui::OpenRGBPluginsPage::on_InstallPluginButton_clicked()
if(match == false)
{
plugin_manager->LoadPlugin(to_path + "/" + filesystem::path(from_path).filename().string());
plugin_manager->AddPlugin(to_path + "/" + filesystem::path(from_path).filename().string());
RefreshList();
}
@@ -181,6 +181,7 @@ void Ui::OpenRGBPluginsPage::on_EnableButton_clicked(OpenRGBPluginsEntry* entry)
std::string entry_name = entry->ui->NameValue->text().toStdString();
std::string entry_desc = entry->ui->DescriptionValue->text().toStdString();
std::string entry_path = entry->ui->PathValue->text().toStdString();
if(entry->ui->EnabledCheckBox->isChecked())
{
@@ -235,4 +236,13 @@ void Ui::OpenRGBPluginsPage::on_EnableButton_clicked(OpenRGBPluginsEntry* entry)
ResourceManager::get()->GetSettingsManager()->SetSettings("Plugins", plugin_settings);
ResourceManager::get()->GetSettingsManager()->SaveSettings();
}
if(enabled)
{
plugin_manager->LoadPlugin(entry_path);
}
else
{
plugin_manager->UnloadPlugin(entry_path);
}
}