mirror of
https://github.com/virtcode/hypr-dynamic-cursors
synced 2025-09-19 08:03:21 +02:00
fix: ignore warps on modes and shake
This commit is contained in:
parent
553dc93cc0
commit
55e8b316ce
14 changed files with 80 additions and 9 deletions
18
README.md
18
README.md
|
@ -98,10 +98,10 @@ plugin:dynamic-cursors {
|
|||
enabled = true
|
||||
|
||||
# sets the cursor behaviour, supports these values:
|
||||
# tilt - tilt the cursor based on x-velocity
|
||||
# rotate - rotate the cursor based on movement direction
|
||||
# tilt - tilt the cursor based on x-velocity
|
||||
# rotate - rotate the cursor based on movement direction
|
||||
# stretch - stretch the cursor shape based on direction and velocity
|
||||
# none - do not change the cursors behaviour
|
||||
# none - do not change the cursors behaviour
|
||||
mode = tilt
|
||||
|
||||
# minimum angle difference in degrees after which the shape is changed
|
||||
|
@ -175,7 +175,7 @@ plugin:dynamic-cursors {
|
|||
base = 4.0
|
||||
# magnification increase per second when continuing to shake
|
||||
speed = 4.0
|
||||
# factor the speed is influenced by the current shake intensitiy
|
||||
# how much the speed is influenced by the current shake intensitiy
|
||||
influence = 0.0
|
||||
|
||||
# maximal magnification the cursor can reach
|
||||
|
@ -209,7 +209,7 @@ shaperule = shape-name, mode (optional), property: value, property: value, ...
|
|||
- `property: value`: At the end of the rule follow zero or more property-value pairs. These are config values that will be overridden if this rule is active. Only config values from the sections `rotate`, `tilt`, `stretch` as seen above can be used.
|
||||
|
||||
Here are a few example rules to get you started:
|
||||
```
|
||||
```ini
|
||||
plugin:dynamic-cursors {
|
||||
# apply a 90° offset in rotate mode to the text shape
|
||||
shaperule = text, rotate:offset: 90
|
||||
|
@ -225,12 +225,12 @@ plugin:dynamic-cursors {
|
|||
### ipc
|
||||
This plugin can expose cursor shake events via IPC. This behaviour must be explicitly enabled via the `plugin:dynamic-cursors:shake:ipc` option, as it will spam the socket quite a bit during a shake. These events will appear on [Hyprland's event socket](https://wiki.hyprland.org/IPC/#xdg_runtime_dirhyprhissocket2sock).
|
||||
|
||||
The following events with the described arguments are available, when ipc is enabled:
|
||||
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`:
|
||||
- `x`, `y` are the current cursor position.
|
||||
- `trail` and `diagonal` are two values indicating the distance the mouse travelled, and the diagonal this movement was within for the last second. Their quotient `trail / diagonal` indicates how intense the shaking is.
|
||||
- `zoom` is the current cursor magnification level, as currently shown by this plugin, as customized in the shake configuration.
|
||||
- `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.
|
||||
- `shakeend`: fired when a shake has ended (after the `timeout`)
|
||||
|
||||
If you only want the IPC events and not the plugin actually changing the cursor size, you can set the properties `base` to `1`, `speed`, `influence` and `timeout` to `0` in the `plugin:dynamic-cursors:shake` section such that the cursor is not magified during the shake.
|
||||
|
@ -260,6 +260,8 @@ To work on this plugin, you can clone this repository and use the Makefile to bu
|
|||
make load
|
||||
```
|
||||
|
||||
In some cases when working in a nest, nothing will happen with the plugin loaded. This is because the mouse input is handled differently in a wayland nest. In these cases, set `plugin:dynamic-cursors:ignore_warps` to `false`, to disable warp ignoring, which should fix the issue.
|
||||
|
||||
If you want to debug hardware cursors, this plugin also has an additional configuration option, `plugin:dynamic-cursors:hw_debug` which when true will show where the whole cursor buffer is, and also shows when it is updated.
|
||||
|
||||
Also make sure you disable the plugin on your host session, otherwise your cursor will be rotated twice.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define CONFIG_MODE "mode"
|
||||
#define CONFIG_THRESHOLD "threshold"
|
||||
#define CONFIG_HW_DEBUG "hw_debug"
|
||||
#define CONFIG_IGNORE_WARPS "ignore_warps"
|
||||
|
||||
#define CONFIG_SHAKE "shake:enabled"
|
||||
#define CONFIG_SHAKE_NEAREST "shake:nearest"
|
||||
|
|
|
@ -154,8 +154,10 @@ SP<Aquamarine::IBuffer> CDynamicCursors::renderHardware(CPointerManager* pointer
|
|||
Debug::log(TRACE, "hardware cursor too big! {} > {}", pointers->currentCursorImage.size, maxSize);
|
||||
return nullptr;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
maxSize = targetSize;
|
||||
if (maxSize.x < 16 || maxSize.y < 16) maxSize = {16, 16}; // fix some annoying crashes in nest
|
||||
}
|
||||
|
||||
if (!state->monitor->cursorSwapchain || maxSize != state->monitor->cursorSwapchain->currentOptions().size || state->monitor->cursorSwapchain->currentOptions().length != 3) {
|
||||
|
||||
|
@ -317,6 +319,9 @@ bool CDynamicCursors::setHardware(CPointerManager* pointers, SP<CPointerManager:
|
|||
Handles cursor move events.
|
||||
*/
|
||||
void CDynamicCursors::onCursorMoved(CPointerManager* pointers) {
|
||||
static auto* const* PSHAKE = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE);
|
||||
static auto* const* PIGNORE_WARPS = (Hyprlang::INT* const*) getConfig(CONFIG_IGNORE_WARPS);
|
||||
|
||||
if (!pointers->hasCursor())
|
||||
return;
|
||||
|
||||
|
@ -332,7 +337,18 @@ void CDynamicCursors::onCursorMoved(CPointerManager* pointers) {
|
|||
m->output->moveCursor(CURSORPOS);
|
||||
}
|
||||
|
||||
// ignore warp
|
||||
if (!isMove && **PIGNORE_WARPS) {
|
||||
auto mode = this->currentMode();
|
||||
if (mode) mode->warp(lastPos, pointers->pointerPos);
|
||||
|
||||
if (**PSHAKE) shake.warp(lastPos, pointers->pointerPos);
|
||||
}
|
||||
|
||||
calculate(MOVE);
|
||||
|
||||
isMove = false;
|
||||
lastPos = pointers->pointerPos;
|
||||
}
|
||||
|
||||
void CDynamicCursors::setShape(const std::string& shape) {
|
||||
|
@ -426,3 +442,7 @@ void CDynamicCursors::calculate(EModeUpdate type) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDynamicCursors::setMove() {
|
||||
isMove = true;
|
||||
}
|
||||
|
|
|
@ -35,12 +35,16 @@ class CDynamicCursors {
|
|||
/* hook on setCursorSoftware */
|
||||
void unsetShape();
|
||||
|
||||
/* hook on move, indicate that next onCursorMoved is actual move */
|
||||
void setMove();
|
||||
|
||||
private:
|
||||
SP<CEventLoopTimer> tick;
|
||||
|
||||
// current state of the cursor
|
||||
SModeResult resultMode;
|
||||
double resultShake;
|
||||
Vector2D lastPos; // used for warp compensation
|
||||
|
||||
SModeResult resultShown;
|
||||
|
||||
|
@ -57,6 +61,9 @@ class CDynamicCursors {
|
|||
// shake
|
||||
CShake shake;
|
||||
|
||||
/* is set true if a genuine move is being performed, and will be reset to false after onCursorMoved */
|
||||
bool isMove = false;
|
||||
|
||||
// calculates the current angle of the cursor, and changes the cursor shape
|
||||
void calculate(EModeUpdate type);
|
||||
};
|
||||
|
|
|
@ -67,6 +67,13 @@ void hkSetCursorSurface(void* thisptr, SP<CWLSurface> surf, const Vector2D& hots
|
|||
(*(origSetCursorSurface)g_pSetCursorSurfaceHook->m_pOriginal)(thisptr, surf, hotspot);
|
||||
}
|
||||
|
||||
typedef void (*origMove)(void*, const Vector2D&);
|
||||
inline CFunctionHook* g_pMoveHook = nullptr;
|
||||
void hkMove(void* thisptr, const Vector2D& deltaLogical) {
|
||||
if (isEnabled()) g_pDynamicCursors->setMove();
|
||||
(*(origMove)g_pMoveHook->m_pOriginal)(thisptr, deltaLogical);
|
||||
}
|
||||
|
||||
/* hooks a function hook */
|
||||
CFunctionHook* hook(std::string name, std::string object, void* function) {
|
||||
auto names = HyprlandAPI::findFunctionsByName(PHANDLE, name);
|
||||
|
@ -126,6 +133,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
addShapeConfig(CONFIG_ROTATE_OFFSET, 0.0f);
|
||||
|
||||
addConfig(CONFIG_HW_DEBUG, false);
|
||||
addConfig(CONFIG_IGNORE_WARPS, true);
|
||||
|
||||
addRulesConfig();
|
||||
finishConfig();
|
||||
|
@ -140,6 +148,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
g_pRenderHWCursorBufferHook = hook("renderHWCursorBuffer", "CPointerManager", (void*) &hkRenderHWCursorBuffer);
|
||||
g_pSetHWCursorBufferHook = hook("setHWCursorBuffer", "CPointerManager", (void*) &hkSetHWCursorBuffer);
|
||||
g_pOnCursorMovedHook = hook("onCursorMoved", "CPointerManager", (void*) &hkOnCursorMoved);
|
||||
g_pMoveHook = hook("moveER", "CPointerManager", (void*) &hkMove); // this `ER` makes it faster because `move` is very generic
|
||||
|
||||
g_pSetCursorFromNameHook = hook("setCursorFromName", "CCursorManager", (void*) &hkSetCursorFromName);
|
||||
g_pSetCursorSurfaceHook = hook("setCursorSurface", "CCursorManager", (void*) &hkSetCursorSurface);
|
||||
|
|
|
@ -11,4 +11,6 @@ class IMode {
|
|||
virtual EModeUpdate strategy() = 0;
|
||||
/* updates the calculations and returns the new angle */
|
||||
virtual SModeResult update(Vector2D pos) = 0;
|
||||
/* called on warp, an update will be sent afterwards (probably) */
|
||||
virtual void warp(Vector2D old, Vector2D pos) = 0;
|
||||
};
|
||||
|
|
|
@ -45,3 +45,7 @@ SModeResult CModeRotate::update(Vector2D pos) {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CModeRotate::warp(Vector2D old, Vector2D pos) {
|
||||
end += (pos - old);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ class CModeRotate : public IMode {
|
|||
public:
|
||||
virtual EModeUpdate strategy();
|
||||
virtual SModeResult update(Vector2D pos);
|
||||
virtual void warp(Vector2D old, Vector2D pos);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -40,3 +40,10 @@ SModeResult CModeStretch::update(Vector2D pos) {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CModeStretch::warp(Vector2D old, Vector2D pos) {
|
||||
auto delta = pos - old;
|
||||
|
||||
for (auto& sample : samples)
|
||||
sample += delta;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ class CModeStretch : public IMode {
|
|||
public:
|
||||
virtual EModeUpdate strategy();
|
||||
virtual SModeResult update(Vector2D pos);
|
||||
virtual void warp(Vector2D old, Vector2D pos);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -30,3 +30,10 @@ SModeResult CModeTilt::update(Vector2D pos) {
|
|||
result.rotation = activation(function, limit, speed) * (PI / 3); // 120° in both directions
|
||||
return result;
|
||||
}
|
||||
|
||||
void CModeTilt::warp(Vector2D old, Vector2D pos) {
|
||||
auto delta = pos - old;
|
||||
|
||||
for (auto& sample : samples)
|
||||
sample += delta;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ class CModeTilt : public IMode {
|
|||
public:
|
||||
virtual EModeUpdate strategy();
|
||||
virtual SModeResult update(Vector2D pos);
|
||||
virtual void warp(Vector2D old, Vector2D pos);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -111,3 +111,10 @@ double CShake::update(Vector2D pos) {
|
|||
|
||||
return this->zoom.value();
|
||||
}
|
||||
|
||||
void CShake::warp(Vector2D old, Vector2D pos) {
|
||||
auto delta = pos - old;
|
||||
|
||||
for (auto& sample : samples)
|
||||
sample += delta;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ class CShake {
|
|||
|
||||
/* calculates the new zoom factor for the current pos */
|
||||
double update(Vector2D pos);
|
||||
/* called when a cursor warp has happened (to avoid magnifying on warps) */
|
||||
void warp(Vector2D old, Vector2D pos);
|
||||
|
||||
private:
|
||||
/* tracks whether the current shake has already been announced in the ipc */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue