2024-02-20 21:16:07 +01:00
|
|
|
---
|
|
|
|
weight: 4
|
|
|
|
title: Advanced
|
|
|
|
---
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
This page documents a few advanced things about the Hyprland Plugin API.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
|
|
|
## Using Function Hooks
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
{{< callout >}}
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
Function hooks are only available on `AMD64` (`x86_64`). Attempting to hook on
|
|
|
|
any other arch will make Hyprland simply ignore your hooking attempt.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
{{</ callout >}}
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
Function hooks are intimidating at first, but when used properly can be
|
|
|
|
_extremely_ powerful.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
|
|
|
Function hooks allow you to intercept any call to the function you hook.
|
|
|
|
|
|
|
|
Let's look at a simple example:
|
2024-02-20 21:16:07 +01:00
|
|
|
|
2023-02-27 13:32:56 +01:00
|
|
|
```cpp
|
|
|
|
void Events::listener_monitorFrame(void* owner, void* data)
|
|
|
|
```
|
2024-02-20 21:16:07 +01:00
|
|
|
|
|
|
|
will be the function we want to hook. `Events::` is a namespace, not a class, so
|
|
|
|
this is just a plain function.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
|
|
|
```cpp
|
|
|
|
// make a global instance of a hook class for this hook
|
|
|
|
inline CFunctionHook* g_pMonitorFrameHook = nullptr;
|
|
|
|
// create a pointer typedef for the function we are hooking.
|
|
|
|
typedef void (*origMonitorFrame)(void*, void*);
|
|
|
|
|
|
|
|
// our hook
|
|
|
|
void hkMonitorFrame(void* owner, void* data) {
|
|
|
|
(*(origMonitorFrame)g_pMonitorFrameHook->m_pOriginal)(owner, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|
|
|
// stuff...
|
|
|
|
|
|
|
|
// create the hook
|
2023-04-16 01:39:00 +02:00
|
|
|
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "listener_monitorFrame");
|
|
|
|
g_pMonitorFrameHook = HyprlandAPI::createFunctionHook(handle, METHODS[0].address, (void*)&hkMonitorFrame);
|
2023-02-27 13:32:56 +01:00
|
|
|
|
|
|
|
// init the hook
|
|
|
|
g_pMonitorFrameHook->hook();
|
|
|
|
|
|
|
|
// further stuff...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
We have just made a hook. Now, whenever Hyprland calls
|
|
|
|
`Events::listener_monitorFrame`, our hook will be called instead!
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
This way, you can run code before / after the function, modify the inputs or
|
|
|
|
results, or even block the function from executing.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
`CFunctionHook` can also be unhooked whenever you please. Just run `unhook()`.
|
|
|
|
It can be rehooked later by calling `hook()` again.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2023-04-16 01:39:00 +02:00
|
|
|
### Member functions
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
For members, e.g. `CCompositor::focusWindow(CWindow*, wlr_surface*)` you will
|
|
|
|
also need to add the thisptr argument to your hook:
|
2023-02-27 13:32:56 +01:00
|
|
|
|
|
|
|
```cpp
|
|
|
|
typedef void (*origFocusWindow)(void*, CWindow*, wlr_surface*);
|
|
|
|
|
|
|
|
void hkFocusWindow(void* thisptr, CWindow* pWindow, wlr_surface* pSurface) {
|
|
|
|
// stuff...
|
|
|
|
|
|
|
|
// and if you want to call the original...
|
|
|
|
(*(origFocusWindow)g_pFocusWindowHook->m_pOriginal)(thisptr, pWindow, pSurface);
|
|
|
|
}
|
|
|
|
|
|
|
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|
|
|
// stuff...
|
|
|
|
|
2023-04-16 01:39:00 +02:00
|
|
|
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "focusWindow");
|
|
|
|
g_pFocusWindowHook = HyprlandAPI::createFunctionHook(handle, METHODS[0].address, (void*)&hkFocusWindow);
|
2023-02-27 13:32:56 +01:00
|
|
|
g_pFocusWindowHook->hook();
|
|
|
|
|
|
|
|
// further stuff...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
{{< callout type=warning >}}
|
|
|
|
|
|
|
|
Please note method lookups are slow and should not be used often. The entries
|
|
|
|
_will not_ change during runtime, so it's a good idea to make the lookups
|
|
|
|
`static`.
|
|
|
|
|
|
|
|
{{</ callout >}}
|
2023-02-27 13:32:56 +01:00
|
|
|
|
2023-04-16 01:39:00 +02:00
|
|
|
### Why use findFunctionsByName?
|
|
|
|
|
|
|
|
Why use that instead of e.g. `&CCompositor::focusWindow`? Two reasons:
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
1 - less breakage. Whenever someone updates hyprland, that address might become
|
|
|
|
invalid. findFunctionsByName is more resilient. As long as the function exists,
|
|
|
|
it will be found.
|
2023-04-16 01:39:00 +02:00
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
2 - error handling. The method array contains, besides the address, the
|
|
|
|
signatures. You can verify those to make 100% sure you got the right function,
|
|
|
|
or throw an error if it was not found.
|
2023-04-16 01:39:00 +02:00
|
|
|
|
2023-02-27 13:32:56 +01:00
|
|
|
## Using the config
|
2024-02-20 21:16:07 +01:00
|
|
|
|
2023-02-27 13:32:56 +01:00
|
|
|
You can register config values in the `PLUGIN_INIT` function:
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|
|
|
// stuff...
|
|
|
|
|
|
|
|
HyprlandAPI::addConfigValue(PHANDLE, "plugin:example:exampleInt", SConfigValue{.intValue = 1});
|
|
|
|
|
|
|
|
// further stuff...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
Plugin variables _**must**_ be in the `plugins:` category. Further categories
|
|
|
|
are up to you. It's generally a good idea to group all variables from your
|
|
|
|
plugin in a subcategory with the plugin name, e.g. `plugins:myPlugin:variable1`.
|
2023-02-27 13:32:56 +01:00
|
|
|
|
|
|
|
For retrieving the values, call `HyprlandAPI::getConfigValue`.
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
Please remember that the pointer to your config value will never change after
|
|
|
|
`PLUGIN_INIT`, so to greatly optimize performance, make it static:
|
|
|
|
|
2023-02-27 13:32:56 +01:00
|
|
|
```cpp
|
|
|
|
static auto* const MYVAR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:myPlugin:variable1")->intValue;
|
|
|
|
```
|
|
|
|
|
|
|
|
## Further
|
|
|
|
|
2024-02-20 21:16:07 +01:00
|
|
|
Read the API at `src/plugins/PluginAPI.hpp`, check out the
|
|
|
|
[official plugins](https://github.com/hyprwm/hyprland-plugins).
|
|
|
|
|
|
|
|
And, most importantly, have fun!
|