summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2024-03-09 16:52:59 +0000
committerGitHub <noreply@github.com>2024-03-09 16:52:59 +0000
commitc3882bb83240b602277f2d22f21d71690531f62e (patch)
treec5e9a1110cd5d22ef700fd8b7408a418cba7c4f0
parente7a5db4852d654596e554b9cdeaa2694b346ee03 (diff)
internal: Support libhyprcursor (#5009)
woo --------- Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
-rw-r--r--.github/actions/setup_base/action.yml14
-rwxr-xr-xCMakeLists.txt2
-rw-r--r--flake.lock72
-rw-r--r--flake.nix6
-rw-r--r--nix/default.nix3
-rw-r--r--nix/overlays.nix1
-rw-r--r--nix/patches/meson-build.patch5
-rw-r--r--src/Compositor.cpp17
-rw-r--r--src/Compositor.hpp1
-rw-r--r--src/config/ConfigManager.cpp1
-rw-r--r--src/debug/HyprCtl.cpp12
-rw-r--r--src/events/Misc.cpp9
-rw-r--r--src/helpers/Monitor.cpp4
-rw-r--r--src/includes.hpp2
-rw-r--r--src/managers/CursorManager.cpp200
-rw-r--r--src/managers/CursorManager.hpp69
-rw-r--r--src/meson.build1
-rw-r--r--src/render/Renderer.cpp3
18 files changed, 379 insertions, 43 deletions
diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml
index 8a33dd55..8598b5a3 100644
--- a/.github/actions/setup_base/action.yml
+++ b/.github/actions/setup_base/action.yml
@@ -12,6 +12,7 @@ runs:
- name: Get required pacman pkgs
shell: bash
run: |
+ sed -i -e "1i [extra-testing]\nInclude = /etc/pacman.d/mirrorlist" "/etc/pacman.conf"
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy \
@@ -51,7 +52,18 @@ runs:
wayland-protocols \
xcb-util-errors \
xcb-util-renderutil \
- xcb-util-wm
+ xcb-util-wm \
+ libzip \
+ librsvg
+
+ - name: Get hyprcursor-git
+ shell: bash
+ run: |
+ git clone https://github.com/hyprwm/hyprcursor --recursive
+ cd hyprcursor
+ cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
+ cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
+ cmake --install build
- name: Get Xorg pacman pkgs
shell: bash
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 857e21de..71766a8c 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -101,7 +101,7 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED)
-pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1 hyprlang>=0.3.2) # we do not check for wlroots, as we provide it ourselves
+pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1 hyprlang>=0.3.2 hyprcursor) # we do not check for wlroots, as we provide it ourselves
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
diff --git a/flake.lock b/flake.lock
index 6d27876d..36a509b1 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,5 +1,29 @@
{
"nodes": {
+ "hyprcursor": {
+ "inputs": {
+ "hyprlang": "hyprlang",
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": [
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709942067,
+ "narHash": "sha256-DGU4zQDwIx6pXM6oHdA+89UU/QjqE05HiXOvigECJjI=",
+ "owner": "hyprwm",
+ "repo": "hyprcursor",
+ "rev": "a2233d4a2a58233457712acfd88d10a2a8a85711",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "hyprcursor",
+ "type": "github"
+ }
+ },
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
@@ -26,6 +50,28 @@
"hyprlang": {
"inputs": {
"nixpkgs": [
+ "hyprcursor",
+ "nixpkgs"
+ ],
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1709914708,
+ "narHash": "sha256-bR4o3mynoTa1Wi4ZTjbnsZ6iqVcPGriXp56bZh5UFTk=",
+ "owner": "hyprwm",
+ "repo": "hyprlang",
+ "rev": "a685493fdbeec01ca8ccdf1f3655c044a8ce2fe2",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "hyprlang",
+ "type": "github"
+ }
+ },
+ "hyprlang_2": {
+ "inputs": {
+ "nixpkgs": [
"nixpkgs"
],
"systems": [
@@ -33,11 +79,11 @@
]
},
"locked": {
- "lastModified": 1709775675,
- "narHash": "sha256-G+gIMUQBtfbbrnsM/OPJzebdqKFP6typplNCE7X8Szw=",
+ "lastModified": 1709914708,
+ "narHash": "sha256-bR4o3mynoTa1Wi4ZTjbnsZ6iqVcPGriXp56bZh5UFTk=",
"owner": "hyprwm",
"repo": "hyprlang",
- "rev": "f1db1a7e1faee2a5c67d03b6bd283da82eed3730",
+ "rev": "a685493fdbeec01ca8ccdf1f3655c044a8ce2fe2",
"type": "github"
},
"original": {
@@ -64,10 +110,11 @@
},
"root": {
"inputs": {
+ "hyprcursor": "hyprcursor",
"hyprland-protocols": "hyprland-protocols",
- "hyprlang": "hyprlang",
+ "hyprlang": "hyprlang_2",
"nixpkgs": "nixpkgs",
- "systems": "systems",
+ "systems": "systems_2",
"wlroots": "wlroots",
"xdph": "xdph"
}
@@ -87,6 +134,21 @@
"type": "github"
}
},
+ "systems_2": {
+ "locked": {
+ "lastModified": 1689347949,
+ "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default-linux",
+ "type": "github"
+ }
+ },
"wlroots": {
"flake": false,
"locked": {
diff --git a/flake.nix b/flake.nix
index ed52478a..e97ba1c9 100644
--- a/flake.nix
+++ b/flake.nix
@@ -16,6 +16,12 @@
flake = false;
};
+ hyprcursor = {
+ url = "github:hyprwm/hyprcursor";
+ inputs.nixpkgs.follows = "nixpkgs";
+ inputs.systems.follows = "systems";
+ };
+
hyprland-protocols = {
url = "github:hyprwm/hyprland-protocols";
inputs.nixpkgs.follows = "nixpkgs";
diff --git a/nix/default.nix b/nix/default.nix
index 66ea53a6..c4650d3c 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -8,6 +8,7 @@
binutils,
cairo,
git,
+ hyprcursor,
hyprland-protocols,
hyprlang,
jq,
@@ -32,7 +33,6 @@
enableXWayland ? true,
legacyRenderer ? false,
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
- wrapRuntimeDeps ? true,
version ? "git",
commit,
date,
@@ -75,6 +75,7 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
[
cairo
git
+ hyprcursor.dev
hyprland-protocols
hyprlang
libdrm
diff --git a/nix/overlays.nix b/nix/overlays.nix
index 0a84d93e..c216ff53 100644
--- a/nix/overlays.nix
+++ b/nix/overlays.nix
@@ -21,6 +21,7 @@ in {
# Packages for variations of Hyprland, dependencies included.
hyprland-packages = lib.composeManyExtensions [
# Dependencies
+ inputs.hyprcursor.overlays.default
inputs.hyprland-protocols.overlays.default
inputs.hyprlang.overlays.default
self.overlays.wlroots-hyprland
diff --git a/nix/patches/meson-build.patch b/nix/patches/meson-build.patch
index aefbf1bc..7f372107 100644
--- a/nix/patches/meson-build.patch
+++ b/nix/patches/meson-build.patch
@@ -37,15 +37,16 @@ diff --git a/src/meson.build b/src/meson.build
index 45701f5f..3505cefe 100644
--- a/src/meson.build
+++ b/src/meson.build
-@@ -9,17 +9,17 @@ executable('Hyprland', src,
+@@ -9,7 +9,7 @@ executable('Hyprland', src,
server_protos,
dependency('wayland-server'),
dependency('wayland-client'),
- wlroots.get_variable('wlroots'),
+ dependency('wlroots'),
dependency('cairo'),
+ dependency('hyprcursor'),
dependency('hyprlang', version: '>= 0.3.2'),
- dependency('libdrm'),
+@@ -16,12 +16,12 @@ executable('Hyprland', src,
dependency('egl'),
dependency('xkbcommon'),
dependency('libinput'),
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 9d5f7241..5697adbf 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -1,6 +1,7 @@
#include "Compositor.hpp"
#include "helpers/Splashes.hpp"
#include "config/ConfigValue.hpp"
+#include "managers/CursorManager.hpp"
#include <random>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
@@ -182,18 +183,6 @@ void CCompositor::initServer() {
m_sWLRCursor = wlr_cursor_create();
wlr_cursor_attach_output_layout(m_sWLRCursor, m_sWLROutputLayout);
- if (const auto XCURSORENV = getenv("XCURSOR_SIZE"); !XCURSORENV || std::string(XCURSORENV).empty())
- setenv("XCURSOR_SIZE", "24", true);
-
- const auto XCURSORENV = getenv("XCURSOR_SIZE");
- int cursorSize = 24;
- try {
- cursorSize = std::stoi(XCURSORENV);
- } catch (std::exception& e) { Debug::log(ERR, "XCURSOR_SIZE invalid in check #2? ({})", XCURSORENV); }
-
- m_sWLRXCursorMgr = wlr_xcursor_manager_create(nullptr, cursorSize);
- wlr_xcursor_manager_load(m_sWLRXCursorMgr, 1);
-
m_sSeat.seat = wlr_seat_create(m_sWLDisplay, "seat0");
m_sWLRPresentation = wlr_presentation_create(m_sWLDisplay, m_sWLRBackend);
@@ -422,6 +411,7 @@ void CCompositor::cleanup() {
wl_display_destroy_clients(g_pCompositor->m_sWLDisplay);
g_pDecorationPositioner.reset();
+ g_pCursorManager.reset();
g_pPluginSystem.reset();
g_pHyprNotificationOverlay.reset();
g_pDebugOverlay.reset();
@@ -511,6 +501,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the DecorationPositioner!");
g_pDecorationPositioner = std::make_unique<CDecorationPositioner>();
+
+ Debug::log(LOG, "Creating the CursorManager!");
+ g_pCursorManager = std::make_unique<CCursorManager>();
} break;
default: UNREACHABLE();
}
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 8042aaaa..b1033e89 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -57,7 +57,6 @@ class CCompositor {
wlr_layer_shell_v1* m_sWLRLayerShell;
wlr_xdg_shell* m_sWLRXDGShell;
wlr_cursor* m_sWLRCursor;
- wlr_xcursor_manager* m_sWLRXCursorMgr;
wlr_virtual_keyboard_manager_v1* m_sWLRVKeyboardMgr;
wlr_output_manager_v1* m_sWLROutputMgr;
wlr_presentation* m_sWLRPresentation;
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 08b2e3c1..0409140a 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -347,6 +347,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:close_special_on_empty", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:background_color", Hyprlang::INT{0xff111111});
m_pConfig->addConfigValue("misc:new_window_takes_over_fullscreen", Hyprlang::INT{0});
+ m_pConfig->addConfigValue("misc:enable_hyprcursor", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1});
diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp
index 80b1d16c..f13843a3 100644
--- a/src/debug/HyprCtl.cpp
+++ b/src/debug/HyprCtl.cpp
@@ -16,6 +16,7 @@
#include <typeindex>
#include "../config/ConfigValue.hpp"
+#include "../managers/CursorManager.hpp"
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
@@ -1045,16 +1046,7 @@ std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request)
if (size <= 0)
return "size not positive";
- wlr_xcursor_manager_destroy(g_pCompositor->m_sWLRXCursorMgr);
-
- g_pCompositor->m_sWLRXCursorMgr = wlr_xcursor_manager_create(theme.c_str(), size);
-
- setenv("XCURSOR_SIZE", SIZESTR.c_str(), true);
- setenv("XCURSOR_THEME", theme.c_str(), true);
-
- for (auto& m : g_pCompositor->m_vMonitors) {
- wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, m->scale);
- }
+ g_pCursorManager->changeTheme(theme, size);
return "ok";
}
diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp
index 64f2f29d..d7e3a849 100644
--- a/src/events/Misc.cpp
+++ b/src/events/Misc.cpp
@@ -4,6 +4,7 @@
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
+#include "../managers/CursorManager.hpp"
// ------------------------------ //
// __ __ _____ _____ _____ //
@@ -69,10 +70,10 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) {
wlr_xwayland_set_seat(g_pXWaylandManager->m_sWLRXWayland, g_pCompositor->m_sSeat.seat);
- const auto XCURSOR = wlr_xcursor_manager_get_xcursor(g_pCompositor->m_sWLRXCursorMgr, "left_ptr", 1);
- if (XCURSOR) {
- wlr_xwayland_set_cursor(g_pXWaylandManager->m_sWLRXWayland, XCURSOR->images[0]->buffer, XCURSOR->images[0]->width * 4, XCURSOR->images[0]->width,
- XCURSOR->images[0]->height, XCURSOR->images[0]->hotspot_x, XCURSOR->images[0]->hotspot_y);
+ const auto CURSOR = g_pCursorManager->dataFor("left_ptr");
+ if (CURSOR.surface) {
+ wlr_xwayland_set_cursor(g_pXWaylandManager->m_sWLRXWayland, cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), CURSOR.size,
+ CURSOR.size, CURSOR.hotspotX, CURSOR.hotspotY);
}
const auto ROOT = xcb_setup_roots_iterator(xcb_get_setup(XCBCONNECTION)).data->root;
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index c05c79cc..9126f936 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -164,8 +164,6 @@ void CMonitor::onConnect(bool noRule) {
wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
- wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
-
Debug::log(LOG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", output->name, vecPosition, vecPixelSize, (uintptr_t)output);
setupDefaultWS(monitorRule);
@@ -192,8 +190,6 @@ void CMonitor::onConnect(bool noRule) {
if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet
g_pCompositor->setActiveMonitor(this);
- wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
-
g_pHyprRenderer->arrangeLayersForMonitor(ID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
diff --git a/src/includes.hpp b/src/includes.hpp
index 961729ad..6a1e5bc3 100644
--- a/src/includes.hpp
+++ b/src/includes.hpp
@@ -63,7 +63,6 @@ extern "C" {
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
-#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
@@ -76,6 +75,7 @@ extern "C" {
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
+#include <wlr/util/edges.h>
#include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_tablet_v2.h>
diff --git a/src/managers/CursorManager.cpp b/src/managers/CursorManager.cpp
new file mode 100644
index 00000000..ae765016
--- /dev/null
+++ b/src/managers/CursorManager.cpp
@@ -0,0 +1,200 @@
+#include "CursorManager.hpp"
+#include "Compositor.hpp"
+#include "../config/ConfigValue.hpp"
+
+extern "C" {
+#include <wlr/interfaces/wlr_buffer.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+}
+
+static int cursorAnimTimer(void* data) {
+ g_pCursorManager->tickAnimatedCursor();
+ return 1;
+}
+
+CCursorManager::CCursorManager() {
+ m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(m_szTheme.empty() ? nullptr : m_szTheme.c_str());
+
+ // find default size. First, HYPRCURSOR_SIZE, then XCURSOR_SIZE, then 24
+ auto SIZE = getenv("HYPRCURSOR_SIZE");
+ if (SIZE) {
+ try {
+ m_iSize = std::stoi(SIZE);
+ } catch (...) { ; }
+ }
+
+ SIZE = getenv("XCURSOR_SIZE");
+ if (SIZE && m_iSize == 0) {
+ try {
+ m_iSize = std::stoi(SIZE);
+ } catch (...) { ; }
+ }
+
+ if (m_iSize == 0)
+ m_iSize = 24;
+
+ m_pWLRXCursorMgr = wlr_xcursor_manager_create(nullptr, m_iSize);
+ wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0);
+
+ m_pAnimationTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ::cursorAnimTimer, nullptr);
+
+ updateTheme();
+
+ g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateTheme(); });
+}
+
+void CCursorManager::dropBufferRef(CCursorManager::CCursorBuffer* ref) {
+ std::erase_if(m_vCursorBuffers, [ref](const auto& buf) { return buf.get() == ref; });
+}
+
+static void cursorBufferDestroy(struct wlr_buffer* wlr_buffer) {
+ CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base);
+ g_pCursorManager->dropBufferRef(buffer->parent);
+}
+
+static bool cursorBufferBeginDataPtr(struct wlr_buffer* wlr_buffer, uint32_t flags, void** data, uint32_t* format, size_t* stride) {
+ CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base);
+
+ if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE)
+ return false;
+
+ *data = cairo_image_surface_get_data(buffer->surface);
+ *stride = cairo_image_surface_get_stride(buffer->surface);
+ *format = DRM_FORMAT_ARGB8888;
+ return true;
+}
+
+static void cursorBufferEndDataPtr(struct wlr_buffer* wlr_buffer) {
+ ;
+}
+
+//
+static const wlr_buffer_impl bufferImpl = {
+ .destroy = cursorBufferDestroy,
+ .begin_data_ptr_access = cursorBufferBeginDataPtr,
+ .end_data_ptr_access = cursorBufferEndDataPtr,
+};
+
+CCursorManager::CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : size(size_), hotspot(hot_) {
+ wlrBuffer.surface = surf;
+ wlr_buffer_init(&wlrBuffer.base, &bufferImpl, size.x, size.y);
+ wlrBuffer.parent = this;
+}
+
+CCursorManager::CCursorBuffer::~CCursorBuffer() {
+ ; // will be freed in .destroy
+}
+
+wlr_buffer* CCursorManager::getCursorBuffer() {
+ return !m_vCursorBuffers.empty() ? &m_vCursorBuffers.back()->wlrBuffer.base : nullptr;
+}
+
+void CCursorManager::setCursorSurface(wlr_surface* surf, const Vector2D& hotspot) {
+ wlr_cursor_set_surface(g_pCompositor->m_sWLRCursor, surf, hotspot.x, hotspot.y);
+
+ m_bOurBufferConnected = false;
+}
+
+void CCursorManager::setCursorFromName(const std::string& name) {
+
+ static auto PUSEHYPRCURSOR = CConfigValue<Hyprlang::INT>("misc:enable_hyprcursor");
+
+ if (!m_pHyprcursor->valid() || !*PUSEHYPRCURSOR) {
+ wlr_cursor_set_xcursor(g_pCompositor->m_sWLRCursor, m_pWLRXCursorMgr, name.c_str());
+ return;
+ }
+
+ m_sCurrentCursorShapeData = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo);
+
+ if (m_sCurrentCursorShapeData.images.size() < 1) {
+ Debug::log(ERR, "BUG THIS: No cursor returned by getShape()");
+ return;
+ }
+
+ m_vCursorBuffers.emplace_back(std::make_unique<CCursorBuffer>(m_sCurrentCursorShapeData.images[0].surface,
+ Vector2D{m_sCurrentCursorShapeData.images[0].size, m_sCurrentCursorShapeData.images[0].size},
+ Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY}));
+
+ if (g_pCompositor->m_sWLRCursor)
+ wlr_cursor_set_buffer(g_pCompositor->m_sWLRCursor, getCursorBuffer(), m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY,
+ m_fCursorScale);
+
+ m_bOurBufferConnected = true;
+
+ if (m_sCurrentCursorShapeData.images.size() > 1) {
+ // animated
+ wl_event_source_timer_update(m_pAnimationTimer, m_sCurrentCursorShapeData.images[0].delay);
+ m_iCurrentAnimationFrame = 0;
+ } else {
+ // disarm
+ wl_event_source_timer_update(m_pAnimationTimer, 0);
+ }
+}
+
+void CCursorManager::tickAnimatedCursor() {
+ if (m_sCurrentCursorShapeData.images.size() < 2 || !m_bOurBufferConnected)
+ return;
+
+ m_iCurrentAnimationFrame++;
+ if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size())
+ m_iCurrentAnimationFrame = 0;
+
+ m_vCursorBuffers.emplace_back(std::make_unique<CCursorBuffer>(
+ m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].surface,
+ Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size},
+ Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY}));
+
+ if (g_pCompositor->m_sWLRCursor)
+ wlr_cursor_set_buffer(g_pCompositor->m_sWLRCursor, getCursorBuffer(), m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX,
+ m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY, m_fCursorScale);
+
+ wl_event_source_timer_update(m_pAnimationTimer, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].delay);
+}
+
+SCursorImageData CCursorManager::dataFor(const std::string& name) {
+ const auto IMAGES = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo);
+
+ if (IMAGES.images.empty())
+ return {};
+
+ return IMAGES.images[0];
+}
+
+void CCursorManager::updateTheme() {
+ float highestScale = 1.0;
+
+ for (auto& m : g_pCompositor->m_vMonitors) {
+ if (m->scale > highestScale)
+ highestScale = m->scale;
+ }
+
+ if (highestScale * m_iSize == m_sCurrentStyleInfo.size)
+ return;
+
+ if (m_sCurrentStyleInfo.size && m_pHyprcursor->valid())
+ m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo);
+
+ m_sCurrentStyleInfo.size = m_iSize * highestScale;
+ m_fCursorScale = highestScale;
+
+ if (m_pHyprcursor->valid())
+ m_pHyprcursor->loadThemeStyle(m_sCurrentStyleInfo);
+
+ setCursorFromName("left_ptr");
+
+ for (auto& m : g_pCompositor->m_vMonitors) {
+ m->forceFullFrames = 5;
+ g_pCompositor->scheduleFrameForMonitor(m.get());
+ }
+}
+
+void CCursorManager::changeTheme(const std::string& name, const int size) {
+ m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(name.empty() ? "" : name.c_str());
+ m_szTheme = name;
+ m_iSize = size;
+
+ setenv("XCURSOR_SIZE", std::to_string(m_iSize).c_str(), true);
+ setenv("XCURSOR_THEME", name.c_str(), true);
+
+ updateTheme();
+}
diff --git a/src/managers/CursorManager.hpp b/src/managers/CursorManager.hpp
new file mode 100644
index 00000000..b1c49b31
--- /dev/null
+++ b/src/managers/CursorManager.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <string>
+#include <hyprcursor/hyprcursor.hpp>
+#include <memory>
+#include "../includes.hpp"
+#include "../helpers/Vector2D.hpp"
+
+struct wlr_buffer;
+struct wlr_xcursor_manager;
+
+class CCursorManager {
+ public:
+ CCursorManager();
+
+ wlr_buffer* getCursorBuffer();
+
+ void setCursorFromName(const std::string& name);
+ void setCursorSurface(wlr_surface* surf, const Vector2D& hotspot);
+
+ void changeTheme(const std::string& name, const int size);
+ void updateTheme();
+ SCursorImageData dataFor(const std::string& name); // for xwayland
+
+ void tickAnimatedCursor();
+
+ class CCursorBuffer {
+ public:
+ CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot);
+ ~CCursorBuffer();
+
+ struct SCursorWlrBuffer {
+ wlr_buffer base;
+ cairo_surface_t* surface = nullptr;
+ bool dropped = false;
+ CCursorBuffer* parent = nullptr;
+ } wlrBuffer;
+
+ private:
+ Vector2D size;
+ Vector2D hotspot;
+
+ friend class CCursorManager;
+ };
+
+ void dropBufferRef(CCursorBuffer* ref);
+
+ bool m_bOurBufferConnected = false;
+
+ private:
+ std::vector<std::unique_ptr<CCursorBuffer>> m_vCursorBuffers;
+
+ std::unique_ptr<Hyprcursor::CHyprcursorManager> m_pHyprcursor;
+
+ std::string m_szTheme = "";
+ int m_iSize = 24;
+ float m_fCursorScale = 1.0;
+
+ Hyprcursor::SCursorStyleInfo m_sCurrentStyleInfo;
+
+ wl_event_source* m_pAnimationTimer = nullptr;
+ int m_iCurrentAnimationFrame = 0;
+ Hyprcursor::SCursorShapeData m_sCurrentCursorShapeData;
+
+ // xcursor fallback
+ wlr_xcursor_manager* m_pWLRXCursorMgr = nullptr;
+};
+
+inline std::unique_ptr<CCursorManager> g_pCursorManager; \ No newline at end of file
diff --git a/src/meson.build b/src/meson.build
index 2d72280c..59771fcf 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -11,6 +11,7 @@ executable('Hyprland', src,
dependency('wayland-client'),
wlroots.get_variable('wlroots'),
dependency('cairo'),
+ dependency('hyprcursor'),
dependency('hyprlang', version: '>= 0.3.2'),
dependency('libdrm'),
dependency('egl'),
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 4e76a972..fd3309e1 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -4,6 +4,7 @@
#include "../helpers/Region.hpp"
#include <algorithm>
#include "../config/ConfigValue.hpp"
+#include "../managers/CursorManager.hpp"
extern "C" {
#include <xf86drm.h>
@@ -2193,7 +2194,7 @@ void CHyprRenderer::setCursorFromName(const std::string& name, bool force) {
if (m_bCursorHidden && !force)
return;
- wlr_cursor_set_xcursor(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sWLRXCursorMgr, name.c_str());
+ g_pCursorManager->setCursorFromName(name);
}
void CHyprRenderer::ensureCursorRenderingMode() {