From d1726112923ddb4e4b30aefe6896045c46b18293 Mon Sep 17 00:00:00 2001 From: Virt <41426325+VirtCode@users.noreply.github.com> Date: Sun, 30 Jun 2024 17:32:53 +0200 Subject: [PATCH] feat: inverted hardware cursor support --- README.md | 15 ++++++++----- src/config/config.hpp | 8 +++---- src/cursor.cpp | 52 +++++++++++++++++++++++-------------------- src/cursor.hpp | 2 -- src/invert/shader.cpp | 4 +++- src/invert/shader.hpp | 7 ++++-- src/main.cpp | 2 +- src/renderer.cpp | 3 +++ 8 files changed, 53 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 78f1f94..d0d8e01 100644 --- a/README.md +++ b/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° diff --git a/src/config/config.hpp b/src/config/config.hpp index a734aeb..f5716f7 100644 --- a/src/config/config.hpp +++ b/src/config/config.hpp @@ -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" diff --git a/src/cursor.cpp b/src/cursor.cpp index 6e49188..25537af 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -49,24 +49,25 @@ CDynamicCursors::~CDynamicCursors() { } } -void renderCursorBox(SP texture, CBox box, CRegion& damage, Vector2D hotspot, float zoom) { +void renderCursorBox(SP texture, CBox box, CRegion& damage, SP 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 pMonitor, timespec* now, CRegion& damage, std::optional 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 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 CDynamicCursors::renderHardware(CPointerManager* pointers, SP state, SP 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 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 diff --git a/src/cursor.hpp b/src/cursor.hpp index 33335ad..f9733fc 100644 --- a/src/cursor.hpp +++ b/src/cursor.hpp @@ -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; diff --git a/src/invert/shader.cpp b/src/invert/shader.cpp index b359e4d..b359342 100644 --- a/src/invert/shader.cpp +++ b/src/invert/shader.cpp @@ -1,10 +1,11 @@ #include "src/debug/Log.hpp" -#include "src/render/Renderer.hpp" #define private public #include #undef private +#include "src/render/Renderer.hpp" + #include #include @@ -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"); diff --git a/src/invert/shader.hpp b/src/invert/shader.hpp index 793491b..01a16df 100644 --- a/src/invert/shader.hpp +++ b/src/invert/shader.hpp @@ -1,7 +1,7 @@ #include #include -// 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; } )#"; diff --git a/src/main.cpp b/src/main.cpp index ad71250..f21dada 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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); diff --git a/src/renderer.cpp b/src/renderer.cpp index ce71d84..85f4cef 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -194,6 +194,9 @@ void renderCursorTextureInternalWithDamageInverted(SP 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) {