From 91c03dea2ebfe34dbe64e54921801829dfe23ea8 Mon Sep 17 00:00:00 2001 From: Virt <41426325+VirtCode@users.noreply.github.com> Date: Sun, 30 Jun 2024 22:38:08 +0200 Subject: [PATCH] fix: better error handling and event loop --- README.md | 8 +++--- flake.nix | 2 +- src/cursor.cpp | 28 +++++++++++++++++++ src/cursor.hpp | 6 +++++ src/globals.hpp | 2 -- src/main.cpp | 72 +++++++++++++++++++++---------------------------- 6 files changed, 69 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index d4bdced..7920a5c 100644 --- a/README.md +++ b/README.md @@ -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.
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.
-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.
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! diff --git a/flake.nix b/flake.nix index d91a1fe..d33448c 100644 --- a/flake.nix +++ b/flake.nix @@ -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"; diff --git a/src/cursor.cpp b/src/cursor.cpp index 60018df..8539502 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -1,5 +1,6 @@ #include "globals.hpp" #include "src/debug/Log.hpp" +#include "src/managers/eventLoop/EventLoopManager.hpp" #include #include @@ -22,6 +23,33 @@ #include "cursor.hpp" #include "renderer.hpp" +void tickRaw(SP 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(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. diff --git a/src/cursor.hpp b/src/cursor.hpp index 52dec73..ed377f3 100644 --- a/src/cursor.hpp +++ b/src/cursor.hpp @@ -4,6 +4,7 @@ #include #undef private #include +#include #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 state, wlr_buffer* buf); private: + SP tick; + // current angle of the cursor in radiants double angle; // current zoom value of the cursor diff --git a/src/globals.hpp b/src/globals.hpp index 7b94a4d..592478e 100644 --- a/src/globals.hpp +++ b/src/globals.hpp @@ -19,5 +19,3 @@ #define CONFIG_HW_DEBUG "plugin:dynamic-cursors:hw_debug" inline HANDLE PHANDLE = nullptr; - -inline wl_event_source* tick = nullptr; diff --git a/src/main.cpp b/src/main.cpp index 1761726..79f12d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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, timespec*, CRegion&, std::optional); @@ -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(); - - 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(); + + // 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() {