diff --git a/README.md b/README.md index 2f83dba..731bcae 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This mode tries recreating the stretching and squishing that is done to moving o https://github.com/VirtCode/hypr-dynamic-cursors/assets/41426325/7b8289e7-9dd2-4b57-b406-4fa28779a260 ### shake to find -The plugin supports shake to find, akin to how KDE Plasma, MacOS, etc. do it. It can also be extensively configured and is enabled by default. It also supports using [hyprcursor](https://github.com/hyprwm/hyprcursor) for high resolution cursor images. If you only want shake to find, and no weird cursor behaviour, you can disable the above modes with the mode `none`. +The plugin supports shake to find, akin to how KDE Plasma, MacOS, etc. do it. It can also be extensively configured and is enabled by default. It also supports using [hyprcursor](https://github.com/hyprwm/hyprcursor) for high resolution cursor images. The magnification can also be triggered as a dispatcher instead of on shake. If you only want shake to find, and no weird cursor behaviour, you can disable the above modes with the mode `none`. https://github.com/user-attachments/assets/1346101e-4e62-4ba2-a1df-5940e0706514 @@ -252,7 +252,7 @@ This plugin can expose cursor shake events via IPC. This behaviour must be expli The following events with the described arguments are available, when IPC is enabled: - `shakestart`: fired when a shake is detected. -- `shakeupdate`: fired on frame during the shake, has arguments `x,y,trail,diagonal,zoom`: +- `shakeupdate`: fired on frame during the shake, has arguments `x, y, trail, diagonal, zoom`: - `x`, `y` are the current cursor position. - `trail` and `diagonal` are two floats, the first indicating the distance the mouse travelled, and second the diagonal this movement was within. Their quotient `trail / diagonal` indicates how intense the shaking is. - `zoom` is the current cursor magnification level, as currently shown by this plugin, depending on the shake configuration. It is also interpolated smoothly. @@ -274,6 +274,12 @@ As mentioned, there are some caveats to it. Here are the most common ones: - **Hyprland lags when loading the plugin** - Loading a set of high resolution cursor shapes takes some time. This means your session will freeze while the theme is being loaded. You can try setting a custom / lower `resolution` option (see config). - **Blurred at very large sizes** - The high resolution cursors are preloaded at a fixed size. If you magnify your cursor beyond this size, your cursors will look blurry. You can increase the preload size with the `resolution` option (see config), at the expense of some memory and higher loading times. +### dispatchers +This plugin has a couple of dispatchers to trigger certain effects with a keybind. Here's a list: +- `plugin:dynamic-cursors:magnify` with arguments `duration?, size?` triggers cursor magnification like on a shake + - `duration` (optional): overrides duration in milliseconds to stay magnified + - `size` (optional): overrides magnification factor + ## 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. diff --git a/src/config/config.cpp b/src/config/config.cpp index 03e8396..fa97cd6 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,5 +1,7 @@ #include "../globals.hpp" #include "../cursor.hpp" +#include "SharedDefs.hpp" +#include "debug/Log.hpp" #include "src/render/Renderer.hpp" #include "config.hpp" #include @@ -74,3 +76,17 @@ void addRulesConfig() { void finishConfig() { HyprlandAPI::reloadConfig(); } + +void addDispatcher(std::string name, std::function(Hyprutils::String::CVarList)> handler) { + HyprlandAPI::addDispatcherV2(PHANDLE, NAMESPACE + name, [=](std::string in) { + auto error = handler(CVarList(in)); + + SDispatchResult result; + if (error.has_value()) { + Debug::log(ERR, "[dynamic-cursors] dispatcher {} recieved invalid args: {}", name, error.value()); + result.error = error.value(); + } + + return result; + }); +} diff --git a/src/config/config.hpp b/src/config/config.hpp index 42a4d7e..7203e90 100644 --- a/src/config/config.hpp +++ b/src/config/config.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include "ShapeRule.hpp" #define NAMESPACE "plugin:dynamic-cursors:" @@ -37,6 +39,8 @@ #define CONFIG_SHAPERULE "shaperule" +#define CONFIG_DISPATCHER_MAGNIFY "magnify" + /* is the plugin enabled */ bool isEnabled(); @@ -59,3 +63,6 @@ void* const* getConfig(std::string name); /* get static pointer a hyprland config value */ void* const* getHyprlandConfig(std::string name); + +/* adds a dispatcher */ +void addDispatcher(std::string name, std::function(Hyprutils::String::CVarList)> handler); diff --git a/src/cursor.cpp b/src/cursor.cpp index f5e6628..212eeb7 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -516,3 +516,10 @@ void CDynamicCursors::calculate(EModeUpdate type) { void CDynamicCursors::setMove() { isMove = true; } + +void CDynamicCursors::dispatchMagnify(std::optional duration, std::optional size) { + static auto* const* PSHAKE = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE); + if (!**PSHAKE) return; + + shake.force(duration, size); +} diff --git a/src/cursor.hpp b/src/cursor.hpp index 9346d9f..5347448 100644 --- a/src/cursor.hpp +++ b/src/cursor.hpp @@ -43,6 +43,8 @@ class CDynamicCursors { /* hook on move, indicate that next onCursorMoved is actual move */ void setMove(); + void dispatchMagnify(std::optional duration, std::optional size); + private: SP tick; diff --git a/src/main.cpp b/src/main.cpp index 2768a79..cfb0a8a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include "globals.hpp" @@ -165,6 +167,31 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { throw std::runtime_error("hooks failed"); } + // add dispatchers + addDispatcher(CONFIG_DISPATCHER_MAGNIFY, [&](CVarList args) { + std::optional error; + + std::optional duration; + std::optional size; + + try { + auto it = args.begin(); + if (it != args.end() && *it != "") { + duration = std::stoi(*it); + + it++; + if (it != args.end()) + size = std::stof(*it); + } + } catch (...) { + error = "types did not match"; + } + + g_pDynamicCursors->dispatchMagnify(duration, size); + + return error; + }); + return {"dynamic-cursors", "a plugin to make your hyprland cursor more realistic, also adds shake to find", "Virt", "0.1"}; } diff --git a/src/other/Shake.cpp b/src/other/Shake.cpp index 03cd92d..16a0392 100644 --- a/src/other/Shake.cpp +++ b/src/other/Shake.cpp @@ -110,6 +110,15 @@ double CShake::update(Vector2D pos) { return this->zoom->value(); } +void CShake::force(std::optional duration, std::optional size) { + static auto* const* PTIMEOUT = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_TIMEOUT); + static auto* const* PBASE = (Hyprlang::FLOAT* const*) getConfig(CONFIG_SHAKE_BASE); + + started = true; + *this->zoom = size.value_or(**PBASE); + this->end = steady_clock::now() + milliseconds(duration.value_or(**PTIMEOUT)); +} + void CShake::warp(Vector2D old, Vector2D pos) { auto delta = pos - old; diff --git a/src/other/Shake.hpp b/src/other/Shake.hpp index fe8b8c7..3151e2b 100644 --- a/src/other/Shake.hpp +++ b/src/other/Shake.hpp @@ -1,4 +1,5 @@ #include "helpers/AnimatedVariable.hpp" +#include #include #include #include @@ -20,6 +21,9 @@ class CShake { /* called when a cursor warp has happened (to avoid magnifying on warps) */ void warp(Vector2D old, Vector2D pos); + /* force magnification regardless of speed now */ + void force(std::optional duration, std::optional size); + private: /* tracks whether the current shake has already been announced in the ipc */ bool ipc = false;