mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-12 21:25:59 +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!");
|
||||
g_pPluginSystem = std::make_unique<CPluginSystem>();
|
||||
g_pConfigManager->handlePluginLoads();
|
||||
} break;
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = "";
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue