fix: better error handling and event loop

This commit is contained in:
Virt 2024-06-30 22:38:08 +02:00
commit 91c03dea2e
6 changed files with 69 additions and 49 deletions

View file

@ -34,7 +34,7 @@ This plugin is still very early in its development. There are also multiple thin
- [ ] per-shape length and starting angle (if possible)
- [X] cursor shake to find
- [ ] overdue refactoring (wait for aquamarine merge)
- [ ] inverted cursor?
- [ ] ~~inverted cursor?~~ (i think out of scope here, but see the [inverted branch](https://github.com/VirtCode/hypr-dynamic-cursors/tree/inverted))
- [ ] hyprcursor magified shape
If anything here sounds interesting to you, don't hesitate to contribute.
@ -161,15 +161,15 @@ plugin:dynamic-cursors {
```
## performance
> **TL;DR:** Hardware cursor performance is about the same as if an animated cursor shape was shown whenever you move your mouse. Sofware cursor performance is not impacted. When the cursor is magnified during a shake, the compositor will temporarily switch to software cursors.
Depending on your hyprland configuration, this plugin can have a different performance impact. Different behaviours have a different impact, but it mainly depends on whether you are using software or hardware cursors:
**Software Cursors**: No (additional) performance impact. <br>
Rotating the cursor can be done in the same draw call that is used to draw the cursor anyways, so there is no additional performance impact. Note however that software cursors in of themselves are not really efficient.
**Hardware Cursors**: Medium performance impact. <br>
To rotate the cursor smoothly, the cursor shape needs to be changed quite often. This is not exactly compatible with how hardware cursors are intended to work. With this plugin, the overhead of changing the cursor shape can be noticed more often, making it use more resources. Compared to normal hardware cursors, there is a big performance hit, but it is still more efficient than using software cursor though.
Note that if the cusor is currently *being* magnified, software cursors will be used, as hardware cursors have a hardware size limit.
To rotate the cursor smoothly, the cursor shape needs to be changed quite often. This is not exactly compatible with how hardware cursors are intended to work. Thus performance can be compared to how an animated cursor shape would be rendered, every time the cursor is not stationary. It is however still more performant than software cursors. <br> Another limitation of hardware cursors are the size they are rendered at. This means that when the cursor is being magnified, software cursors will be used temporarily.
If you have any ideas to improve performance, let me know!

View file

@ -1,5 +1,5 @@
{
description = "A plugin to make your hyprland cursor more realistic, also adds shake to find.";
description = "a plugin to make your hyprland cursor more realistic, also adds shake to find";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";

View file

@ -1,5 +1,6 @@
#include "globals.hpp"
#include "src/debug/Log.hpp"
#include "src/managers/eventLoop/EventLoopManager.hpp"
#include <cmath>
#include <cstdlib>
@ -22,6 +23,33 @@
#include "cursor.hpp"
#include "renderer.hpp"
void tickRaw(SP<CEventLoopTimer> self, void* data) {
static auto* const* PENABLED = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_ENABLED)->getDataStaticPtr();
if (**PENABLED && g_pDynamicCursors)
g_pDynamicCursors->onTick(g_pPointerManager.get());
const int TIMEOUT = g_pHyprRenderer->m_pMostHzMonitor ? 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate : 16;
self->updateTimeout(std::chrono::milliseconds(TIMEOUT));
}
CDynamicCursors::CDynamicCursors() {
this->tick = SP<CEventLoopTimer>(new CEventLoopTimer(std::chrono::microseconds(500), tickRaw, nullptr));
g_pEventLoopManager->addTimer(this->tick);
}
CDynamicCursors::~CDynamicCursors() {
// stop and deallocate timer
g_pEventLoopManager->removeTimer(this->tick);
this->tick.reset();
// release software lock
if (zoomSoftware) {
g_pPointerManager->unlockSoftwareAll();
zoomSoftware = false;
}
}
/*
Reimplements rendering of the software cursor.
Is also largely identical to hyprlands impl, but uses our custom rendering to rotate the cursor.

View file

@ -4,6 +4,7 @@
#include <hyprland/src/managers/PointerManager.hpp>
#undef private
#include <hyprutils/math/Vector2D.hpp>
#include <hyprland/src/managers/eventLoop/EventLoopManager.hpp>
#include "mode/ModeRotate.hpp"
#include "mode/ModeTilt.hpp"
@ -11,6 +12,9 @@
class CDynamicCursors {
public:
CDynamicCursors();
~CDynamicCursors();
/* hook on onCursorMoved */
void onCursorMoved(CPointerManager* pointers);
/* called on tick */
@ -26,6 +30,8 @@ class CDynamicCursors {
bool setHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, wlr_buffer* buf);
private:
SP<CEventLoopTimer> tick;
// current angle of the cursor in radiants
double angle;
// current zoom value of the cursor

View file

@ -19,5 +19,3 @@
#define CONFIG_HW_DEBUG "plugin:dynamic-cursors:hw_debug"
inline HANDLE PHANDLE = nullptr;
inline wl_event_source* tick = nullptr;

View file

@ -8,6 +8,7 @@
#include "globals.hpp"
#include "cursor.hpp"
#include "src/debug/Log.hpp"
#include "src/managers/PointerManager.hpp"
typedef void (*origRenderSofwareCursorsFor)(void*, SP<CMonitor>, timespec*, CRegion&, std::optional<Vector2D>);
@ -55,56 +56,30 @@ void hkOnCursorMoved(void* thisptr) {
else return (*(origOnCursorMoved)g_pOnCursorMovedHook->m_pOriginal)(thisptr);
}
int onTick(void* data) {
static auto* const* PENABLED = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_ENABLED)->getDataStaticPtr();
/* hooks a function hook */
CFunctionHook* hook(std::string name, void* function) {
auto names = HyprlandAPI::findFunctionsByName(PHANDLE, name);
auto match = names.at(0);
if (**PENABLED) g_pDynamicCursors->onTick(g_pPointerManager.get());
Debug::log(LOG, "[dynamic-cursors] hooking on {} for {}", match.signature, name);
const int TIMEOUT = g_pHyprRenderer->m_pMostHzMonitor ? 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate : 16;
wl_event_source_timer_update(tick, TIMEOUT);
auto hook = HyprlandAPI::createFunctionHook(PHANDLE, match.address, function);
hook->hook();
return 0;
return hook;
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
const std::string HASH = __hyprland_api_get_hash();
// check that header version aligns with running version
const std::string HASH = __hyprland_api_get_hash();
if (HASH != GIT_COMMIT_HASH) {
HyprlandAPI::addNotification(PHANDLE, "[dynamic-cursors] Mismatched headers! Can't proceed.", CColor{1.0, 0.2, 0.2, 1.0}, 5000);
throw std::runtime_error("[dynamic-cursors] Version mismatch");
HyprlandAPI::addNotification(PHANDLE, "[dynamic-cursors] Failed to load, mismatched headers!", CColor{1.0, 0.2, 0.2, 1.0}, 5000);
throw std::runtime_error("version mismatch");
}
g_pDynamicCursors = std::make_unique<CDynamicCursors>();
static const auto RENDER_SOFTWARE_CURSORS_FOR_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderSoftwareCursorsFor");
g_pRenderSoftwareCursorsForHook = HyprlandAPI::createFunctionHook(PHANDLE, RENDER_SOFTWARE_CURSORS_FOR_METHODS[0].address, (void*) &hkRenderSoftwareCursorsFor);
g_pRenderSoftwareCursorsForHook->hook();
static const auto DAMAGE_IF_SOFTWARE_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "damageIfSoftware");
g_pDamageIfSoftwareHook = HyprlandAPI::createFunctionHook(PHANDLE, DAMAGE_IF_SOFTWARE_METHODS[0].address, (void*) &hkDamageIfSoftware);
g_pDamageIfSoftwareHook->hook();
static const auto RENDER_HW_CURSOR_BUFFER_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderHWCursorBuffer");
g_pRenderHWCursorBufferHook = HyprlandAPI::createFunctionHook(PHANDLE, RENDER_HW_CURSOR_BUFFER_METHODS[0].address, (void*) &hkRenderHWCursorBuffer);
g_pRenderHWCursorBufferHook->hook();
static const auto SET_HW_CURSOR_BUFFER_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "setHWCursorBuffer");
g_pSetHWCursorBufferHook = HyprlandAPI::createFunctionHook(PHANDLE, SET_HW_CURSOR_BUFFER_METHODS[0].address, (void*) &hkSetHWCursorBuffer);
g_pSetHWCursorBufferHook->hook();
static const auto ON_CURSOR_MOVED_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "onCursorMoved");
g_pOnCursorMovedHook = HyprlandAPI::createFunctionHook(PHANDLE, ON_CURSOR_MOVED_METHODS[0].address, (void*) &hkOnCursorMoved);
g_pOnCursorMovedHook->hook();
// for some damn reason this tick handler does not work
// static auto P_TICK = HyprlandAPI::registerCallbackDynamic(PHANDLE, "tick", [](void* self, SCallbackInfo& info, std::any data) { std::cout << "ticking?" << "\n"; });
// so we have to use this (stolen from hyprtrails)
tick = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, &onTick, nullptr);
wl_event_source_timer_update(tick, 1);
// setup config
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_ENABLED, Hyprlang::INT{1});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_MODE, Hyprlang::STRING{"tilt"});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_THRESHOLD, Hyprlang::INT{2});
@ -126,12 +101,25 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
HyprlandAPI::reloadConfig();
return {"dynamic-cursors", "make your cursor more realistic", "Virt", "0.1"};
// init things
g_pDynamicCursors = std::make_unique<CDynamicCursors>();
// try hooking
try {
g_pRenderSoftwareCursorsForHook = hook("renderSoftwareCursorsFor", (void*) &hkRenderSoftwareCursorsFor);
g_pDamageIfSoftwareHook = hook("damageIfSoftware", (void*) &hkDamageIfSoftware);
g_pRenderHWCursorBufferHook = hook("renderHWCursorBuffer", (void*) &hkRenderHWCursorBuffer);
g_pSetHWCursorBufferHook = hook("setHWCursorBuffer", (void*) &hkSetHWCursorBuffer);
g_pOnCursorMovedHook = hook("onCursorMoved", (void*) &hkOnCursorMoved);
} catch (...) {
HyprlandAPI::addNotification(PHANDLE, "[dynamic-cursors] Failed to load, hooks could not be made!", CColor{1.0, 0.2, 0.2, 1.0}, 5000);
throw std::runtime_error("hooks failed");
}
return {"dynamic-cursors", "a plugin to make your hyprland cursor more realistic, also adds shake to find", "Virt", "0.1"};
}
APICALL EXPORT void PLUGIN_EXIT() {
// ...
}
APICALL EXPORT void PLUGIN_EXIT() { }
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {