2023-02-27 13:32:38 +01:00
|
|
|
#include "PluginSystem.hpp"
|
|
|
|
|
|
|
|
#include <dlfcn.h>
|
2023-03-01 00:11:49 +01:00
|
|
|
#include <ranges>
|
2023-02-27 13:32:38 +01:00
|
|
|
#include "../Compositor.hpp"
|
|
|
|
|
|
|
|
CPluginSystem::CPluginSystem() {
|
|
|
|
g_pFunctionHookSystem = std::make_unique<CHookSystem>();
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin* CPluginSystem::loadPlugin(const std::string& path) {
|
|
|
|
|
2023-11-17 23:22:31 +01:00
|
|
|
m_szLastError = "";
|
|
|
|
|
2023-02-27 13:32:38 +01:00
|
|
|
if (getPluginByPath(path)) {
|
2023-11-17 23:22:31 +01:00
|
|
|
m_szLastError = "Cannot load a plugin twice!";
|
2023-02-27 13:32:38 +01:00
|
|
|
Debug::log(ERR, " [PluginSystem] Cannot load a plugin twice!");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* const PLUGIN = m_vLoadedPlugins.emplace_back(std::make_unique<CPlugin>()).get();
|
|
|
|
|
|
|
|
PLUGIN->path = path;
|
|
|
|
|
|
|
|
HANDLE MODULE = dlopen(path.c_str(), RTLD_LAZY);
|
|
|
|
|
|
|
|
if (!MODULE) {
|
2023-11-17 23:22:31 +01:00
|
|
|
m_szLastError = std::format("Plugin {} could not be loaded: {}", path, dlerror());
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded: {}", path, dlerror());
|
2023-02-27 13:32:38 +01:00
|
|
|
m_vLoadedPlugins.pop_back();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PLUGIN->m_pHandle = MODULE;
|
|
|
|
|
|
|
|
PPLUGIN_API_VERSION_FUNC apiVerFunc = (PPLUGIN_API_VERSION_FUNC)dlsym(MODULE, PLUGIN_API_VERSION_FUNC_STR);
|
|
|
|
PPLUGIN_INIT_FUNC initFunc = (PPLUGIN_INIT_FUNC)dlsym(MODULE, PLUGIN_INIT_FUNC_STR);
|
|
|
|
|
|
|
|
if (!apiVerFunc || !initFunc) {
|
2023-11-17 23:22:31 +01:00
|
|
|
m_szLastError = std::format("Plugin {} could not be loaded: {}", path, "missing apiver/init func");
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded. (No apiver/init func)", path);
|
2023-02-27 13:32:38 +01:00
|
|
|
dlclose(MODULE);
|
|
|
|
m_vLoadedPlugins.pop_back();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string PLUGINAPIVER = apiVerFunc();
|
|
|
|
|
|
|
|
if (PLUGINAPIVER != HYPRLAND_API_VERSION) {
|
2023-11-17 23:22:31 +01:00
|
|
|
m_szLastError = std::format("Plugin {} could not be loaded: {}", path, "API version mismatch");
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded. (API version mismatch)", path);
|
2023-02-27 13:32:38 +01:00
|
|
|
dlclose(MODULE);
|
|
|
|
m_vLoadedPlugins.pop_back();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PLUGIN_DESCRIPTION_INFO PLUGINDATA;
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (!setjmp(m_jbPluginFaultJumpBuf)) {
|
|
|
|
m_bAllowConfigVars = true;
|
|
|
|
PLUGINDATA = initFunc(MODULE);
|
|
|
|
} else {
|
|
|
|
// this module crashed.
|
2023-11-17 23:22:31 +01:00
|
|
|
throw std::runtime_error("received a fatal signal");
|
2023-02-27 13:32:38 +01:00
|
|
|
}
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
m_bAllowConfigVars = false;
|
2023-11-17 23:22:31 +01:00
|
|
|
m_szLastError = std::format("Plugin {} could not be loaded: plugin crashed/threw in main: {}", path, e.what());
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(ERR, " [PluginSystem] Plugin {} (Handle {:x}) crashed in init. Unloading.", path, (uintptr_t)MODULE);
|
2023-02-27 13:32:38 +01:00
|
|
|
unloadPlugin(PLUGIN, true); // Plugin could've already hooked/done something
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_bAllowConfigVars = false;
|
|
|
|
|
|
|
|
PLUGIN->author = PLUGINDATA.author;
|
|
|
|
PLUGIN->description = PLUGINDATA.description;
|
|
|
|
PLUGIN->version = PLUGINDATA.version;
|
|
|
|
PLUGIN->name = PLUGINDATA.name;
|
|
|
|
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(LOG, " [PluginSystem] Plugin {} loaded. Handle: {:x}, path: \"{}\", author: \"{}\", description: \"{}\", version: \"{}\"", PLUGINDATA.name, (uintptr_t)MODULE, path,
|
|
|
|
PLUGINDATA.author, PLUGINDATA.description, PLUGINDATA.version);
|
2023-02-27 13:32:38 +01:00
|
|
|
|
|
|
|
return PLUGIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) {
|
|
|
|
if (!plugin)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!eject) {
|
|
|
|
PPLUGIN_EXIT_FUNC exitFunc = (PPLUGIN_EXIT_FUNC)dlsym(plugin->m_pHandle, PLUGIN_EXIT_FUNC_STR);
|
|
|
|
if (exitFunc)
|
|
|
|
exitFunc();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& [k, v] : plugin->registeredCallbacks)
|
|
|
|
g_pHookSystem->unhook(v);
|
|
|
|
|
|
|
|
const auto ls = plugin->registeredLayouts;
|
|
|
|
for (auto& l : ls)
|
|
|
|
g_pLayoutManager->removeLayout(l);
|
|
|
|
|
|
|
|
g_pFunctionHookSystem->removeAllHooksFrom(plugin->m_pHandle);
|
|
|
|
|
|
|
|
const auto rd = plugin->registeredDecorations;
|
|
|
|
for (auto& d : rd)
|
|
|
|
HyprlandAPI::removeWindowDecoration(plugin->m_pHandle, d);
|
|
|
|
|
|
|
|
const auto rdi = plugin->registeredDispatchers;
|
|
|
|
for (auto& d : rdi)
|
|
|
|
HyprlandAPI::removeDispatcher(plugin->m_pHandle, d);
|
|
|
|
|
|
|
|
g_pConfigManager->removePluginConfig(plugin->m_pHandle);
|
|
|
|
|
|
|
|
dlclose(plugin->m_pHandle);
|
|
|
|
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(LOG, " [PluginSystem] Plugin {} unloaded.", plugin->name);
|
2023-02-27 13:32:38 +01:00
|
|
|
|
|
|
|
std::erase_if(m_vLoadedPlugins, [&](const auto& other) { return other->m_pHandle == plugin->m_pHandle; });
|
2023-07-10 14:13:23 +02:00
|
|
|
|
|
|
|
// reload config to fix some stuf like e.g. unloadedPluginVars
|
|
|
|
g_pConfigManager->m_bForceReload = true;
|
2023-02-27 13:32:38 +01:00
|
|
|
}
|
|
|
|
|
2023-03-01 00:11:49 +01:00
|
|
|
void CPluginSystem::unloadAllPlugins() {
|
|
|
|
for (auto& p : m_vLoadedPlugins | std::views::reverse)
|
|
|
|
unloadPlugin(p.get(), false); // Unload remaining plugins gracefully
|
|
|
|
}
|
|
|
|
|
2023-05-01 16:10:53 +02:00
|
|
|
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()) {
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(LOG, "Unloading plugin {} which is no longer present in config", p->path);
|
2023-05-01 16:10:53 +02:00
|
|
|
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()) {
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(LOG, "Loading plugin {} which is now present in config", path);
|
2023-05-01 16:10:53 +02:00
|
|
|
const auto plugin = loadPlugin(path);
|
|
|
|
|
|
|
|
if (plugin) {
|
|
|
|
plugin->m_bLoadedWithConfig = true;
|
|
|
|
changed = true;
|
|
|
|
} else
|
|
|
|
failures.push_back(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return failures;
|
|
|
|
}
|
|
|
|
|
2023-02-27 13:32:38 +01:00
|
|
|
CPlugin* CPluginSystem::getPluginByPath(const std::string& path) {
|
|
|
|
for (auto& p : m_vLoadedPlugins) {
|
|
|
|
if (p->path == path)
|
|
|
|
return p.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin* CPluginSystem::getPluginByHandle(HANDLE handle) {
|
|
|
|
for (auto& p : m_vLoadedPlugins) {
|
|
|
|
if (p->m_pHandle == handle)
|
|
|
|
return p.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CPlugin*> CPluginSystem::getAllPlugins() {
|
|
|
|
std::vector<CPlugin*> results(m_vLoadedPlugins.size());
|
|
|
|
for (size_t i = 0; i < m_vLoadedPlugins.size(); ++i)
|
|
|
|
results[i] = m_vLoadedPlugins[i].get();
|
|
|
|
return results;
|
2023-03-01 00:11:49 +01:00
|
|
|
}
|