diff --git a/lib/libimhex/include/hex/api/plugin_manager.hpp b/lib/libimhex/include/hex/api/plugin_manager.hpp index c74accf20..2f4449ca7 100644 --- a/lib/libimhex/include/hex/api/plugin_manager.hpp +++ b/lib/libimhex/include/hex/api/plugin_manager.hpp @@ -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 getSubCommands() const; [[nodiscard]] std::span 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& getPluginsMutable(); diff --git a/lib/libimhex/include/hex/plugin.hpp b/lib/libimhex/include/hex/plugin.hpp index 1668a3953..b245f2834 100644 --- a/lib/libimhex/include/hex/plugin.hpp +++ b/lib/libimhex/include/hex/plugin.hpp @@ -80,6 +80,10 @@ void* PluginSubCommandsFunctionHelper::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; } \ diff --git a/lib/libimhex/source/api/plugin_manager.cpp b/lib/libimhex/source/api/plugin_manager.cpp index 3ae50c886..e467f1f1a 100644 --- a/lib/libimhex/source/api/plugin_manager.cpp +++ b/lib/libimhex/source/api/plugin_manager.cpp @@ -81,6 +81,7 @@ namespace hex { m_functions.setImGuiContextLibraryFunction = getPluginFunction(hex::format("setImGuiContext_{}", fileName)); m_functions.getSubCommandsFunction = getPluginFunction("getSubCommands"); m_functions.getFeaturesFunction = getPluginFunction("getFeatures"); + m_functions.isBuiltinPluginFunction = getPluginFunction("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 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(GetProcAddress(HMODULE(m_handle), symbol.c_str())); #else @@ -256,6 +270,11 @@ namespace hex { #endif } + void Plugin::setEnabled(bool enabled) { + m_enabled = enabled; + } + + AutoReset> 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); + } + } + + } diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 701dda342..a053d18dc 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -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", diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 775d8af86..2e021bc20 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #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("hex.builtin.setting.plugins", "hex.builtin.setting.loaded_plugins", plugin.getPluginName(), true) + .setTooltip(plugin.getPluginDescription()) + .setChangedCallback([&plugin](Widgets::Widget &widget) { + auto checkBox = static_cast(&widget); + PluginManager::setPluginEnabled(plugin, checkBox->isChecked()); + }) + .requiresRestart(); + + PluginManager::setPluginEnabled(plugin, static_cast(&interface.getWidget())->isChecked()); + } + } } static void loadLayoutSettings() { diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index 6f26dd28a..80ebf9864 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -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