Add Plugins tab to settings for installing, enabling, disabling, and removing plugins
* Rework Plugin Manager so that plugins can be loaded after initialization * Use a callback function to add plugin tabs to the dialog * Install button lets you choose plugin file, copies it to plugins directory, and immediately loads it * Remove button deletes selected plugin file from plugins directory - need to add a means to unload it firstmaster
parent
a28badafb9
commit
a20405a6ef
@ -1,57 +1,134 @@
|
||||
#include "LogManager.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
void PluginManager::ScanAndLoadPlugins(bool dark_theme)
|
||||
PluginManager::PluginManager(bool dark_theme_val)
|
||||
{
|
||||
LOG_INFO("Loading plugins");
|
||||
|
||||
std::string OpenRGBConfigDir = ResourceManager::get()->GetConfigurationDirectory();
|
||||
dark_theme = dark_theme_val;
|
||||
}
|
||||
|
||||
std::string PluginPath = OpenRGBConfigDir + "/Plugins";
|
||||
void PluginManager::RegisterAddPluginTabCallback(AddPluginTabCallback new_callback, void * new_callback_arg)
|
||||
{
|
||||
AddPluginTabCallbacks.push_back(new_callback);
|
||||
AddPluginTabCallbackArgs.push_back(new_callback_arg);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------------*\
|
||||
| I used https://github.com/krf/cmake-qtqml-plugin-example to figure out how to do this |
|
||||
| So BIG credit to krf |
|
||||
\*--------------------------------------------------------------------------------------*/
|
||||
OpenRGBPluginInterface *OpenRGBPlugin = nullptr;
|
||||
void PluginManager::ScanAndLoadPlugins()
|
||||
{
|
||||
LOG_INFO("Loading plugins");
|
||||
|
||||
const QDir pluginsDir = QString().fromStdString(ResourceManager::get()->GetConfigurationDirectory()) + "plugins/";
|
||||
const QDir plugins_dir = QString().fromStdString(ResourceManager::get()->GetConfigurationDirectory()) + "plugins/";
|
||||
|
||||
std::vector<std::string> FileList;
|
||||
|
||||
for(int i = 0; i < QDir(pluginsDir).entryList(QDir::Files).size(); i++)
|
||||
for(int i = 0; i < QDir(plugins_dir).entryList(QDir::Files).size(); i++)
|
||||
{
|
||||
/*--------------------------------------*\
|
||||
| Add all of the Plugin Files to a list |
|
||||
\*--------------------------------------*/
|
||||
FileList.push_back(QDir(pluginsDir).entryList(QDir::Files)[i].toStdString());
|
||||
FileList.push_back(QDir(plugins_dir).entryList(QDir::Files)[i].toStdString());
|
||||
}
|
||||
|
||||
for(const std::string &fileName : FileList)
|
||||
for(const std::string &plugin_name : FileList)
|
||||
{
|
||||
const std::string filePath = pluginsDir.absoluteFilePath(QString().fromStdString(fileName)).toStdString();
|
||||
const std::string plugin_path = plugins_dir.absoluteFilePath(QString().fromStdString(plugin_name)).toStdString();
|
||||
|
||||
LoadPlugin(plugin_path);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::LoadPlugin(std::string path)
|
||||
{
|
||||
OpenRGBPluginInterface *OpenRGBPlugin = nullptr;
|
||||
|
||||
LOG_VERBOSE("Attempting to load: %s", filePath.c_str());
|
||||
LOG_VERBOSE("Attempting to load: %s", path.c_str());
|
||||
|
||||
QPluginLoader loader(pluginsDir.absoluteFilePath(QString().fromStdString(fileName)));
|
||||
QPluginLoader loader(QString().fromStdString(path));
|
||||
|
||||
if (QObject *instance = loader.instance())
|
||||
if (QObject *instance = loader.instance())
|
||||
{
|
||||
if ((OpenRGBPlugin = qobject_cast<OpenRGBPluginInterface*>(instance)))
|
||||
{
|
||||
if ((OpenRGBPlugin = qobject_cast<OpenRGBPluginInterface*>(instance)))
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize the plugin |
|
||||
\*-----------------------------------------------------*/
|
||||
OpenRGBPlugin->info = OpenRGBPlugin->Initialize(dark_theme, ResourceManager::get());
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| 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 device disable list and read in disabled |
|
||||
| device strings |
|
||||
\*-------------------------------------------------*/
|
||||
json plugin_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Plugins");
|
||||
|
||||
if(plugin_settings.contains("plugins"))
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize the plugin |
|
||||
\*-----------------------------------------------------*/
|
||||
OpenRGBPlugin->info = OpenRGBPlugin->Initialize(dark_theme, ResourceManager::get());
|
||||
plugin_ct = plugin_settings["plugins"].size();
|
||||
|
||||
LOG_VERBOSE("Loaded plugin %s", OpenRGBPlugin->info.PluginName.c_str());
|
||||
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"];
|
||||
}
|
||||
|
||||
PluginManager::ActivePlugins.push_back(OpenRGBPlugin);
|
||||
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((OpenRGBPlugin->info.PluginName == name)
|
||||
&&(OpenRGBPlugin->info.PluginDescription == description))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
plugin_settings["plugins"][plugin_ct]["name"] = OpenRGBPlugin->info.PluginName;
|
||||
plugin_settings["plugins"][plugin_ct]["description"] = OpenRGBPlugin->info.PluginDescription;
|
||||
plugin_settings["plugins"][plugin_ct]["enabled"] = enabled;
|
||||
|
||||
ResourceManager::get()->GetSettingsManager()->SetSettings("Plugins", plugin_settings);
|
||||
ResourceManager::get()->GetSettingsManager()->SaveSettings();
|
||||
}
|
||||
|
||||
LOG_VERBOSE("Loaded plugin %s", OpenRGBPlugin->info.PluginName.c_str());
|
||||
|
||||
OpenRGBPluginEntry entry;
|
||||
|
||||
entry.plugin = OpenRGBPlugin;
|
||||
entry.path = path;
|
||||
entry.enabled = enabled;
|
||||
|
||||
PluginManager::ActivePlugins.push_back(entry);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Call the callbacks |
|
||||
\*-------------------------------------------------*/
|
||||
for(unsigned int callback_idx = 0; callback_idx < AddPluginTabCallbacks.size(); callback_idx++)
|
||||
{
|
||||
AddPluginTabCallbacks[callback_idx](AddPluginTabCallbackArgs[callback_idx], OpenRGBPlugin);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << loader.errorString().toStdString() << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << loader.errorString().toStdString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
#include "OpenRGBPluginsEntry.h"
|
||||
#include "ui_OpenRGBPluginsEntry.h"
|
||||
|
||||
using namespace Ui;
|
||||
|
||||
OpenRGBPluginsEntry::OpenRGBPluginsEntry(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OpenRGBPluginsEntryUi)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
OpenRGBPluginsEntry::~OpenRGBPluginsEntry()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
#ifndef OPENRGBPLUGINSENTRY_H
|
||||
#define OPENRGBPLUGINSENTRY_H
|
||||
|
||||
#include "ui_OpenRGBPluginsEntry.h"
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class OpenRGBPluginsEntry;
|
||||
}
|
||||
|
||||
class Ui::OpenRGBPluginsEntry : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenRGBPluginsEntry(QWidget *parent = nullptr);
|
||||
~OpenRGBPluginsEntry();
|
||||
Ui::OpenRGBPluginsEntryUi *ui;
|
||||
};
|
||||
|
||||
#endif // OPENRGBPLUGINSENTRY_H
|
||||
@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenRGBPluginsEntryUi</class>
|
||||
<widget class="QWidget" name="OpenRGBPluginsEntryUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>195</width>
|
||||
<height>109</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="DescriptionValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="EnabledLabel">
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="NameValue">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Name Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="DescriptionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="NameLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="EnabledCheckBox">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="PathLabel">
|
||||
<property name="text">
|
||||
<string>Path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="PathValue">
|
||||
<property name="text">
|
||||
<string>Path Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@ -0,0 +1,132 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "OpenRGBPluginsPage.h"
|
||||
#include "ui_OpenRGBPluginsPage.h"
|
||||
|
||||
Ui::OpenRGBPluginsPage::OpenRGBPluginsPage(PluginManager* plugin_manager_ptr, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OpenRGBPluginsPageUi)
|
||||
{
|
||||
plugin_manager = plugin_manager_ptr;
|
||||
ui->setupUi(this);
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
Ui::OpenRGBPluginsPage::~OpenRGBPluginsPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void Ui::OpenRGBPluginsPage::RefreshList()
|
||||
{
|
||||
ui->PluginsList->clear();
|
||||
entries.clear();
|
||||
|
||||
for(unsigned int plugin_idx = 0; plugin_idx < plugin_manager->ActivePlugins.size(); plugin_idx++)
|
||||
{
|
||||
OpenRGBPluginsEntry* entry = new OpenRGBPluginsEntry();
|
||||
|
||||
entry->ui->NameValue->setText(QString::fromStdString(plugin_manager->ActivePlugins[plugin_idx].plugin->info.PluginName));
|
||||
entry->ui->DescriptionValue->setText(QString::fromStdString(plugin_manager->ActivePlugins[plugin_idx].plugin->info.PluginDescription));
|
||||
entry->ui->PathValue->setText(QString::fromStdString(plugin_manager->ActivePlugins[plugin_idx].path));
|
||||
entry->ui->EnabledCheckBox->setChecked((plugin_manager->ActivePlugins[plugin_idx].enabled));
|
||||
|
||||
//TODO: Get plugin enable/disable working
|
||||
// Until then, hide the enable checkbox
|
||||
entry->ui->EnabledLabel->setVisible(false);
|
||||
entry->ui->EnabledCheckBox->setVisible(false);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
|
||||
item->setSizeHint(entry->sizeHint());
|
||||
|
||||
ui->PluginsList->addItem(item);
|
||||
ui->PluginsList->setItemWidget(item, entry);
|
||||
entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void Ui::OpenRGBPluginsPage::on_InstallPluginButton_clicked()
|
||||
{
|
||||
QString install_file = QFileDialog::getOpenFileName(this, "Install OpenRGB Plugin", "", "DLL Files (*.dll; *.dylib; *.so; *.so.*)");
|
||||
|
||||
std::string from_path = install_file.toStdString();
|
||||
std::string to_path = ResourceManager::get()->GetConfigurationDirectory() + "plugins/";
|
||||
std::string to_file = to_path + filesystem::path(from_path).filename().string();
|
||||
bool match = false;
|
||||
|
||||
for(unsigned int plugin_idx = 0; plugin_idx < plugin_manager->ActivePlugins.size(); plugin_idx++)
|
||||
{
|
||||
if(to_file == plugin_manager->ActivePlugins[plugin_idx].path)
|
||||
{
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(match == true)
|
||||
{
|
||||
QMessageBox::StandardButton reply;
|
||||
|
||||
reply = QMessageBox::question(this, "Replace Plugin", "A plugin with this filename is already installed. Are you sure you want to replace this plugin?", QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if(reply != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
filesystem::copy(from_path, to_path, filesystem::copy_options::update_existing);
|
||||
|
||||
//TODO: Unregister the old plugin and load the new one if matched
|
||||
// For now, don't load the new plugin
|
||||
|
||||
if(match == false)
|
||||
{
|
||||
plugin_manager->LoadPlugin(to_path + "/" + filesystem::path(from_path).filename().string());
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Ui::OpenRGBPluginsPage::on_RemovePluginButton_clicked()
|
||||
{
|
||||
QMessageBox::StandardButton reply;
|
||||
|
||||
reply = QMessageBox::question(this, "Remove Plugin", "Are you sure you want to remove this plugin?", QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if(reply != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int cur_row = ui->PluginsList->currentRow();
|
||||
|
||||
if(cur_row < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem* item = ui->PluginsList->takeItem(cur_row);
|
||||
|
||||
ui->PluginsList->removeItemWidget(item);
|
||||
delete item;
|
||||
|
||||
//TODO: Unregister the plugin from the plugin manager
|
||||
|
||||
filesystem::remove(entries[cur_row]->ui->PathValue->text().toStdString());
|
||||
|
||||
delete entries[cur_row];
|
||||
entries.erase(entries.begin() + cur_row);
|
||||
}
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
#ifndef OPENRGBPLUGINSPAGE_H
|
||||
#define OPENRGBPLUGINSPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "OpenRGBPluginsEntry.h"
|
||||
#include "ui_OpenRGBPluginsPage.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class OpenRGBPluginsPage;
|
||||
}
|
||||
|
||||
class Ui::OpenRGBPluginsPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenRGBPluginsPage(PluginManager* plugin_manager_ptr, QWidget *parent = nullptr);
|
||||
~OpenRGBPluginsPage();
|
||||
|
||||
private slots:
|
||||
void on_InstallPluginButton_clicked();
|
||||
|
||||
void on_RemovePluginButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::OpenRGBPluginsPageUi* ui;
|
||||
PluginManager* plugin_manager;
|
||||
std::vector<OpenRGBPluginsEntry*> entries;
|
||||
|
||||
void RefreshList();
|
||||
};
|
||||
|
||||
#endif // OPENRGBPLUGINSPAGE_H
|
||||
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OpenRGBPluginsPageUi</class>
|
||||
<widget class="QWidget" name="OpenRGBPluginsPageUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="InstallPluginButton">
|
||||
<property name="text">
|
||||
<string>Install Plugin</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="RemovePluginButton">
|
||||
<property name="text">
|
||||
<string>Remove Plugin</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QListWidget" name="PluginsList"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Loading…
Reference in New Issue