From 016da234d0e852de3ef20eb2e89ac58d2a85f6e7 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Sun, 21 Jul 2024 13:09:54 +0200 Subject: Core: Move to aquamarine (#6608) Moves Hyprland from wlroots to aquamarine for the backend. --------- Signed-off-by: Vaxry Co-authored-by: Mihai Fufezan Co-authored-by: Jan Beich Co-authored-by: vaxerski Co-authored-by: UjinT34 <41110182+UjinT34@users.noreply.github.com> Co-authored-by: Tom Englund Co-authored-by: Ikalco <73481042+ikalco@users.noreply.github.com> Co-authored-by: diniamo --- src/Compositor.cpp | 391 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 282 insertions(+), 109 deletions(-) (limited to 'src/Compositor.cpp') diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 7ffccf36..c7a0cfb1 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -6,7 +6,10 @@ #include "managers/PointerManager.hpp" #include "managers/SeatManager.hpp" #include "managers/eventLoop/EventLoopManager.hpp" +#include #include +#include +#include #include #include "debug/HyprCtl.hpp" #include "debug/CrashReporter.hpp" @@ -22,16 +25,20 @@ #include "protocols/core/Compositor.hpp" #include "protocols/core/Subcompositor.hpp" #include "desktop/LayerSurface.hpp" +#include "render/Renderer.hpp" #include "xwayland/XWayland.hpp" #include -using namespace Hyprutils::String; +#include #include #include #include #include +using namespace Hyprutils::String; +using namespace Aquamarine; + int handleCritSignal(int signo, void* data) { Debug::log(LOG, "Hyprland received signal {}", signo); @@ -72,6 +79,23 @@ void handleUserSignal(int sig) { } } +static LogLevel aqLevelToHl(Aquamarine::eBackendLogLevel level) { + switch (level) { + case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return TRACE; + case Aquamarine::eBackendLogLevel::AQ_LOG_DEBUG: return LOG; + case Aquamarine::eBackendLogLevel::AQ_LOG_ERROR: return ERR; + case Aquamarine::eBackendLogLevel::AQ_LOG_WARNING: return WARN; + case Aquamarine::eBackendLogLevel::AQ_LOG_CRITICAL: return CRIT; + default: break; + } + + return NONE; +} + +void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) { + Debug::log(aqLevelToHl(level), "[AQ] {}", msg); +} + void CCompositor::bumpNofile() { if (!getrlimit(RLIMIT_NOFILE, &m_sOriginalNofile)) Debug::log(LOG, "Old rlimit: soft -> {}, hard -> {}", m_sOriginalNofile.rlim_cur, m_sOriginalNofile.rlim_max); @@ -180,6 +204,9 @@ void CCompositor::setRandomSplash() { m_szCurrentSplash = SPLASHES[distribution(engine)]; } +static std::vector> pendingOutputs; + +// void CCompositor::initServer() { m_sWLDisplay = wl_display_create(); @@ -200,102 +227,179 @@ void CCompositor::initServer() { if (envEnabled("HYPRLAND_TRACE")) Debug::trace = true; - wlr_log_init(WLR_INFO, NULL); + Aquamarine::SBackendOptions options; + options.logFunction = aqLog; - if (envEnabled("HYPRLAND_LOG_WLR")) - wlr_log_init(WLR_DEBUG, Debug::wlrLog); - else - wlr_log_init(WLR_ERROR, Debug::wlrLog); + std::vector implementations; + Aquamarine::SBackendImplementationOptions option; + option.backendType = Aquamarine::eBackendType::AQ_BACKEND_HEADLESS; + option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_MANDATORY; + implementations.emplace_back(option); + option.backendType = Aquamarine::eBackendType::AQ_BACKEND_DRM; + option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_IF_AVAILABLE; + implementations.emplace_back(option); + option.backendType = Aquamarine::eBackendType::AQ_BACKEND_WAYLAND; + option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_FALLBACK; + implementations.emplace_back(option); - m_sWLRBackend = wlr_backend_autocreate(m_sWLEventLoop, &m_sWLRSession); + m_pAqBackend = CBackend::create(implementations, options); - if (!m_sWLRBackend) { - Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues."); - throwError("wlr_backend_autocreate() failed!"); + if (!m_pAqBackend) { + Debug::log(CRIT, + "m_pAqBackend was null! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a Wayland " + "session, NOT an X11 one."); + throwError("CBackend::create() failed!"); } - bool isHeadlessOnly = true; - wlr_multi_for_each_backend( - m_sWLRBackend, - [](wlr_backend* backend, void* isHeadlessOnly) { - if (!wlr_backend_is_headless(backend) && !wlr_backend_is_libinput(backend)) - *(bool*)isHeadlessOnly = false; - }, - &isHeadlessOnly); + // TODO: headless only - if (isHeadlessOnly) { - m_sWLRRenderer = wlr_renderer_autocreate(m_sWLRBackend); // TODO: remove this, it's barely needed now. - } else { - m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend); - if (m_iDRMFD < 0) { - Debug::log(CRIT, "Couldn't query the DRM FD!"); - throwError("wlr_backend_get_drm_fd() failed!"); - } + initAllSignals(); - m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD); + if (!m_pAqBackend->start()) { + Debug::log(CRIT, + "m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a " + "Wayland session, NOT an X11 one."); + throwError("CBackend::create() failed!"); } - if (!m_sWLRRenderer) { - Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues."); - throwError("wlr_gles2_renderer_create_with_drm_fd() failed!"); - } + m_bInitialized = true; - m_sWLRAllocator = wlr_allocator_autocreate(m_sWLRBackend, m_sWLRRenderer); + m_iDRMFD = m_pAqBackend->drmFD(); + Debug::log(LOG, "Running on DRMFD: {}", m_iDRMFD); - if (!m_sWLRAllocator) { - Debug::log(CRIT, "m_sWLRAllocator was NULL!"); - throwError("wlr_allocator_autocreate() failed!"); + // get socket, avoid using 0 + for (int candidate = 1; candidate <= 32; candidate++) { + const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate)); + const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str()); + if (RETVAL >= 0) { + m_szWLDisplaySocket = CANDIDATESTR; + Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL); + break; + } else { + Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate); + } } - m_sWLREGL = wlr_gles2_renderer_get_egl(m_sWLRRenderer); + if (m_szWLDisplaySocket.empty()) { + Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto"); + const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay); + if (SOCKETSTR) + m_szWLDisplaySocket = SOCKETSTR; + } - if (!m_sWLREGL) { - Debug::log(CRIT, "m_sWLREGL was NULL!"); - throwError("wlr_gles2_renderer_get_egl() failed!"); + if (m_szWLDisplaySocket.empty()) { + Debug::log(CRIT, "m_szWLDisplaySocket NULL!"); + throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)"); } + Debug::log(LOG, "Setting WAYLAND_DISPLAY to {}", m_szWLDisplaySocket); + setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1); + setenv("XDG_SESSION_TYPE", "wayland", 1); + initManagers(STAGE_BASICINIT); - m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend); - if (!m_sWRLDRMLeaseMgr) { - Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager"); - Debug::log(INFO, "VR will not be available"); + initManagers(STAGE_LATE); + + for (auto& o : pendingOutputs) { + onNewMonitor(o); } + pendingOutputs.clear(); +} - m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop); +void CCompositor::initAllSignals() { + m_pAqBackend->events.newOutput.registerStaticListener( + [this](void* p, std::any data) { + auto output = std::any_cast>(data); + Debug::log(LOG, "New aquamarine output with name {}", output->name); + if (m_bInitialized) + onNewMonitor(output); + else + pendingOutputs.emplace_back(output); + }, + nullptr); + + m_pAqBackend->events.newPointer.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine pointer with name {}", dev->getName()); + g_pInputManager->newMouse(dev); + g_pInputManager->updateCapabilities(); + }, + nullptr); + + m_pAqBackend->events.newKeyboard.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine keyboard with name {}", dev->getName()); + g_pInputManager->newKeyboard(dev); + g_pInputManager->updateCapabilities(); + }, + nullptr); + + m_pAqBackend->events.newTouch.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine touch with name {}", dev->getName()); + g_pInputManager->newTouchDevice(dev); + g_pInputManager->updateCapabilities(); + }, + nullptr); - if (!m_sWLRHeadlessBackend) { - Debug::log(CRIT, "Couldn't create the headless backend"); - throwError("wlr_headless_backend_create() failed!"); - } + m_pAqBackend->events.newSwitch.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine switch with name {}", dev->getName()); + g_pInputManager->newSwitch(dev); + }, + nullptr); - wlr_multi_backend_add(m_sWLRBackend, m_sWLRHeadlessBackend); + m_pAqBackend->events.newTablet.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine tablet with name {}", dev->getName()); + g_pInputManager->newTablet(dev); + }, + nullptr); - initManagers(STAGE_LATE); -} + m_pAqBackend->events.newTabletPad.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine tablet pad with name {}", dev->getName()); + g_pInputManager->newTabletPad(dev); + }, + nullptr); -void CCompositor::initAllSignals() { - addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend"); - addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend"); - addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer"); + if (m_pAqBackend->hasSession()) { + m_pAqBackend->session->events.changeActive.registerStaticListener( + [this](void*, std::any) { + if (m_pAqBackend->session->active) { + Debug::log(LOG, "Session got activated!"); - if (m_sWRLDRMLeaseMgr) - addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM"); + m_bSessionActive = true; - if (m_sWLRSession) - addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session"); -} + for (auto& m : m_vMonitors) { + scheduleFrameForMonitor(m.get()); + g_pHyprRenderer->applyMonitorRule(m.get(), &m->activeMonitorRule, true); + } -void CCompositor::removeAllSignals() { - removeWLSignal(&Events::listen_newOutput); - removeWLSignal(&Events::listen_newInput); - removeWLSignal(&Events::listen_RendererDestroy); + g_pConfigManager->m_bWantsMonitorReload = true; + } else { + Debug::log(LOG, "Session got deactivated!"); - if (m_sWRLDRMLeaseMgr) - removeWLSignal(&Events::listen_leaseRequest); + m_bSessionActive = false; + + for (auto& m : m_vMonitors) { + m->noFrameSchedule = true; + m->framesToSkip = 1; + } + } + }, + nullptr); + } +} - if (m_sWLRSession) - removeWLSignal(&Events::listen_sessionActive); +void CCompositor::removeAllSignals() { + ; } void CCompositor::cleanEnvironment() { @@ -309,7 +413,7 @@ void CCompositor::cleanEnvironment() { unsetenv("XDG_BACKEND"); unsetenv("XDG_CURRENT_DESKTOP"); - if (m_sWLRSession) { + if (m_pAqBackend->hasSession()) { const auto CMD = #ifdef USES_SYSTEMD "systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " @@ -352,7 +456,7 @@ void CCompositor::cleanup() { for (auto& m : m_vMonitors) { g_pHyprOpenGL->destroyMonitorResources(m.get()); - wlr_output_state_set_enabled(m->state.wlr(), false); + m->output->state->setEnabled(false); m->state.commit(); } @@ -389,14 +493,8 @@ void CCompositor::cleanup() { g_pHyprCtl.reset(); g_pEventLoopManager.reset(); - if (m_sWLRRenderer) - wlr_renderer_destroy(m_sWLRRenderer); - - if (m_sWLRAllocator) - wlr_allocator_destroy(m_sWLRAllocator); - - if (m_sWLRBackend) - wlr_backend_destroy(m_sWLRBackend); + if (m_pAqBackend) + m_pAqBackend.reset(); if (m_critSigSource) wl_event_source_remove(m_critSigSource); @@ -442,6 +540,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the PointerManager!"); g_pPointerManager = std::make_unique(); + + Debug::log(LOG, "Creating the EventManager!"); + g_pEventManager = std::make_unique(); } break; case STAGE_BASICINIT: { Debug::log(LOG, "Creating the CHyprOpenGLImpl!"); @@ -472,9 +573,6 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the SessionLockManager!"); g_pSessionLockManager = std::make_unique(); - Debug::log(LOG, "Creating the EventManager!"); - g_pEventManager = std::make_unique(); - Debug::log(LOG, "Creating the HyprDebugOverlay!"); g_pDebugOverlay = std::make_unique(); @@ -517,26 +615,23 @@ void CCompositor::removeLockFile() { void CCompositor::prepareFallbackOutput() { // create a backup monitor - wlr_backend* headless = nullptr; - wlr_multi_for_each_backend( - m_sWLRBackend, - [](wlr_backend* b, void* data) { - if (wlr_backend_is_headless(b)) - *((wlr_backend**)data) = b; - }, - &headless); + SP headless; + for (auto& impl : m_pAqBackend->getImplementations()) { + if (impl->type() == Aquamarine::AQ_BACKEND_HEADLESS) { + headless = impl; + break; + } + } if (!headless) { - Debug::log(WARN, "Unsafe state will be ineffective, no fallback output"); + Debug::log(WARN, "No headless in prepareFallbackOutput?!"); return; } - wlr_headless_add_output(headless, 1920, 1080); + headless->createOutput(); } void CCompositor::startCompositor(std::string socketName, int socketFd) { - initAllSignals(); - if (!socketName.empty() && socketFd != -1) { fcntl(socketFd, F_SETFD, FD_CLOEXEC); const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd); @@ -568,15 +663,15 @@ void CCompositor::startCompositor(std::string socketName, int socketFd) { if (m_szWLDisplaySocket.empty()) { Debug::log(CRIT, "m_szWLDisplaySocket NULL!"); - wlr_backend_destroy(m_sWLRBackend); throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)"); } setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1); + setenv("XDG_SESSION_TYPE", "wayland", 1); signal(SIGPIPE, SIG_IGN); - if (m_sWLRSession /* Session-less Hyprland usually means a nest, don't update the env in that case */) { + if (m_pAqBackend->hasSession() /* Session-less Hyprland usually means a nest, don't update the env in that case */) { const auto CMD = #ifdef USES_SYSTEMD "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " @@ -588,13 +683,6 @@ void CCompositor::startCompositor(std::string socketName, int socketFd) { Debug::log(LOG, "Running on WAYLAND_DISPLAY: {}", m_szWLDisplaySocket); - if (!wlr_backend_start(m_sWLRBackend)) { - Debug::log(CRIT, "Backend did not start!"); - wlr_backend_destroy(m_sWLRBackend); - wl_display_destroy(m_sWLDisplay); - throwError("The backend could not start!"); - } - prepareFallbackOutput(); g_pHyprRenderer->setCursorFromName("left_ptr"); @@ -882,7 +970,7 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo return vec - pWindow->m_vRealPosition.goal() - std::get<1>(iterData) + Vector2D{geom.x, geom.y}; } -CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) { +CMonitor* CCompositor::getMonitorFromOutput(SP out) { for (auto& m : m_vMonitors) { if (m->output == out) { return m.get(); @@ -892,7 +980,7 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) { return nullptr; } -CMonitor* CCompositor::getRealMonitorFromOutput(wlr_output* out) { +CMonitor* CCompositor::getRealMonitorFromOutput(SP out) { for (auto& m : m_vRealMonitors) { if (m->output == out) { return m.get(); @@ -2240,8 +2328,12 @@ void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMod g_pInputManager->recheckIdleInhibitorStatus(); - // DMAbuf stuff for direct scanout - g_pHyprRenderer->setWindowScanoutMode(pWindow); + // further updates require a monitor + if (!PMONITOR) + return; + + // send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't. + g_pHyprRenderer->setSurfaceScanoutMode(pWindow->m_pWLSurface->resource(), on ? PMONITOR->self.lock() : nullptr); g_pConfigManager->ensureVRR(PMONITOR); } @@ -2282,8 +2374,8 @@ void CCompositor::updateWorkspaceWindowData(const int& id) { } } -void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) { - if ((m_sWLRSession && !m_sWLRSession->active) || !m_bSessionActive) +void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor, IOutput::scheduleFrameReason reason) { + if ((m_pAqBackend->hasSession() && !m_pAqBackend->session->active) || !m_bSessionActive) return; if (!pMonitor->m_bEnabled) @@ -2292,7 +2384,7 @@ void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) { if (pMonitor->renderingActive) pMonitor->pendingFrame = true; - wlr_output_schedule_frame(pMonitor->output); + pMonitor->output->scheduleFrame(reason); } PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) { @@ -2808,3 +2900,84 @@ PHLWINDOW CCompositor::windowForCPointer(CWindow* pWindow) { return {}; } + +static void checkDefaultCursorWarp(SP PNEWMONITOR, std::string monitorName) { + static auto PCURSORMONITOR = CConfigValue("cursor:default_monitor"); + static auto firstMonitorAdded = std::chrono::system_clock::now(); + static bool cursorDefaultDone = false; + static bool firstLaunch = true; + + const auto POS = PNEWMONITOR->middle(); + + // by default, cursor should be set to first monitor detected + // this is needed as a default if the monitor given in config above doesn't exist + if (firstLaunch) { + firstLaunch = false; + g_pCompositor->warpCursorTo(POS, true); + g_pInputManager->refocus(); + } + + if (cursorDefaultDone || *PCURSORMONITOR == STRVAL_EMPTY) + return; + + // after 10s, don't set cursor to default monitor + auto timePassedSec = std::chrono::duration_cast(std::chrono::system_clock::now() - firstMonitorAdded); + if (timePassedSec.count() > 10) { + cursorDefaultDone = true; + return; + } + + if (*PCURSORMONITOR == monitorName) { + cursorDefaultDone = true; + g_pCompositor->warpCursorTo(POS, true); + g_pInputManager->refocus(); + } +} + +void CCompositor::onNewMonitor(SP output) { + // add it to real + auto PNEWMONITOR = g_pCompositor->m_vRealMonitors.emplace_back(makeShared()); + if (std::string("HEADLESS-1") == output->name) { + g_pCompositor->m_pUnsafeOutput = PNEWMONITOR.get(); + output->name = "FALLBACK"; // we are allowed to do this :) + } + + Debug::log(LOG, "New output with name {}", output->name); + + PNEWMONITOR->szName = output->name; + PNEWMONITOR->output = output; + PNEWMONITOR->self = PNEWMONITOR; + const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? output == g_pCompositor->m_pUnsafeOutput->output : false; + PNEWMONITOR->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(output->name); + PNEWMONITOR->isUnsafeFallback = FALLBACK; + + EMIT_HOOK_EVENT("newMonitor", PNEWMONITOR); + + if (!FALLBACK) + PNEWMONITOR->onConnect(false); + + if (!PNEWMONITOR->m_bEnabled || FALLBACK) + return; + + // ready to process if we have a real monitor + + if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled) + g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR.get(); + + g_pCompositor->m_bReadyToProcess = true; + + g_pConfigManager->m_bWantsMonitorReload = true; + g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR.get(), IOutput::AQ_SCHEDULE_NEW_MONITOR); + + checkDefaultCursorWarp(PNEWMONITOR, output->name); + + for (auto& w : g_pCompositor->m_vWindows) { + if (w->m_iMonitorID == PNEWMONITOR->ID) { + w->m_iLastSurfaceMonitorID = -1; + w->updateSurfaceScaleTransformDetails(); + } + } + + g_pHyprRenderer->damageMonitor(PNEWMONITOR.get()); + Events::listener_monitorFrame(PNEWMONITOR.get(), nullptr); +} -- cgit v1.2.3