mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-14 16:45:58 +01:00
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:
parent
dc469dc4c1
commit
3a631e40db
6 changed files with 86 additions and 8 deletions
|
@ -402,6 +402,7 @@ void CCompositor::initManagers(eManagersInitStage stage) {
|
||||||
|
|
||||||
Debug::log(LOG, "Creating the PluginSystem!");
|
Debug::log(LOG, "Creating the PluginSystem!");
|
||||||
g_pPluginSystem = std::make_unique<CPluginSystem>();
|
g_pPluginSystem = std::make_unique<CPluginSystem>();
|
||||||
|
g_pConfigManager->handlePluginLoads();
|
||||||
} break;
|
} break;
|
||||||
default: UNREACHABLE();
|
default: UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE, bool dynamic) {
|
||||||
if (dynamic) {
|
if (dynamic) {
|
||||||
parseError = "";
|
parseError = "";
|
||||||
|
@ -1151,6 +1160,8 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
|
||||||
handleBindWS(COMMAND, VALUE);
|
handleBindWS(COMMAND, VALUE);
|
||||||
else if (COMMAND.find("env") == 0)
|
else if (COMMAND.find("env") == 0)
|
||||||
handleEnv(COMMAND, VALUE);
|
handleEnv(COMMAND, VALUE);
|
||||||
|
else if (COMMAND.find("plugin") == 0)
|
||||||
|
handlePlugin(COMMAND, VALUE);
|
||||||
else {
|
else {
|
||||||
configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE);
|
configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE);
|
||||||
needsLayoutRecalc = 2;
|
needsLayoutRecalc = 2;
|
||||||
|
@ -1303,6 +1314,7 @@ void CConfigManager::loadConfigLoadVars() {
|
||||||
m_dBlurLSNamespaces.clear();
|
m_dBlurLSNamespaces.clear();
|
||||||
boundWorkspaces.clear();
|
boundWorkspaces.clear();
|
||||||
setDefaultAnimationVars(); // reset anims
|
setDefaultAnimationVars(); // reset anims
|
||||||
|
m_vDeclaredPlugins.clear();
|
||||||
|
|
||||||
// paths
|
// paths
|
||||||
configPaths.clear();
|
configPaths.clear();
|
||||||
|
@ -1441,6 +1453,9 @@ void CConfigManager::loadConfigLoadVars() {
|
||||||
|
|
||||||
// Reset no monitor reload
|
// Reset no monitor reload
|
||||||
m_bNoMonitorReload = false;
|
m_bNoMonitorReload = false;
|
||||||
|
|
||||||
|
// update plugins
|
||||||
|
handlePluginLoads();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CConfigManager::tick() {
|
void CConfigManager::tick() {
|
||||||
|
@ -1954,6 +1969,31 @@ void CConfigManager::addExecRule(const SExecRequestedRule& rule) {
|
||||||
execRequestedRules.push_back(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() {
|
ICustomConfigValueData::~ICustomConfigValueData() {
|
||||||
; // empty
|
; // empty
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,8 @@ class CConfigManager {
|
||||||
|
|
||||||
void addExecRule(const SExecRequestedRule&);
|
void addExecRule(const SExecRequestedRule&);
|
||||||
|
|
||||||
|
void handlePluginLoads();
|
||||||
|
|
||||||
std::string configCurrentPath;
|
std::string configCurrentPath;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -203,6 +205,7 @@ class CConfigManager {
|
||||||
|
|
||||||
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
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
|
std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs
|
||||||
|
|
||||||
bool isFirstLaunch = true; // For exec-once
|
bool isFirstLaunch = true; // For exec-once
|
||||||
|
@ -250,6 +253,7 @@ class CConfigManager {
|
||||||
void handleBlurLS(const std::string&, const std::string&);
|
void handleBlurLS(const std::string&, const std::string&);
|
||||||
void handleBindWS(const std::string&, const std::string&);
|
void handleBindWS(const std::string&, const std::string&);
|
||||||
void handleEnv(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;
|
inline std::unique_ptr<CConfigManager> g_pConfigManager;
|
||||||
|
|
|
@ -33,7 +33,6 @@ void CHyprError::queueCreate(std::string message, const CColor& color) {
|
||||||
|
|
||||||
void CHyprError::createQueued() {
|
void CHyprError::createQueued() {
|
||||||
if (m_bIsCreated) {
|
if (m_bIsCreated) {
|
||||||
m_bQueuedDestroy = false;
|
|
||||||
m_tTexture.destroyTexture();
|
m_tTexture.destroyTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,4 +170,6 @@ void CHyprError::draw() {
|
||||||
void CHyprError::destroy() {
|
void CHyprError::destroy() {
|
||||||
if (m_bIsCreated)
|
if (m_bIsCreated)
|
||||||
m_bQueuedDestroy = true;
|
m_bQueuedDestroy = true;
|
||||||
|
else
|
||||||
|
m_szQueued = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,35 @@ void CPluginSystem::unloadAllPlugins() {
|
||||||
unloadPlugin(p.get(), false); // Unload remaining plugins gracefully
|
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) {
|
CPlugin* CPluginSystem::getPluginByPath(const std::string& path) {
|
||||||
for (auto& p : m_vLoadedPlugins) {
|
for (auto& p : m_vLoadedPlugins) {
|
||||||
if (p->path == path)
|
if (p->path == path)
|
||||||
|
|
|
@ -15,6 +15,8 @@ class CPlugin {
|
||||||
|
|
||||||
std::string path = "";
|
std::string path = "";
|
||||||
|
|
||||||
|
bool m_bLoadedWithConfig = false;
|
||||||
|
|
||||||
HANDLE m_pHandle = nullptr;
|
HANDLE m_pHandle = nullptr;
|
||||||
|
|
||||||
std::vector<IHyprLayout*> registeredLayouts;
|
std::vector<IHyprLayout*> registeredLayouts;
|
||||||
|
@ -30,6 +32,7 @@ class CPluginSystem {
|
||||||
CPlugin* loadPlugin(const std::string& path);
|
CPlugin* loadPlugin(const std::string& path);
|
||||||
void unloadPlugin(const CPlugin* plugin, bool eject = false);
|
void unloadPlugin(const CPlugin* plugin, bool eject = false);
|
||||||
void unloadAllPlugins();
|
void unloadAllPlugins();
|
||||||
|
std::vector<std::string> updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed);
|
||||||
CPlugin* getPluginByPath(const std::string& path);
|
CPlugin* getPluginByPath(const std::string& path);
|
||||||
CPlugin* getPluginByHandle(HANDLE handle);
|
CPlugin* getPluginByHandle(HANDLE handle);
|
||||||
std::vector<CPlugin*> getAllPlugins();
|
std::vector<CPlugin*> getAllPlugins();
|
||||||
|
|
Loading…
Reference in a new issue