From 6bacee59f5c91b2a7ef9c631172dbcf6a708e524 Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Thu, 26 Aug 2021 00:52:58 -0500 Subject: [PATCH] Add capability to load and unload plugins --- PluginManager.cpp | 306 ++++++++++++------- PluginManager.h | 18 +- qt/OpenRGBDialog2.cpp | 86 +++++- qt/OpenRGBDialog2.h | 3 +- qt/OpenRGBPluginContainer.h | 3 +- qt/OpenRGBPluginsPage/OpenRGBPluginsPage.cpp | 12 +- 6 files changed, 300 insertions(+), 128 deletions(-) diff --git a/PluginManager.cpp b/PluginManager.cpp index b8a4c9e7..c5244915 100644 --- a/PluginManager.cpp +++ b/PluginManager.cpp @@ -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(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(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(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; } } diff --git a/PluginManager.h b/PluginManager.h index 239f7976..4388db8f 100644 --- a/PluginManager.h +++ b/PluginManager.h @@ -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 ActivePlugins; private: bool dark_theme; - std::vector AddPluginTabCallbacks; - std::vector AddPluginTabCallbackArgs; + std::vector AddPluginTabCallbacks; + std::vector AddPluginTabCallbackArgs; + + std::vector RemovePluginTabCallbacks; + std::vector RemovePluginTabCallbackArgs; }; diff --git a/qt/OpenRGBDialog2.cpp b/qt/OpenRGBDialog2.cpp index 5745a26d..b30775c6 100644 --- a/qt/OpenRGBDialog2.cpp +++ b/qt/OpenRGBDialog2.cpp @@ -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(ui->MainTabBar->widget(tab_idx)) != nullptr) + { + std::cout << "found a plugin tab" << std::endl; + if(dynamic_cast(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") + { + } } diff --git a/qt/OpenRGBDialog2.h b/qt/OpenRGBDialog2.h index 6880434f..1e2c1fd8 100644 --- a/qt/OpenRGBDialog2.h +++ b/qt/OpenRGBDialog2.h @@ -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); diff --git a/qt/OpenRGBPluginContainer.h b/qt/OpenRGBPluginContainer.h index fe0b493a..6cde4072 100644 --- a/qt/OpenRGBPluginContainer.h +++ b/qt/OpenRGBPluginContainer.h @@ -20,9 +20,10 @@ public: void Hide(); void Show(); + QWidget* plugin_widget; + private: Ui::OpenRGBPluginContainerUi *ui; - QWidget* plugin_widget; }; #endif // OPENRGBPLUGINCONTAINER_H diff --git a/qt/OpenRGBPluginsPage/OpenRGBPluginsPage.cpp b/qt/OpenRGBPluginsPage/OpenRGBPluginsPage.cpp index d06b967f..f381a954 100644 --- a/qt/OpenRGBPluginsPage/OpenRGBPluginsPage.cpp +++ b/qt/OpenRGBPluginsPage/OpenRGBPluginsPage.cpp @@ -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); + } }