From 8e8c4352e5cb9073b8a6c6955799f333530a7c16 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 7 Mar 2024 17:51:47 +0000 Subject: [PATCH] initial hyprcursor support --- CMakeLists.txt | 2 +- src/Compositor.cpp | 17 ++-- src/Compositor.hpp | 1 - src/debug/HyprCtl.cpp | 12 +-- src/events/Misc.cpp | 9 ++- src/helpers/Monitor.cpp | 4 - src/includes.hpp | 2 +- src/managers/CursorManager.cpp | 137 +++++++++++++++++++++++++++++++++ src/managers/CursorManager.hpp | 56 ++++++++++++++ src/render/Renderer.cpp | 3 +- 10 files changed, 209 insertions(+), 34 deletions(-) create mode 100644 src/managers/CursorManager.cpp create mode 100644 src/managers/CursorManager.hpp 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/src/Compositor.cpp b/src/Compositor.cpp index ca722ada..32a2e269 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 5d75b199..6b6124e0 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/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 332e3969..d6fa608d 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() == ',') @@ -1039,16 +1040,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 0ee95ac3..eb751b71 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" // ------------------------------ // // __ __ _____ _____ _____ // @@ -67,10 +68,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); } xcb_disconnect(XCBCONNECTION); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 06ea0fa5..006fa3ca 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..2e113d43 --- /dev/null +++ b/src/managers/CursorManager.cpp @@ -0,0 +1,137 @@ +#include "CursorManager.hpp" +#include "Compositor.hpp" + +extern "C" { +#include +} + +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; + + updateTheme(); + + g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateTheme(); }); +} + +static void cursorBufferDestroy(struct wlr_buffer* wlr_buffer) { + CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base); + buffer->dropped = true; +} + +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); +} + +CCursorManager::CCursorBuffer::~CCursorBuffer() { + if (g_pCursorManager->m_bOurBufferConnected) + wlr_cursor_set_surface(g_pCompositor->m_sWLRCursor, nullptr, 0, 0); + + if (!wlrBuffer.dropped) + wlr_buffer_drop(&wlrBuffer.base); +} + +wlr_buffer* CCursorManager::getCursorBuffer() { + return m_sCursorBuffer ? &m_sCursorBuffer->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) { + if (m_sCursorBuffer) + m_sCursorBuffer.reset(); + + const auto IMAGES = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo); + + m_sCursorBuffer = std::make_unique(IMAGES.images[0].surface, Vector2D{IMAGES.images[0].size, IMAGES.images[0].size}, + Vector2D{IMAGES.images[0].hotspotX, IMAGES.images[0].hotspotY}); + + if (g_pCompositor->m_sWLRCursor) + wlr_cursor_set_buffer(g_pCompositor->m_sWLRCursor, getCursorBuffer(), IMAGES.images[0].hotspotX, IMAGES.images[0].hotspotY, m_fCursorScale); + + m_bOurBufferConnected = true; +} + +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->cursorSurfaceStyleDone(m_sCurrentStyleInfo); + + m_sCurrentStyleInfo.size = m_iSize * highestScale; + m_fCursorScale = highestScale; + + m_pHyprcursor->loadThemeStyle(m_sCurrentStyleInfo); + + setCursorFromName("left_ptr"); +} + +void CCursorManager::changeTheme(const std::string& name, const int size) { + m_pHyprcursor = std::make_unique(name.empty() ? "" : name.c_str(), size); + m_szTheme = name; + m_iSize = size; + + updateTheme(); +} diff --git a/src/managers/CursorManager.hpp b/src/managers/CursorManager.hpp new file mode 100644 index 00000000..7bf0f7d8 --- /dev/null +++ b/src/managers/CursorManager.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include "../includes.hpp" +#include "../helpers/Vector2D.hpp" + +struct wlr_buffer; + +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 + + 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; + } wlrBuffer; + + private: + Vector2D size; + Vector2D hotspot; + + friend class CCursorManager; + }; + + bool m_bOurBufferConnected = false; + + private: + std::unique_ptr m_sCursorBuffer; + + std::unique_ptr m_pHyprcursor; + + std::string m_szTheme = ""; + int m_iSize = 24; + float m_fCursorScale = 1.0; + + Hyprcursor::SCursorStyleInfo m_sCurrentStyleInfo; +}; + +inline std::unique_ptr g_pCursorManager; \ No newline at end of file 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() {