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": [ @@ -24,6 +48,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" @@ -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 #include #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(); + + Debug::log(LOG, "Creating the CursorManager!"); + g_pCursorManager = std::make_unique(); } 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 #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 #include #include -#include #include #include #include @@ -76,6 +75,7 @@ extern "C" { #include #include #include +#include #include #include #include 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 +#include +} + +static int cursorAnimTimer(void* data) { + g_pCursorManager->tickAnimatedCursor(); + return 1; +} + +CCursorManager::CCursorManager() { + m_pHyprcursor = std::make_unique(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("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(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( + 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(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 +#include +#include +#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> m_vCursorBuffers; + + std::unique_ptr 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 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 #include "../config/ConfigValue.hpp" +#include "../managers/CursorManager.hpp" extern "C" { #include @@ -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() {