feat: inverted hardware cursor support

This commit is contained in:
Virt 2024-06-30 17:32:53 +02:00
commit d172611292
8 changed files with 53 additions and 40 deletions

View file

@ -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°

View file

@ -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"

View file

@ -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

View file

@ -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;

View file

@ -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");

View file

@ -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;
}
)#";

View file

@ -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);

View file

@ -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) {