mirror of
https://github.com/virtcode/hypr-dynamic-cursors
synced 2025-09-19 16:13:21 +02:00
fix: better error handling and event loop
This commit is contained in:
parent
17f8bc1c62
commit
91c03dea2e
6 changed files with 69 additions and 49 deletions
|
@ -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)
|
- [ ] per-shape length and starting angle (if possible)
|
||||||
- [X] cursor shake to find
|
- [X] cursor shake to find
|
||||||
- [ ] overdue refactoring (wait for aquamarine merge)
|
- [ ] 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
|
- [ ] hyprcursor magified shape
|
||||||
|
|
||||||
If anything here sounds interesting to you, don't hesitate to contribute.
|
If anything here sounds interesting to you, don't hesitate to contribute.
|
||||||
|
@ -161,15 +161,15 @@ plugin:dynamic-cursors {
|
||||||
```
|
```
|
||||||
|
|
||||||
## performance
|
## 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:
|
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>
|
**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.
|
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>
|
**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.
|
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.
|
||||||
|
|
||||||
Note that if the cusor is currently *being* magnified, software cursors will be used, as hardware cursors have a hardware size limit.
|
|
||||||
|
|
||||||
If you have any ideas to improve performance, let me know!
|
If you have any ideas to improve performance, let me know!
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "globals.hpp"
|
#include "globals.hpp"
|
||||||
#include "src/debug/Log.hpp"
|
#include "src/debug/Log.hpp"
|
||||||
|
#include "src/managers/eventLoop/EventLoopManager.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -22,6 +23,33 @@
|
||||||
#include "cursor.hpp"
|
#include "cursor.hpp"
|
||||||
#include "renderer.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.
|
Reimplements rendering of the software cursor.
|
||||||
Is also largely identical to hyprlands impl, but uses our custom rendering to rotate the cursor.
|
Is also largely identical to hyprlands impl, but uses our custom rendering to rotate the cursor.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <hyprland/src/managers/PointerManager.hpp>
|
#include <hyprland/src/managers/PointerManager.hpp>
|
||||||
#undef private
|
#undef private
|
||||||
#include <hyprutils/math/Vector2D.hpp>
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
|
#include <hyprland/src/managers/eventLoop/EventLoopManager.hpp>
|
||||||
|
|
||||||
#include "mode/ModeRotate.hpp"
|
#include "mode/ModeRotate.hpp"
|
||||||
#include "mode/ModeTilt.hpp"
|
#include "mode/ModeTilt.hpp"
|
||||||
|
@ -11,6 +12,9 @@
|
||||||
|
|
||||||
class CDynamicCursors {
|
class CDynamicCursors {
|
||||||
public:
|
public:
|
||||||
|
CDynamicCursors();
|
||||||
|
~CDynamicCursors();
|
||||||
|
|
||||||
/* hook on onCursorMoved */
|
/* hook on onCursorMoved */
|
||||||
void onCursorMoved(CPointerManager* pointers);
|
void onCursorMoved(CPointerManager* pointers);
|
||||||
/* called on tick */
|
/* called on tick */
|
||||||
|
@ -26,6 +30,8 @@ class CDynamicCursors {
|
||||||
bool setHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, wlr_buffer* buf);
|
bool setHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, wlr_buffer* buf);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SP<CEventLoopTimer> tick;
|
||||||
|
|
||||||
// current angle of the cursor in radiants
|
// current angle of the cursor in radiants
|
||||||
double angle;
|
double angle;
|
||||||
// current zoom value of the cursor
|
// current zoom value of the cursor
|
||||||
|
|
|
@ -19,5 +19,3 @@
|
||||||
#define CONFIG_HW_DEBUG "plugin:dynamic-cursors:hw_debug"
|
#define CONFIG_HW_DEBUG "plugin:dynamic-cursors:hw_debug"
|
||||||
|
|
||||||
inline HANDLE PHANDLE = nullptr;
|
inline HANDLE PHANDLE = nullptr;
|
||||||
|
|
||||||
inline wl_event_source* tick = nullptr;
|
|
||||||
|
|
70
src/main.cpp
70
src/main.cpp
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "globals.hpp"
|
#include "globals.hpp"
|
||||||
#include "cursor.hpp"
|
#include "cursor.hpp"
|
||||||
|
#include "src/debug/Log.hpp"
|
||||||
#include "src/managers/PointerManager.hpp"
|
#include "src/managers/PointerManager.hpp"
|
||||||
|
|
||||||
typedef void (*origRenderSofwareCursorsFor)(void*, SP<CMonitor>, timespec*, CRegion&, std::optional<Vector2D>);
|
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);
|
else return (*(origOnCursorMoved)g_pOnCursorMovedHook->m_pOriginal)(thisptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int onTick(void* data) {
|
/* hooks a function hook */
|
||||||
static auto* const* PENABLED = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_ENABLED)->getDataStaticPtr();
|
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;
|
auto hook = HyprlandAPI::createFunctionHook(PHANDLE, match.address, function);
|
||||||
wl_event_source_timer_update(tick, TIMEOUT);
|
hook->hook();
|
||||||
|
|
||||||
return 0;
|
return hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
PHANDLE = handle;
|
PHANDLE = handle;
|
||||||
|
|
||||||
const std::string HASH = __hyprland_api_get_hash();
|
|
||||||
|
|
||||||
// check that header version aligns with running version
|
// check that header version aligns with running version
|
||||||
|
const std::string HASH = __hyprland_api_get_hash();
|
||||||
if (HASH != GIT_COMMIT_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);
|
HyprlandAPI::addNotification(PHANDLE, "[dynamic-cursors] Failed to load, mismatched headers!", CColor{1.0, 0.2, 0.2, 1.0}, 5000);
|
||||||
throw std::runtime_error("[dynamic-cursors] Version mismatch");
|
throw std::runtime_error("version mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
g_pDynamicCursors = std::make_unique<CDynamicCursors>();
|
// setup config
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_ENABLED, Hyprlang::INT{1});
|
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_ENABLED, Hyprlang::INT{1});
|
||||||
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_MODE, Hyprlang::STRING{"tilt"});
|
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_MODE, Hyprlang::STRING{"tilt"});
|
||||||
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_THRESHOLD, Hyprlang::INT{2});
|
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_THRESHOLD, Hyprlang::INT{2});
|
||||||
|
@ -126,13 +101,26 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
|
|
||||||
HyprlandAPI::reloadConfig();
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
APICALL EXPORT void PLUGIN_EXIT() {
|
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() { }
|
||||||
|
|
||||||
// Do NOT change this function.
|
// Do NOT change this function.
|
||||||
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||||
return HYPRLAND_API_VERSION;
|
return HYPRLAND_API_VERSION;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue