mirror of
https://github.com/virtcode/hypr-dynamic-cursors
synced 2025-09-19 08:03:21 +02:00
feat: inverted hardware cursor support
This commit is contained in:
parent
ec4db58ade
commit
d172611292
8 changed files with 53 additions and 40 deletions
15
README.md
15
README.md
|
@ -5,6 +5,10 @@ Why did I implement this again?
|
|||
|
||||
Inspired by KDE, it also supports shake to find, to enlarge the cursor when it is shaken so it is easier to find it. It can be enabled separately or together with one simulation mode.
|
||||
|
||||
|
||||
> [!WARNING]
|
||||
> This branch is experimental, poorly implemented, and not maintained. Use it at your own digression. Also, inverted hardware cursors are currently broken.
|
||||
|
||||
### simulation modes
|
||||
The plugin supports a few different modes. They can all be customized induvidually.
|
||||
|
||||
|
@ -31,9 +35,9 @@ https://github.com/VirtCode/hypr-dynamic-cursors/assets/41426325/9ff64a9b-64e5-4
|
|||
### inverted cursor (experimental)
|
||||
You can also finally have an inverted cursor with this plugin. This is similar to the inverted cursor theme found in MS Windows.
|
||||
|
||||
**Note:** Inverted cursors have about the same performance impact as a *basic* screen shader. They are also only supported as software cursors.
|
||||
**Note:** Inverted cursors have about the same performance impact as a *basic* screen shader. They are also currently broken on nvidia.
|
||||
|
||||
INSERT VIDEO HERE
|
||||
https://github.com/VirtCode/hypr-dynamic-cursors/assets/41426325/b62698d8-d53f-45f2-b36f-2c9752b8f90d
|
||||
|
||||
## state
|
||||
This plugin is still very early in its development. There are also multiple things which may or may not be implemented in the future:
|
||||
|
@ -191,15 +195,14 @@ plugin:dynamic-cursors {
|
|||
ipc = false
|
||||
}
|
||||
|
||||
# enables inverted cursor
|
||||
# this replaces your cursor shape with the inverted colors of the background
|
||||
# by default, this replaces the non transparent parts of your cursor
|
||||
# WARNING: inverted cursors are experimental and have a high performance impact
|
||||
invert = true
|
||||
|
||||
# for when invert = true
|
||||
invert {
|
||||
|
||||
# enables inverted cursor
|
||||
enabled = false
|
||||
|
||||
# shader function that is used on the background color, supports:
|
||||
# invert - take the negative of the color
|
||||
# invert_hue - take the negative of the color and shift hue by 180°
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
#define CONFIG_THRESHOLD "threshold"
|
||||
#define CONFIG_HW_DEBUG "hw_debug"
|
||||
|
||||
#define CONFIG_INVERT "plugin:dynamic-cursors:invert"
|
||||
#define CONFIG_INVERT_SHADER "plugin:dynamic-cursors:invert:shader"
|
||||
#define CONFIG_INVERT_CHROMA "plugin:dynamic-cursors:invert:chroma"
|
||||
#define CONFIG_INVERT_CHROMA_COLOR "plugin:dynamic-cursors:invert:chroma:color"
|
||||
#define CONFIG_INVERT "invert:enabled"
|
||||
#define CONFIG_INVERT_SHADER "invert:shader"
|
||||
#define CONFIG_INVERT_CHROMA "invert:chroma"
|
||||
#define CONFIG_INVERT_CHROMA_COLOR "invert:chroma:color"
|
||||
|
||||
#define CONFIG_SHAKE "shake:enabled"
|
||||
#define CONFIG_SHAKE_NEAREST "shake:nearest"
|
||||
|
|
|
@ -49,24 +49,25 @@ CDynamicCursors::~CDynamicCursors() {
|
|||
}
|
||||
}
|
||||
|
||||
void renderCursorBox(SP<CTexture> texture, CBox box, CRegion& damage, Vector2D hotspot, float zoom) {
|
||||
void renderCursorBox(SP<CTexture> texture, CBox box, CRegion& damage, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint, Vector2D hotspot, float zoom, float stretchAngle, Vector2D stretch, Vector2D offset) {
|
||||
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
|
||||
|
||||
static auto* const* PINVERT = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT);
|
||||
static auto const* PINVERT_MODE = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT_SHADER);
|
||||
static auto* const* PINVERT_CHROMA = (Hyprlang::STRING const*) getConfig(CONFIG_INVERT_CHROMA);
|
||||
static auto const* PINVERT_MODE = (Hyprlang::STRING const*) getConfig(CONFIG_INVERT_SHADER);
|
||||
static auto* const* PINVERT_CHROMA = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT_CHROMA);
|
||||
static auto* const* PINVERT_CHROMA_COLOR = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT_CHROMA_COLOR);
|
||||
|
||||
bool nearest = zoom > 1 && **PNEAREST;
|
||||
|
||||
if (**PINVERT) {
|
||||
int mode = 0;
|
||||
if (!strcmp(*PINVERT_MODE, "invert_hue")) mode = 1;
|
||||
else if (!strcmp(*PINVERT_MODE, "hue")) mode = 2;
|
||||
|
||||
renderCursorTextureInternalWithDamageInverted(texture, &box, &damage, 1.f, hotspot, nearest, mode, **PINVERT_CHROMA, CColor(**PINVERT_CHROMA_COLOR));
|
||||
if (*PINVERT_MODE == std::string("invert_hue")) mode = 1;
|
||||
else if (*PINVERT_MODE == std::string("hue")) mode = 2;
|
||||
|
||||
renderCursorTextureInternalWithDamageInverted(texture, &box, &damage, 1.f, waitTimeline, waitPoint, hotspot, nearest, stretchAngle, stretch, mode, **PINVERT_CHROMA, CColor(**PINVERT_CHROMA_COLOR), offset);
|
||||
} else
|
||||
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.f, hotspot, nearest);
|
||||
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.f, waitTimeline, waitPoint, hotspot, nearest, stretchAngle, stretch);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -74,8 +75,6 @@ Reimplements rendering of the software cursor.
|
|||
Is also largely identical to hyprlands impl, but uses our custom rendering to rotate the cursor.
|
||||
*/
|
||||
void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos) {
|
||||
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
|
||||
static auto* const* PINVERT = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT);
|
||||
|
||||
if (!pointers->hasCursor())
|
||||
return;
|
||||
|
@ -115,7 +114,7 @@ void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMo
|
|||
box.rot = resultShown.rotation;
|
||||
|
||||
// now pass the hotspot to rotate around
|
||||
renderCursorBox(texture, box, damage, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom);
|
||||
renderCursorBox(texture, box, damage, nullptr, 0, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom, resultShown.stretch.angle, resultShown.stretch.magnitude, Vector2D());
|
||||
|
||||
if (pointers->currentCursorImage.surface)
|
||||
pointers->currentCursorImage.surface->resource()->frame(now);
|
||||
|
@ -154,7 +153,7 @@ It is largely copied from hyprland, but adjusted to allow the cursor to be rotat
|
|||
*/
|
||||
SP<Aquamarine::IBuffer> CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
|
||||
static auto* const* PHW_DEBUG = (Hyprlang::INT* const*) getConfig(CONFIG_HW_DEBUG);
|
||||
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
|
||||
static auto* const* PINVERT = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT);
|
||||
|
||||
auto output = state->monitor->output;
|
||||
|
||||
|
@ -263,8 +262,21 @@ SP<Aquamarine::IBuffer> CDynamicCursors::renderHardware(CPointerManager* pointer
|
|||
CBox xbox = {cursorPadding, Vector2D{pointers->currentCursorImage.size / pointers->currentCursorImage.scale * state->monitor->scale * zoom}.round()};
|
||||
xbox.rot = resultShown.rotation;
|
||||
|
||||
// use our custom draw function
|
||||
renderCursorBox(texture, xbox, damage, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom);
|
||||
// calculate correct offset of the offload buffer into the cursor buffer
|
||||
auto offset = Vector2D();
|
||||
if (**PINVERT) {
|
||||
auto PMONITOR = state->monitor.lock();
|
||||
|
||||
const auto HOTSPOT = CBox{((pointers->currentCursorImage.hotspot * PMONITOR->scale) + cursorPadding) * resultShown.scale, {0, 0}}
|
||||
.transform(wlTransformToHyprutils(invertTransform(PMONITOR->transform)), PMONITOR->cursorSwapchain->currentOptions().size.x, PMONITOR->cursorSwapchain->currentOptions().size.y)
|
||||
.pos();
|
||||
|
||||
offset = pointers->getCursorPosForMonitor(state->monitor.lock()) - HOTSPOT;
|
||||
offset.x /= xbox.x;
|
||||
offset.y /= xbox.y;
|
||||
}
|
||||
|
||||
renderCursorBox(texture, xbox, damage, nullptr, 0, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom, resultShown.stretch.angle, resultShown.stretch.magnitude, offset);
|
||||
|
||||
g_pHyprOpenGL->end();
|
||||
glFlush();
|
||||
|
@ -360,16 +372,6 @@ void CDynamicCursors::beforeRender(CPointerManager* pointers) {
|
|||
if (**PINVERT) {
|
||||
// we need introspection as we make use of the offloadFB
|
||||
g_pHyprOpenGL->m_RenderData.forceIntrospection = true;
|
||||
|
||||
if (!invertSoftware) {
|
||||
pointers->lockSoftwareAll();
|
||||
invertSoftware = true;
|
||||
}
|
||||
} else {
|
||||
if (invertSoftware) {
|
||||
pointers->unlockSoftwareAll();
|
||||
invertSoftware = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,6 +379,7 @@ void CDynamicCursors::calculate(EModeUpdate type) {
|
|||
static auto* const* PTHRESHOLD = (Hyprlang::INT* const*) getConfig(CONFIG_THRESHOLD);
|
||||
static auto* const* PSHAKE = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE);
|
||||
static auto* const* PSHAKE_EFFECTS = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_EFFECTS);
|
||||
static auto* const* PINVERT = (Hyprlang::INT* const*)getConfig(CONFIG_INVERT);
|
||||
|
||||
IMode* mode = currentMode();
|
||||
|
||||
|
@ -395,7 +398,8 @@ void CDynamicCursors::calculate(EModeUpdate type) {
|
|||
auto result = resultMode;
|
||||
result.scale *= resultShake;
|
||||
|
||||
if (resultShown.hasDifference(&result, **PTHRESHOLD * (PI / 180.0), 0.01, 0.01)) {
|
||||
// always rerender inverted on move
|
||||
if (resultShown.hasDifference(&result, **PTHRESHOLD * (PI / 180.0), 0.01, 0.01) || (**PINVERT && type == MOVE)) {
|
||||
resultShown = result;
|
||||
resultShown.clamp(**PTHRESHOLD * (PI / 180.0), 0.01, 0.01); // clamp low values so it is rendered pixel-perfectly when no effect
|
||||
|
||||
|
|
|
@ -48,8 +48,6 @@ class CDynamicCursors {
|
|||
|
||||
// whether we have already locked software for cursor zoom
|
||||
bool zoomSoftware = false;
|
||||
// whether we have already locked software for inverted cursors
|
||||
bool invertSoftware = false;
|
||||
|
||||
// modes
|
||||
CModeRotate rotate;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "src/debug/Log.hpp"
|
||||
#include "src/render/Renderer.hpp"
|
||||
|
||||
#define private public
|
||||
#include <hyprland/src/render/OpenGL.hpp>
|
||||
#undef private
|
||||
|
||||
#include "src/render/Renderer.hpp"
|
||||
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/render/Shaders.hpp>
|
||||
|
||||
|
@ -18,6 +19,7 @@ void CInversionShader::compile(std::string vertex, std::string fragment) {
|
|||
posAttrib = glGetAttribLocation(program, "pos");
|
||||
|
||||
proj = glGetUniformLocation(program, "proj");
|
||||
screenOffset = glGetUniformLocation(program, "screenOffset");
|
||||
backgroundTex = glGetUniformLocation(program, "backgroundTex");
|
||||
cursorTex = glGetUniformLocation(program, "cursorTex");
|
||||
alpha = glGetUniformLocation(program, "alpha");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <string>
|
||||
#include <hyprland/src/render/Shader.hpp>
|
||||
|
||||
// we need our own shader class as we have two textures
|
||||
// we need our own shader class as we have two textures, and custom uniforms
|
||||
class CInversionShader {
|
||||
public:
|
||||
GLuint program = 0;
|
||||
|
@ -10,6 +10,7 @@ class CInversionShader {
|
|||
GLint texAttrib = -1;
|
||||
|
||||
GLint proj = -1;
|
||||
GLint screenOffset = -1;
|
||||
GLint cursorTex = -1;
|
||||
GLint backgroundTex = -1;
|
||||
GLint alpha = -1;
|
||||
|
@ -44,10 +45,12 @@ inline const std::string VERTEX = R"#(
|
|||
varying vec2 v_texcoord;
|
||||
varying vec2 v_screencord;
|
||||
|
||||
uniform vec2 screenOffset;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
|
||||
|
||||
v_screencord = gl_Position.xy / 2.0 + vec2(0.5, 0.5); // transform to texture coords
|
||||
v_screencord = (proj * vec3(pos + screenOffset, 1.0)).xy / 2.0 + vec2(0.5, 0.5); // transform to texture coords
|
||||
v_texcoord = texcoord;
|
||||
}
|
||||
)#";
|
||||
|
|
|
@ -116,7 +116,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
addConfig(CONFIG_INVERT, 0);
|
||||
addConfig(CONFIG_INVERT_SHADER, "normal");
|
||||
addConfig(CONFIG_INVERT_CHROMA, 0);
|
||||
addConfig(CONFIG_INVERT_CHROMA_COLOR, 0xFF000000); // opaque black
|
||||
addConfig(CONFIG_INVERT_CHROMA_COLOR, (int) 0xFF000000); // opaque black
|
||||
|
||||
addShapeConfig(CONFIG_TILT_FUNCTION, "negative_quadratic");
|
||||
addShapeConfig(CONFIG_TILT_LIMIT, 5000);
|
||||
|
|
|
@ -194,6 +194,9 @@ void renderCursorTextureInternalWithDamageInverted(SP<CTexture> tex, CBox* pBox,
|
|||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB.m_cTex->m_iTarget, g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB.m_cTex->m_iTexID);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(tex->m_iTarget, tex->m_iTexID);
|
||||
|
||||
if (g_pHyprOpenGL->m_RenderData.useNearestNeighbor || nearest) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue