feat: inverted config, setup and docs

This commit is contained in:
Virt 2024-06-30 14:41:31 +02:00
commit ec4db58ade
5 changed files with 108 additions and 55 deletions

View file

@ -28,6 +28,13 @@ The plugin supports shake to find, akin to how KDE Plasma, MacOS, etc. do it. It
https://github.com/VirtCode/hypr-dynamic-cursors/assets/41426325/9ff64a9b-64e5-4595-b721-dcb4d62bee18 https://github.com/VirtCode/hypr-dynamic-cursors/assets/41426325/9ff64a9b-64e5-4595-b721-dcb4d62bee18
### 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.
INSERT VIDEO HERE
## state ## 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: This plugin is still very early in its development. There are also multiple things which may or may not be implemented in the future:
@ -39,7 +46,7 @@ This plugin is still very early in its development. There are also multiple thin
- [X] per-shape length and starting angle (if possible) - [X] per-shape length and starting angle (if possible)
- [X] cursor shake to find - [X] cursor shake to find
- [X] overdue refactoring (wait for aquamarine merge) - [X] overdue refactoring (wait for aquamarine merge)
- [ ] ~~inverted cursor?~~ (i think out of scope here, but see the [inverted branch](https://github.com/VirtCode/hypr-dynamic-cursors/tree/inverted)) - [X] inverted cursor!
- [ ] hyprcursor magified shape - [ ] hyprcursor magified shape
If anything here sounds interesting to you, don't hesitate to contribute. If anything here sounds interesting to you, don't hesitate to contribute.
@ -183,6 +190,29 @@ plugin:dynamic-cursors {
# see #3 # see #3
ipc = false 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 {
# 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°
# hue - only shift hue by 180°
shader = invert
# apply a chroma key algorithm to the cursor
# this allows for only replacing certain parts of the cursor texture
chroma = false
# color to replace when chroma = true
chroma:color = rgba(000000ff)
}
} }
``` ```

View file

@ -49,17 +49,33 @@ CDynamicCursors::~CDynamicCursors() {
} }
} }
void renderCursorBox(SP<CTexture> texture, CBox box, CRegion& damage, Vector2D hotspot, float zoom) {
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_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));
} else
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.f, hotspot, nearest);
}
/* /*
Reimplements rendering of the software cursor. Reimplements rendering of the software cursor.
Is also largely identical to hyprlands impl, but uses our custom rendering to rotate the 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) { void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos) {
<<<<<<< HEAD
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST); static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
======= static auto* const* PINVERT = (Hyprlang::INT* const*) getConfig(CONFIG_INVERT);
static auto* const* PNEAREST = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_SHAKE_NEAREST)->getDataStaticPtr();
static auto* const* PINVERT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_INVERT)->getDataStaticPtr();
>>>>>>> c8ae8fe (feat: basic inverted rendering)
if (!pointers->hasCursor()) if (!pointers->hasCursor())
return; return;
@ -99,17 +115,10 @@ void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMo
box.rot = resultShown.rotation; box.rot = resultShown.rotation;
// now pass the hotspot to rotate around // now pass the hotspot to rotate around
<<<<<<< HEAD renderCursorBox(texture, box, damage, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom);
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.F, nullptr, 0, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST, resultShown.stretch.angle, resultShown.stretch.magnitude);
if (pointers->currentCursorImage.surface) if (pointers->currentCursorImage.surface)
pointers->currentCursorImage.surface->resource()->frame(now); pointers->currentCursorImage.surface->resource()->frame(now);
=======
if (**PINVERT)
renderCursorTextureInternalWithDamageInverted(texture, &box, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
else
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
>>>>>>> c8ae8fe (feat: basic inverted rendering)
} }
/* /*
@ -143,16 +152,9 @@ void CDynamicCursors::damageSoftware(CPointerManager* pointers) {
This function reimplements the hardware cursor buffer drawing. This function reimplements the hardware cursor buffer drawing.
It is largely copied from hyprland, but adjusted to allow the cursor to be rotated. It is largely copied from hyprland, but adjusted to allow the cursor to be rotated.
*/ */
<<<<<<< HEAD
SP<Aquamarine::IBuffer> CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) { 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* PHW_DEBUG = (Hyprlang::INT* const*) getConfig(CONFIG_HW_DEBUG);
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST); static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
=======
wlr_buffer* CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
static auto* const* PHW_DEBUG= (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_HW_DEBUG)->getDataStaticPtr();
static auto* const* PNEAREST = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_SHAKE_NEAREST)->getDataStaticPtr();
static auto* const* PINVERT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_INVERT)->getDataStaticPtr();
>>>>>>> c8ae8fe (feat: basic inverted rendering)
auto output = state->monitor->output; auto output = state->monitor->output;
@ -262,14 +264,7 @@ wlr_buffer* CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPoint
xbox.rot = resultShown.rotation; xbox.rot = resultShown.rotation;
// use our custom draw function // use our custom draw function
<<<<<<< HEAD renderCursorBox(texture, xbox, damage, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom);
renderCursorTextureInternalWithDamage(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.waitTimeline, pointers->currentCursorImage.waitPoint, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST, resultShown.stretch.angle, resultShown.stretch.magnitude);
=======
if (**PINVERT)
renderCursorTextureInternalWithDamageInverted(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
else
renderCursorTextureInternalWithDamage(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
>>>>>>> c8ae8fe (feat: basic inverted rendering)
g_pHyprOpenGL->end(); g_pHyprOpenGL->end();
glFlush(); glFlush();
@ -359,6 +354,25 @@ IMode* CDynamicCursors::currentMode() {
else return nullptr; else return nullptr;
} }
void CDynamicCursors::beforeRender(CPointerManager* pointers) {
static auto* const* PINVERT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_INVERT)->getDataStaticPtr();
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;
}
}
}
void CDynamicCursors::calculate(EModeUpdate type) { void CDynamicCursors::calculate(EModeUpdate type) {
static auto* const* PTHRESHOLD = (Hyprlang::INT* const*) getConfig(CONFIG_THRESHOLD); static auto* const* PTHRESHOLD = (Hyprlang::INT* const*) getConfig(CONFIG_THRESHOLD);
static auto* const* PSHAKE = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE); static auto* const* PSHAKE = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE);

View file

@ -20,6 +20,8 @@ class CDynamicCursors {
void onCursorMoved(CPointerManager* pointers); void onCursorMoved(CPointerManager* pointers);
/* called on tick */ /* called on tick */
void onTick(CPointerManager* pointers); void onTick(CPointerManager* pointers);
/* called before render */
void beforeRender(CPointerManager* pointers);
/* hook on renderSoftwareCursorsFor */ /* hook on renderSoftwareCursorsFor */
void renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos); void renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos);
@ -46,6 +48,8 @@ class CDynamicCursors {
// whether we have already locked software for cursor zoom // whether we have already locked software for cursor zoom
bool zoomSoftware = false; bool zoomSoftware = false;
// whether we have already locked software for inverted cursors
bool invertSoftware = false;
// modes // modes
CModeRotate rotate; CModeRotate rotate;

View file

@ -1,4 +1,5 @@
#include "src/debug/Log.hpp" #include "src/debug/Log.hpp"
#include "src/render/Renderer.hpp"
#define private public #define private public
#include <hyprland/src/render/OpenGL.hpp> #include <hyprland/src/render/OpenGL.hpp>
@ -20,6 +21,10 @@ void CInversionShader::compile(std::string vertex, std::string fragment) {
backgroundTex = glGetUniformLocation(program, "backgroundTex"); backgroundTex = glGetUniformLocation(program, "backgroundTex");
cursorTex = glGetUniformLocation(program, "cursorTex"); cursorTex = glGetUniformLocation(program, "cursorTex");
alpha = glGetUniformLocation(program, "alpha"); alpha = glGetUniformLocation(program, "alpha");
chroma = glGetUniformLocation(program, "chroma");
chromaColor = glGetUniformLocation(program, "chromaColor");
mode = glGetUniformLocation(program, "mode");
applyTint = glGetUniformLocation(program, "applyTint"); applyTint = glGetUniformLocation(program, "applyTint");
tint = glGetUniformLocation(program, "tint"); tint = glGetUniformLocation(program, "tint");
} }
@ -30,13 +35,11 @@ CInversionShader::~CInversionShader() {
} }
CShaders::CShaders() { CShaders::CShaders() {
RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)), g_pHyprRenderer->makeEGLCurrent();
"Couldn't set current EGL!");
rgba.compile(VERTEX, RGBA); rgba.compile(VERTEX, RGBA);
ext.compile(VERTEX, EXT); ext.compile(VERTEX, EXT);
rgbx.compile(VERTEX, RGBX); rgbx.compile(VERTEX, RGBX);
RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)), g_pHyprRenderer->unsetEGL();
"Couldn't set current EGL!");
} }

View file

@ -5,18 +5,17 @@
class CInversionShader { class CInversionShader {
public: public:
GLuint program = 0; GLuint program = 0;
GLint posAttrib = -1;
GLint texAttrib = -1;
GLint proj = -1; GLint proj = -1;
GLint color = -1;
GLint alphaMatte = -1;
GLint cursorTex = -1; GLint cursorTex = -1;
GLint backgroundTex = -1; GLint backgroundTex = -1;
GLint alpha = -1; GLint alpha = -1;
GLint posAttrib = -1; GLint mode = -1;
GLint texAttrib = -1; GLint chroma = -1;
GLint matteTexAttrib = -1; GLint chromaColor = -1;
GLint discardOpaque = -1;
GLint discardAlpha = -1;
GLfloat discardAlphaValue = -1;
GLint applyTint = -1; GLint applyTint = -1;
GLint tint = -1; GLint tint = -1;
@ -66,25 +65,28 @@ inline const std::string RGBA = R"#(
uniform int applyTint; uniform int applyTint;
uniform vec3 tint; uniform vec3 tint;
uniform int mode;
uniform int chroma;
uniform vec4 chromaColor;
void main() { void main() {
vec4 cursor = texture2D(cursorTex, v_texcoord); vec4 cursor = texture2D(cursorTex, v_texcoord);
// load and invert background
vec4 background = texture2D(backgroundTex, v_screencord); vec4 background = texture2D(backgroundTex, v_screencord);
// invert based on mode
if (mode == 0 || mode == 1) // invert
background.rgb = vec3(1.0, 1.0, 1.0) - background.rgb; background.rgb = vec3(1.0, 1.0, 1.0) - background.rgb;
if (mode == 1 || mode == 2) // hueshift
background.rgb = -background.rgb + dot(vec3(0.26312, 0.5283, 0.10488), background.rgb) * 2.0;
background *= cursor[3]; // premultiplied alpha background *= cursor[3]; // premultiplied alpha
vec4 pixColor = background;
// invert hue // this is a very crude "chroma algorithm", feel free to contribute a better one
//background.rgb = -background.rgb + dot(vec3(0.26312, 0.5283, 0.10488), background.rgb) * 2.0; if (chroma == 1) {
float diff = (abs(chromaColor.x - cursor.x) + abs(chromaColor.y - cursor.y) + abs(chromaColor.z - cursor.z) + abs(chromaColor.w - cursor.w)) / 4.0;
vec4 pixColor = cursor;
//if (cursor[3] != 1.0) pixColor = cursor;
//pixColor = vec4(v_screencord + vec2(1.0, 1.0) / 2.0, 0.0, 1.0);
vec4 chroma = vec4(0.0, 0.0, 0.0, 1.0);
float diff = (abs(chroma.x - cursor.x) + abs(chroma.y - cursor.y) + abs(chroma.z - cursor.z) + abs(chroma.w - cursor.w)) / 4.0;
pixColor = background * (1.0 - diff) + cursor * diff; pixColor = background * (1.0 - diff) + cursor * diff;
}
if (applyTint == 1) { if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0]; pixColor[0] = pixColor[0] * tint[0];