Declarative plugin management (#2180)

* Declarative plugin management

Allow declaring `plugin` entries in the hyprland configuration.

Plugins will be loaded if an entry is added and unloaded if that entry
is removed.

* Replace pointers with copying in updateconfigPlugins

* Include which plugin was declared twice in error
This commit is contained in:
outfoxxed 2023-05-01 07:10:53 -07:00 committed by GitHub
parent dc469dc4c1
commit 3a631e40db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 8 deletions

View file

@ -402,6 +402,7 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the PluginSystem!");
g_pPluginSystem = std::make_unique<CPluginSystem>();
g_pConfigManager->handlePluginLoads();
} break;
default: UNREACHABLE();
}

View file

@ -1105,6 +1105,15 @@ void CConfigManager::handleEnv(const std::string& command, const std::string& va
}
}
void CConfigManager::handlePlugin(const std::string& command, const std::string& path) {
if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) {
parseError = "plugin '" + path + "' declared twice";
return;
}
m_vDeclaredPlugins.push_back(path);
}
std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE, bool dynamic) {
if (dynamic) {
parseError = "";
@ -1151,6 +1160,8 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
handleBindWS(COMMAND, VALUE);
else if (COMMAND.find("env") == 0)
handleEnv(COMMAND, VALUE);
else if (COMMAND.find("plugin") == 0)
handlePlugin(COMMAND, VALUE);
else {
configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE);
needsLayoutRecalc = 2;
@ -1303,6 +1314,7 @@ void CConfigManager::loadConfigLoadVars() {
m_dBlurLSNamespaces.clear();
boundWorkspaces.clear();
setDefaultAnimationVars(); // reset anims
m_vDeclaredPlugins.clear();
// paths
configPaths.clear();
@ -1441,6 +1453,9 @@ void CConfigManager::loadConfigLoadVars() {
// Reset no monitor reload
m_bNoMonitorReload = false;
// update plugins
handlePluginLoads();
}
void CConfigManager::tick() {
@ -1954,6 +1969,31 @@ void CConfigManager::addExecRule(const SExecRequestedRule& rule) {
execRequestedRules.push_back(rule);
}
void CConfigManager::handlePluginLoads() {
if (g_pPluginSystem == nullptr)
return;
bool pluginsChanged = false;
auto failedPlugins = g_pPluginSystem->updateConfigPlugins(m_vDeclaredPlugins, pluginsChanged);
if (!failedPlugins.empty()) {
std::stringstream error;
error << "Failed to load the following plugins:";
for (auto path : failedPlugins) {
error << "\n" << path;
}
g_pHyprError->queueCreate(error.str(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0));
}
if (pluginsChanged) {
g_pHyprError->destroy();
m_bForceReload = true;
tick();
}
}
ICustomConfigValueData::~ICustomConfigValueData() {
; // empty
}

View file

@ -182,6 +182,8 @@ class CConfigManager {
void addExecRule(const SExecRequestedRule&);
void handlePluginLoads();
std::string configCurrentPath;
private:
@ -203,6 +205,7 @@ class CConfigManager {
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
std::vector<std::string> m_vDeclaredPlugins;
std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs
bool isFirstLaunch = true; // For exec-once
@ -250,6 +253,7 @@ class CConfigManager {
void handleBlurLS(const std::string&, const std::string&);
void handleBindWS(const std::string&, const std::string&);
void handleEnv(const std::string&, const std::string&);
void handlePlugin(const std::string&, const std::string&);
};
inline std::unique_ptr<CConfigManager> g_pConfigManager;

View file

@ -33,7 +33,6 @@ void CHyprError::queueCreate(std::string message, const CColor& color) {
void CHyprError::createQueued() {
if (m_bIsCreated) {
m_bQueuedDestroy = false;
m_tTexture.destroyTexture();
}
@ -171,4 +170,6 @@ void CHyprError::draw() {
void CHyprError::destroy() {
if (m_bIsCreated)
m_bQueuedDestroy = true;
else
m_szQueued = "";
}

View file

@ -119,6 +119,35 @@ void CPluginSystem::unloadAllPlugins() {
unloadPlugin(p.get(), false); // Unload remaining plugins gracefully
}
std::vector<std::string> CPluginSystem::updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed) {
std::vector<std::string> failures;
// unload all plugins that are no longer present
for (auto& p : m_vLoadedPlugins | std::views::reverse) {
if (p->m_bLoadedWithConfig && std::find(plugins.begin(), plugins.end(), p->path) == plugins.end()) {
Debug::log(LOG, "Unloading plugin %s which is no longer present in config", p->path.c_str());
unloadPlugin(p.get(), false);
changed = true;
}
}
// load all new plugins
for (auto& path : plugins) {
if (std::find_if(m_vLoadedPlugins.begin(), m_vLoadedPlugins.end(), [&](const auto& other) { return other->path == path; }) == m_vLoadedPlugins.end()) {
Debug::log(LOG, "Loading plugin %s which is now present in config", path.c_str());
const auto plugin = loadPlugin(path);
if (plugin) {
plugin->m_bLoadedWithConfig = true;
changed = true;
} else
failures.push_back(path);
}
}
return failures;
}
CPlugin* CPluginSystem::getPluginByPath(const std::string& path) {
for (auto& p : m_vLoadedPlugins) {
if (p->path == path)

View file

@ -15,6 +15,8 @@ class CPlugin {
std::string path = "";
bool m_bLoadedWithConfig = false;
HANDLE m_pHandle = nullptr;
std::vector<IHyprLayout*> registeredLayouts;
@ -27,14 +29,15 @@ class CPluginSystem {
public:
CPluginSystem();
CPlugin* loadPlugin(const std::string& path);
void unloadPlugin(const CPlugin* plugin, bool eject = false);
void unloadAllPlugins();
CPlugin* getPluginByPath(const std::string& path);
CPlugin* getPluginByHandle(HANDLE handle);
std::vector<CPlugin*> getAllPlugins();
CPlugin* loadPlugin(const std::string& path);
void unloadPlugin(const CPlugin* plugin, bool eject = false);
void unloadAllPlugins();
std::vector<std::string> updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed);
CPlugin* getPluginByPath(const std::string& path);
CPlugin* getPluginByHandle(HANDLE handle);
std::vector<CPlugin*> getAllPlugins();
bool m_bAllowConfigVars = false;
bool m_bAllowConfigVars = false;
private:
std::vector<std::unique_ptr<CPlugin>> m_vLoadedPlugins;