Plugin System (#1590)

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
This commit is contained in:
Vaxry 2023-02-27 12:32:38 +00:00 committed by GitHub
parent 74a10f26a4
commit 8b81f41e52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1691 additions and 85 deletions

151
src/plugins/HookSystem.cpp Normal file
View file

@ -0,0 +1,151 @@
#include "HookSystem.hpp"
#define register
#include <udis86.h>
#undef register
#include <sys/mman.h>
#include <unistd.h>
#include <cstring>
CFunctionHook::CFunctionHook(HANDLE owner, void* source, void* destination) {
m_pSource = source;
m_pDestination = destination;
m_pOwner = owner;
}
CFunctionHook::~CFunctionHook() {
if (m_bActive) {
unhook();
}
}
size_t getInstructionLenAt(void* start) {
ud_t udis;
ud_init(&udis);
ud_set_mode(&udis, 64);
ud_set_syntax(&udis, UD_SYN_INTEL);
size_t curOffset = 1;
size_t insSize = 0;
while (true) {
ud_set_input_buffer(&udis, (uint8_t*)start, curOffset);
insSize = ud_disassemble(&udis);
if (insSize != curOffset)
break;
curOffset++;
}
return insSize;
}
size_t probeMinimumJumpSize(void* start, size_t min) {
size_t size = 0;
while (size <= min) {
// find info about this instruction
size_t insLen = getInstructionLenAt(start + size);
size += insLen;
}
return size;
}
bool CFunctionHook::hook() {
// check for unsupported platforms
#if !defined(__x86_64__)
return false;
#endif
// movabs $0,%rax | jmpq *%rax
static constexpr uint8_t ABSOLUTE_JMP_ADDRESS[] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0};
// pushq %rax
static constexpr uint8_t PUSH_RAX[] = {0x50};
// popq %rax
static constexpr uint8_t POP_RAX[] = {0x58};
// nop
static constexpr uint8_t NOP = 0x90;
// get minimum size to overwrite
const auto HOOKSIZE = probeMinimumJumpSize(m_pSource, sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(PUSH_RAX) + sizeof(POP_RAX));
// alloc trampoline
m_pTrampolineAddr = mmap(NULL, sizeof(ABSOLUTE_JMP_ADDRESS) + HOOKSIZE + sizeof(PUSH_RAX), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// populate trampoline
memcpy(m_pTrampolineAddr, m_pSource, HOOKSIZE); // first, original func bytes
memcpy(m_pTrampolineAddr + HOOKSIZE, PUSH_RAX, sizeof(PUSH_RAX)); // then, pushq %rax
memcpy(m_pTrampolineAddr + HOOKSIZE + sizeof(PUSH_RAX), ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // then, jump to source
// fixup trampoline addr
*(uint64_t*)(m_pTrampolineAddr + HOOKSIZE + 2 + sizeof(PUSH_RAX)) = (uint64_t)(m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS));
// make jump to hk
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC);
memcpy(m_pSource, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS));
// make popq %rax and NOP all remaining
memcpy(m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS), POP_RAX, sizeof(POP_RAX));
size_t currentOp = sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(POP_RAX);
memset(m_pSource + currentOp, NOP, HOOKSIZE - currentOp);
// fixup jump addr
*(uint64_t*)(m_pSource + 2) = (uint64_t)(m_pDestination);
// revert mprot
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC);
// set original addr to trampo addr
m_pOriginal = m_pTrampolineAddr;
m_bActive = true;
m_iHookLen = HOOKSIZE;
m_iTrampoLen = HOOKSIZE + sizeof(ABSOLUTE_JMP_ADDRESS);
return true;
}
bool CFunctionHook::unhook() {
// check for unsupported platforms
#if !defined(__x86_64__)
return false;
#endif
if (!m_bActive)
return false;
// allow write to src
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC);
// write back original bytes
memcpy(m_pSource, m_pTrampolineAddr, m_iHookLen);
// revert mprot
mprotect(m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC);
// unmap
munmap(m_pTrampolineAddr, m_iTrampoLen);
// reset vars
m_bActive = false;
m_iHookLen = 0;
m_iTrampoLen = 0;
m_pTrampolineAddr = nullptr;
return true;
}
CFunctionHook* CHookSystem::initHook(HANDLE owner, void* source, void* destination) {
return m_vHooks.emplace_back(std::make_unique<CFunctionHook>(owner, source, destination)).get();
}
bool CHookSystem::removeHook(CFunctionHook* hook) {
std::erase_if(m_vHooks, [&](const auto& other) { return other.get() == hook; });
return true; // todo: make false if not found
}
void CHookSystem::removeAllHooksFrom(HANDLE handle) {
std::erase_if(m_vHooks, [&](const auto& other) { return other->m_pOwner == handle; });
}

View file

@ -0,0 +1,48 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#define HANDLE void*
class CFunctionHook {
public:
CFunctionHook(HANDLE owner, void* source, void* destination);
~CFunctionHook();
bool hook();
bool unhook();
CFunctionHook(const CFunctionHook&) = delete;
CFunctionHook(CFunctionHook&&) = delete;
CFunctionHook& operator=(const CFunctionHook&) = delete;
CFunctionHook& operator=(CFunctionHook&&) = delete;
void* m_pOriginal = nullptr;
private:
void* m_pSource = nullptr;
void* m_pFunctionAddr = nullptr;
void* m_pTrampolineAddr = nullptr;
void* m_pDestination = nullptr;
size_t m_iHookLen = 0;
size_t m_iTrampoLen = 0;
HANDLE m_pOwner = nullptr;
bool m_bActive = false;
friend class CHookSystem;
};
class CHookSystem {
public:
CFunctionHook* initHook(HANDLE handle, void* source, void* destination);
bool removeHook(CFunctionHook* hook);
void removeAllHooksFrom(HANDLE handle);
private:
std::vector<std::unique_ptr<CFunctionHook>> m_vHooks;
};
inline std::unique_ptr<CHookSystem> g_pFunctionHookSystem;

193
src/plugins/PluginAPI.cpp Normal file
View file

@ -0,0 +1,193 @@
#include "PluginAPI.hpp"
#include "../Compositor.hpp"
#include "../debug/HyprCtl.hpp"
#include <dlfcn.h>
APICALL bool HyprlandAPI::registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
g_pHookSystem->hookStatic(event, fn, handle);
PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, fn));
return true;
}
APICALL HOOK_CALLBACK_FN* HyprlandAPI::registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return nullptr;
auto* const PFN = g_pHookSystem->hookDynamic(event, fn, handle);
PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, PFN));
return PFN;
}
APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
g_pHookSystem->unhook(fn);
std::erase_if(PLUGIN->registeredCallbacks, [&](const auto& other) { return other.second == fn; });
return true;
}
APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format) {
std::string COMMAND = format + "/" + call + " " + args;
return HyprCtl::makeDynamicCall(COMMAND);
}
APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
PLUGIN->registeredLayouts.push_back(layout);
return g_pLayoutManager->addLayout(name, layout);
}
APICALL bool HyprlandAPI::removeLayout(HANDLE handle, IHyprLayout* layout) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
std::erase(PLUGIN->registeredLayouts, layout);
return g_pLayoutManager->removeLayout(layout);
}
APICALL bool HyprlandAPI::reloadConfig() {
g_pConfigManager->m_bForceReload = true;
return true;
}
APICALL bool HyprlandAPI::addNotification(HANDLE handle, const std::string& text, const CColor& color, const float timeMs) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
g_pHyprNotificationOverlay->addNotification(text, color, timeMs);
return true;
}
APICALL CFunctionHook* HyprlandAPI::createFunctionHook(HANDLE handle, const void* source, const void* destination) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return nullptr;
return g_pFunctionHookSystem->initHook(handle, (void*)source, (void*)destination);
}
APICALL bool HyprlandAPI::removeFunctionHook(HANDLE handle, CFunctionHook* hook) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
return g_pFunctionHookSystem->removeHook(hook);
}
APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, CWindow* pWindow, IHyprWindowDecoration* pDecoration) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
if (!g_pCompositor->windowValidMapped(pWindow))
return false;
PLUGIN->registeredDecorations.push_back(pDecoration);
pWindow->m_dWindowDecorations.emplace_back(pDecoration);
return true;
}
APICALL bool HyprlandAPI::removeWindowDecoration(HANDLE handle, IHyprWindowDecoration* pDecoration) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
for (auto& w : g_pCompositor->m_vWindows) {
for (auto& d : w->m_dWindowDecorations) {
if (d.get() == pDecoration) {
std::erase(w->m_dWindowDecorations, d);
return true;
}
}
}
return false;
}
APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, const SConfigValue& value) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!g_pPluginSystem->m_bAllowConfigVars)
return false;
if (!PLUGIN)
return false;
if (name.find("plugin:") != 0)
return false;
g_pConfigManager->addPluginConfigVar(handle, name, value);
return true;
}
APICALL SConfigValue* HyprlandAPI::getConfigValue(HANDLE handle, const std::string& name) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return nullptr;
return g_pConfigManager->getConfigValuePtrSafe(name);
}
APICALL void* HyprlandAPI::getFunctionAddressFromSignature(HANDLE handle, const std::string& sig) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return nullptr;
return dlsym(nullptr, sig.c_str());
}
APICALL bool HyprlandAPI::addDispatcher(HANDLE handle, const std::string& name, std::function<void(std::string)> handler) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
PLUGIN->registeredDispatchers.push_back(name);
g_pKeybindManager->m_mDispatchers[name] = handler;
return true;
}
APICALL bool HyprlandAPI::removeDispatcher(HANDLE handle, const std::string& name) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
std::erase_if(g_pKeybindManager->m_mDispatchers, [&](const auto& other) { return other.first == name; });
std::erase_if(PLUGIN->registeredDispatchers, [&](const auto& other) { return other == name; });
return true;
}

217
src/plugins/PluginAPI.hpp Normal file
View file

@ -0,0 +1,217 @@
#pragma once
/*
Hyprland Plugin API.
Most documentation will be made with comments in this code, but more info can be also found on the wiki.
!WARNING!
The Hyprland API passes C++ objects over, so no ABI compatibility is guaranteed.
Make sure to compile your plugins with the same compiler as Hyprland, and ideally,
on the same machine.
See examples/examplePlugin for an example plugin
*/
#define HYPRLAND_API_VERSION "0.1"
#include "../helpers/Color.hpp"
#include "HookSystem.hpp"
#include <any>
#include <functional>
#include <string>
typedef std::function<void(void*, std::any)> HOOK_CALLBACK_FN;
typedef struct {
std::string name;
std::string description;
std::string author;
std::string version;
} PLUGIN_DESCRIPTION_INFO;
#define APICALL extern "C"
#define EXPORT __attribute__((visibility("default")))
#define REQUIRED
#define OPTIONAL
#define HANDLE void*
class IHyprLayout;
class CWindow;
class IHyprWindowDecoration;
struct SConfigValue;
/*
These methods are for the plugin to implement
Methods marked with REQUIRED are required.
*/
/*
called pre-plugin init.
In case of a version mismatch, will eject the .so.
This function should not be modified, see the example plugin.
*/
typedef REQUIRED std::string (*PPLUGIN_API_VERSION_FUNC)();
#define PLUGIN_API_VERSION pluginAPIVersion
#define PLUGIN_API_VERSION_FUNC_STR "pluginAPIVersion"
/*
called on plugin init. Passes a handle as the parameter, which the plugin should keep for identification later.
The plugin should return a PLUGIN_DESCRIPTION_INFO struct with information about itself.
Keep in mind this is executed synchronously, and as such any blocking calls to hyprland might hang. (e.g. system("hyprctl ..."))
*/
typedef REQUIRED PLUGIN_DESCRIPTION_INFO (*PPLUGIN_INIT_FUNC)(HANDLE);
#define PLUGIN_INIT pluginInit
#define PLUGIN_INIT_FUNC_STR "pluginInit"
/*
called on plugin unload, if that was a user action. If the plugin is being unloaded by an error,
this will not be called.
Hooks are unloaded after exit.
*/
typedef OPTIONAL void (*PPLUGIN_EXIT_FUNC)(void);
#define PLUGIN_EXIT pluginExit
#define PLUGIN_EXIT_FUNC_STR "pluginExit"
/*
End plugin methods
*/
namespace HyprlandAPI {
/*
Add a config value.
All config values MUST be in the plugin: namespace
This method may only be called in "pluginInit"
After you have registered ALL of your config values, you may call `getConfigValue`
returns: true on success, false on fail
*/
APICALL bool addConfigValue(HANDLE handle, const std::string& name, const SConfigValue& value);
/*
Get a config value.
returns: a pointer to the config value struct, which is guaranteed to be valid for the life of this plugin, unless another `addConfigValue` is called afterwards.
nullptr on error.
*/
APICALL SConfigValue* getConfigValue(HANDLE handle, const std::string& name);
/*
Register a static (pointer) callback to a selected event.
Pointer must be kept valid until unregisterCallback() is called.
returns: true on success, false on fail
*/
APICALL bool registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn);
/*
Register a dynamic (function) callback to a selected event.
Pointer will be free'd by Hyprland on unregisterCallback().
returns: a pointer to the newly allocated function. nullptr on fail.
*/
APICALL HOOK_CALLBACK_FN* registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn);
/*
Unregisters a callback. If the callback was dynamic, frees the memory.
returns: true on success, false on fail
*/
APICALL bool unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn);
/*
Calls a hyprctl command.
returns: the output (as in hyprctl)
*/
APICALL std::string invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format = "");
/*
Adds a layout to Hyprland.
returns: true on success. False otherwise.
*/
APICALL bool addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout);
/*
Removes an added layout from Hyprland.
returns: true on success. False otherwise.
*/
APICALL bool removeLayout(HANDLE handle, IHyprLayout* layout);
/*
Queues a config reload. Does not take effect immediately.
returns: true on success. False otherwise.
*/
APICALL bool reloadConfig();
/*
Adds a notification.
returns: true on success. False otherwise.
*/
APICALL bool addNotification(HANDLE handle, const std::string& text, const CColor& color, const float timeMs);
/*
Creates a trampoline function hook to an internal hl func.
returns: CFunctionHook*
!WARNING! Hooks are *not* guaranteed any API stability. Internal methods may be removed, added, or renamed. Consider preferring the API whenever possible.
*/
APICALL CFunctionHook* createFunctionHook(HANDLE handle, const void* source, const void* destination);
/*
Removes a trampoline function hook. Will unhook if still hooked.
returns: true on success. False otherwise.
!WARNING! Hooks are *not* guaranteed any API stability. Internal methods may be removed, added, or renamed. Consider preferring the API whenever possible.
*/
APICALL bool removeFunctionHook(HANDLE handle, CFunctionHook* hook);
/*
Gets a function address from a signature.
This is useful for hooking private functions.
returns: function address, or nullptr on fail.
*/
APICALL void* getFunctionAddressFromSignature(HANDLE handle, const std::string& sig);
/*
Adds a window decoration to a window
returns: true on success. False otherwise.
*/
APICALL bool addWindowDecoration(HANDLE handle, CWindow* pWindow, IHyprWindowDecoration* pDecoration);
/*
Removes a window decoration
returns: true on success. False otherwise.
*/
APICALL bool removeWindowDecoration(HANDLE handle, IHyprWindowDecoration* pDecoration);
/*
Adds a keybind dispatcher.
returns: true on success. False otherwise.
*/
APICALL bool addDispatcher(HANDLE handle, const std::string& name, std::function<void(std::string)> handler);
/*
Removes a keybind dispatcher.
returns: true on success. False otherwise.
*/
APICALL bool removeDispatcher(HANDLE handle, const std::string& name);
};

View file

@ -0,0 +1,139 @@
#include "PluginSystem.hpp"
#include <dlfcn.h>
#include "../Compositor.hpp"
CPluginSystem::CPluginSystem() {
g_pFunctionHookSystem = std::make_unique<CHookSystem>();
}
CPlugin* CPluginSystem::loadPlugin(const std::string& path) {
if (getPluginByPath(path)) {
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) {
Debug::log(ERR, " [PluginSystem] Plugin %s could not be loaded: %s", path.c_str(), dlerror());
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) {
Debug::log(ERR, " [PluginSystem] Plugin %s could not be loaded. (No apiver/init func)", path.c_str());
dlclose(MODULE);
m_vLoadedPlugins.pop_back();
return nullptr;
}
const std::string PLUGINAPIVER = apiVerFunc();
if (PLUGINAPIVER != HYPRLAND_API_VERSION) {
Debug::log(ERR, " [PluginSystem] Plugin %s could not be loaded. (API version mismatch)", path.c_str());
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.
throw std::exception();
}
} catch (std::exception& e) {
m_bAllowConfigVars = false;
Debug::log(ERR, " [PluginSystem] Plugin %s (Handle %lx) crashed in init. Unloading.", path.c_str(), MODULE);
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;
Debug::log(LOG, " [PluginSystem] Plugin %s loaded. Handle: %lx, path: \"%s\", author: \"%s\", description: \"%s\", version: \"%s\"", PLUGINDATA.name.c_str(), MODULE,
path.c_str(), PLUGINDATA.author.c_str(), PLUGINDATA.description.c_str(), PLUGINDATA.version.c_str());
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);
Debug::log(LOG, " [PluginSystem] Plugin %s unloaded.", plugin->name.c_str());
std::erase_if(m_vLoadedPlugins, [&](const auto& other) { return other->m_pHandle == plugin->m_pHandle; });
}
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;
}

View file

@ -0,0 +1,44 @@
#pragma once
#include "../defines.hpp"
#include "PluginAPI.hpp"
#include <csetjmp>
class IHyprWindowDecoration;
class CPlugin {
public:
std::string name = "";
std::string description = "";
std::string author = "";
std::string version = "";
std::string path = "";
HANDLE m_pHandle = nullptr;
std::vector<IHyprLayout*> registeredLayouts;
std::vector<IHyprWindowDecoration*> registeredDecorations;
std::vector<std::pair<std::string, HOOK_CALLBACK_FN*>> registeredCallbacks;
std::vector<std::string> registeredDispatchers;
};
class CPluginSystem {
public:
CPluginSystem();
CPlugin* loadPlugin(const std::string& path);
void unloadPlugin(const CPlugin* plugin, bool eject = false);
CPlugin* getPluginByPath(const std::string& path);
CPlugin* getPluginByHandle(HANDLE handle);
std::vector<CPlugin*> getAllPlugins();
bool m_bAllowConfigVars = false;
private:
std::vector<std::unique_ptr<CPlugin>> m_vLoadedPlugins;
jmp_buf m_jbPluginFaultJumpBuf;
};
inline std::unique_ptr<CPluginSystem> g_pPluginSystem;