feat: Allow plugins to be dynamically turned on and off

This commit is contained in:
WerWolv
2025-08-05 22:16:39 +02:00
parent 95465e2fc3
commit 5b06702dee
6 changed files with 81 additions and 3 deletions

View File

@@ -46,6 +46,7 @@ EXPORT_MODULE namespace hex {
using SetImGuiContextFunc = void (*)(ImGuiContext *);
using GetSubCommandsFunc = void* (*)();
using GetFeaturesFunc = void* (*)();
using IsBuiltinPluginFunc = bool (*)();
InitializePluginFunc initializePluginFunction = nullptr;
InitializeLibraryFunc initializeLibraryFunction = nullptr;
@@ -58,6 +59,7 @@ EXPORT_MODULE namespace hex {
SetImGuiContextFunc setImGuiContextLibraryFunction = nullptr;
GetSubCommandsFunc getSubCommandsFunction = nullptr;
GetFeaturesFunc getFeaturesFunction = nullptr;
IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr;
};
class Plugin {
@@ -84,6 +86,7 @@ EXPORT_MODULE namespace hex {
[[nodiscard]] bool isLoaded() const;
[[nodiscard]] bool isValid() const;
[[nodiscard]] bool isInitialized() const;
[[nodiscard]] bool isBuiltinPlugin() const;
[[nodiscard]] std::span<SubCommand> getSubCommands() const;
[[nodiscard]] std::span<Feature> getFeatures() const;
@@ -92,12 +95,15 @@ EXPORT_MODULE namespace hex {
[[nodiscard]] bool wasAddedManually() const;
void setEnabled(bool enabled);
private:
uintptr_t m_handle = 0;
std::fs::path m_path;
mutable bool m_initialized = false;
bool m_addedManually = false;
bool m_enabled = true;
PluginFunctions m_functions = {};
@@ -133,6 +139,8 @@ EXPORT_MODULE namespace hex {
static bool isPluginLoaded(const std::fs::path &path);
static void setPluginEnabled(const Plugin &plugin, bool enabled);
private:
static std::list<Plugin>& getPluginsMutable();

View File

@@ -80,6 +80,10 @@ void* PluginSubCommandsFunctionHelper<T>::getSubCommands() {
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
#define IMHEX_LIBRARY_SETUP(name) IMHEX_LIBRARY_SETUP_IMPL(name)
#define IMHEX_PLUGIN_SETUP_BUILTIN(name, author, description) \
IMHEX_PLUGIN_VISIBILITY_PREFIX bool isBuiltinPlugin() { return true; } \
IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
#define IMHEX_LIBRARY_SETUP_IMPL(name) \
IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)(); \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME)() { return name; } \

View File

@@ -81,6 +81,7 @@ namespace hex {
m_functions.setImGuiContextLibraryFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>(hex::format("setImGuiContext_{}", fileName));
m_functions.getSubCommandsFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getSubCommands");
m_functions.getFeaturesFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getFeatures");
m_functions.isBuiltinPluginFunction = getPluginFunction<PluginFunctions::IsBuiltinPluginFunc>("isBuiltinPlugin");
}
Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) :
@@ -96,6 +97,9 @@ namespace hex {
m_functions = other.m_functions;
other.m_functions = {};
m_enabled = other.m_enabled;
m_initialized = other.m_initialized;
}
Plugin& Plugin::operator=(Plugin &&other) noexcept {
@@ -108,6 +112,9 @@ namespace hex {
m_functions = other.m_functions;
other.m_functions = {};
m_enabled = other.m_enabled;
m_initialized = other.m_initialized;
return *this;
}
@@ -127,6 +134,8 @@ namespace hex {
return true;
}
if (!m_enabled)
return true;
const auto requestedVersion = getCompatibleVersion();
const auto imhexVersion = ImHexApi::System::getImHexVersion().get();
@@ -215,6 +224,11 @@ namespace hex {
return m_initialized;
}
bool Plugin::isBuiltinPlugin() const {
return m_functions.isBuiltinPluginFunction != nullptr && m_functions.isBuiltinPluginFunction();
}
std::span<SubCommand> Plugin::getSubCommands() const {
if (m_functions.getSubCommandsFunction != nullptr) {
const auto result = m_functions.getSubCommandsFunction();
@@ -248,7 +262,7 @@ namespace hex {
return m_addedManually;
}
void *Plugin::getPluginFunction(const std::string &symbol) const {
void* Plugin::getPluginFunction(const std::string &symbol) const {
#if defined(OS_WINDOWS)
return reinterpret_cast<void *>(GetProcAddress(HMODULE(m_handle), symbol.c_str()));
#else
@@ -256,6 +270,11 @@ namespace hex {
#endif
}
void Plugin::setEnabled(bool enabled) {
m_enabled = enabled;
}
AutoReset<std::vector<std::fs::path>> PluginManager::s_pluginPaths, PluginManager::s_pluginLoadPaths;
@@ -288,7 +307,7 @@ namespace hex {
}
}
// Load regular plugins afterwards
// Load regular plugins afterward
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug") {
if (!isPluginLoaded(pluginPath.path())) {
@@ -301,6 +320,17 @@ namespace hex {
return !plugin.isValid();
});
// Move the built-in plugins to the front of the list so they get initialized first
getPluginsMutable().sort([](const Plugin &a, const Plugin &b) {
if (a.isBuiltinPlugin() && !b.isBuiltinPlugin()) {
return true;
} else if (!a.isBuiltinPlugin() && b.isBuiltinPlugin()) {
return false;
} else {
return a.getPluginName() < b.getPluginName();
}
});
return true;
}
@@ -398,4 +428,16 @@ namespace hex {
});
}
void PluginManager::setPluginEnabled(const Plugin &plugin, bool enabled) {
auto &plugins = getPluginsMutable();
auto it = std::ranges::find_if(plugins, [&plugin](const Plugin &p) {
return &plugin == &p;
});
if (it != plugins.end()) {
it->setEnabled(enabled);
}
}
}

View File

@@ -532,6 +532,8 @@
"hex.builtin.setting.interface.pattern_data_row_bg": "Enable colored pattern background",
"hex.builtin.setting.interface.wiki_explain_language": "Wikipedia Language",
"hex.builtin.setting.interface.restore_window_pos": "Restore window position",
"hex.builtin.setting.plugins": "Plugins",
"hex.builtin.setting.loaded_plugins": "Plugins to be loaded",
"hex.builtin.setting.proxy": "Proxy",
"hex.builtin.setting.proxy.description": "Proxy will take effect on store, wikipedia or any other plugin immediately.",
"hex.builtin.setting.proxy.enable": "Enable Proxy",

View File

@@ -20,6 +20,7 @@
#include <nlohmann/json.hpp>
#include <utility>
#include <hex/api/plugin_manager.hpp>
#include <romfs/romfs.hpp>
#if defined(OS_WEB)
@@ -972,6 +973,27 @@ namespace hex::plugin::builtin {
ContentRegistry::Settings::impl::store();
});
/* Plugins */
{
for (const auto &plugin : PluginManager::getPlugins()) {
if (plugin.isLibraryPlugin())
continue;
if (plugin.isBuiltinPlugin())
continue;
auto interface = ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.plugins", "hex.builtin.setting.loaded_plugins", plugin.getPluginName(), true)
.setTooltip(plugin.getPluginDescription())
.setChangedCallback([&plugin](Widgets::Widget &widget) {
auto checkBox = static_cast<Widgets::Checkbox *>(&widget);
PluginManager::setPluginEnabled(plugin, checkBox->isChecked());
})
.requiresRestart();
PluginManager::setPluginEnabled(plugin, static_cast<Widgets::Checkbox *>(&interface.getWidget())->isChecked());
}
}
}
static void loadLayoutSettings() {

View File

@@ -88,7 +88,7 @@ IMHEX_PLUGIN_SUBCOMMANDS() {
{ "save-editor", "", "Opens a pattern file for save file editing", hex::plugin::builtin::handleSaveEditorCommand },
};
IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
IMHEX_PLUGIN_SETUP_BUILTIN("Built-in", "WerWolv", "Default ImHex functionality") {
using namespace hex::plugin::builtin;
// Show a warning banner on debug builds