diff --git a/CMakeLists.txt b/CMakeLists.txt index a52c798a..b3bc5526 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,5 +218,6 @@ protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false) protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false) +protocol("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false) diff --git a/protocols/meson.build b/protocols/meson.build index 60ae20cb..cc111e98 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -25,6 +25,7 @@ protocols = [ [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], [wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'], + [wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-output-power-management-unstable-v1.xml'], diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 0672a543..1cb378db 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -257,6 +257,8 @@ void CCompositor::initServer() { m_sWLRCursorShapeMgr = wlr_cursor_shape_manager_v1_create(m_sWLDisplay, 1); + m_sWLRTearingControlMgr = wlr_tearing_control_manager_v1_create(m_sWLDisplay, 1); + if (!m_sWLRHeadlessBackend) { Debug::log(CRIT, "Couldn't create the headless backend"); throwError("wlr_headless_backend_create() failed!"); @@ -315,6 +317,7 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRSessionLockMgr->events.new_lock, &Events::listen_newSessionLock, m_sWLRSessionLockMgr, "SessionLockMgr"); addWLSignal(&m_sWLRGammaCtrlMgr->events.set_gamma, &Events::listen_setGamma, m_sWLRGammaCtrlMgr, "GammaCtrlMgr"); addWLSignal(&m_sWLRCursorShapeMgr->events.request_set_shape, &Events::listen_setCursorShape, m_sWLRCursorShapeMgr, "CursorShapeMgr"); + addWLSignal(&m_sWLRTearingControlMgr->events.new_object, &Events::listen_newTearingHint, m_sWLRTearingControlMgr, "TearingControlMgr"); if (m_sWRLDRMLeaseMgr) addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM"); @@ -2475,7 +2478,13 @@ int CCompositor::getNewSpecialID() { } void CCompositor::performUserChecks() { - // empty + const auto atomicEnv = getenv("WLR_DRM_NO_ATOMIC"); + const auto atomicEnvStr = std::string(atomicEnv ? atomicEnv : ""); + if (g_pConfigManager->getInt("general:allow_tearing") == 1 && atomicEnvStr != "1") { + g_pHyprNotificationOverlay->addNotification("You have enabled tearing, but immediate presentations are not available on your configuration. Try adding " + "env = WLR_DRM_NO_ATOMIC,1 to your config.", + CColor(0), 15000, ICON_WARNING); + } } void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 06d15c3c..99839b06 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -85,6 +85,7 @@ class CCompositor { wlr_session_lock_manager_v1* m_sWLRSessionLockMgr; wlr_gamma_control_manager_v1* m_sWLRGammaCtrlMgr; wlr_cursor_shape_manager_v1* m_sWLRCursorShapeMgr; + wlr_tearing_control_manager_v1* m_sWLRTearingControlMgr; // ------------------------------------------------- // std::string m_szWLDisplaySocket = ""; diff --git a/src/Window.cpp b/src/Window.cpp index 4ee40dff..9055101e 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -414,6 +414,11 @@ void CWindow::onUnmap() { if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID) PMONITOR->setSpecialWorkspace(nullptr); } + + const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); + + if (PMONITOR && PMONITOR->solitaryClient == this) + PMONITOR->solitaryClient = nullptr; } void CWindow::onMap() { @@ -492,6 +497,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { } else if (r.szRule == "opaque") { if (!m_sAdditionalConfigData.forceOpaqueOverridden) m_sAdditionalConfigData.forceOpaque = true; + } else if (r.szRule == "immediate") { + m_sAdditionalConfigData.forceTearing = true; } else if (r.szRule.find("rounding") == 0) { try { m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); @@ -580,6 +587,7 @@ void CWindow::updateDynamicRules() { m_sAdditionalConfigData.borderSize = -1; m_sAdditionalConfigData.keepAspectRatio = false; m_sAdditionalConfigData.xray = -1; + m_sAdditionalConfigData.forceTearing = false; const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this); for (auto& r : WINDOWRULES) { @@ -925,3 +933,7 @@ int CWindow::getRealBorderSize() { return g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; } + +bool CWindow::canBeTorn() { + return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint) && g_pHyprRenderer->m_bTearingEnvSatisfied; +} diff --git a/src/Window.hpp b/src/Window.hpp index 81b6156f..dd16fdcb 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -11,14 +11,16 @@ #include "macros.hpp" #include "managers/XWaylandManager.hpp" -enum eIdleInhibitMode { +enum eIdleInhibitMode +{ IDLEINHIBIT_NONE = 0, IDLEINHIBIT_ALWAYS, IDLEINHIBIT_FULLSCREEN, IDLEINHIBIT_FOCUS }; -enum eGroupRules { +enum eGroupRules +{ // effective only during first map, except for _ALWAYS variant GROUP_NONE = 0, GROUP_SET = 1 << 0, // Open as new group or add to focused group @@ -138,6 +140,7 @@ struct SWindowAdditionalConfigData { CWindowOverridableVar keepAspectRatio = false; CWindowOverridableVar xray = -1; // -1 means unset, takes precedence over the renderdata one CWindowOverridableVar borderSize = -1; // -1 means unset, takes precedence over the renderdata one + CWindowOverridableVar forceTearing = false; }; struct SWindowRule { @@ -317,6 +320,8 @@ class CWindow { } m_sGroupData; uint16_t m_eGroupRules = GROUP_NONE; + bool m_bTearingHint = false; + // For the list lookup bool operator==(const CWindow& rhs) { return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && @@ -348,6 +353,7 @@ class CWindow { Vector2D middle(); bool opaque(); float rounding(); + bool canBeTorn(); int getRealBorderSize(); void updateSpecialRenderData(); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 7556776e..fc7a9e3e 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -87,6 +87,7 @@ void CConfigManager::setDefaultVars() { configValues["general:extend_border_grab_area"].intValue = 15; configValues["general:hover_icon_on_border"].intValue = 1; configValues["general:layout"].strValue = "dwindle"; + configValues["general:allow_tearing"].intValue = 0; configValues["misc:disable_hyprland_logo"].intValue = 0; configValues["misc:disable_splash_rendering"].intValue = 0; @@ -279,7 +280,7 @@ void CConfigManager::setDeviceDefaultVars(const std::string& dev) { cfgValues["scroll_button_lock"].intValue = 0; cfgValues["transform"].intValue = 0; cfgValues["output"].strValue = STRVAL_EMPTY; - cfgValues["enabled"].intValue = 1; // only for mice / touchpads + cfgValues["enabled"].intValue = 1; // only for mice / touchpads cfgValues["region_position"].vecValue = Vector2D(); // only for tablets cfgValues["region_size"].vecValue = Vector2D(); // only for tablets } @@ -912,7 +913,7 @@ bool windowRuleValid(const std::string& RULE) { RULE != "nomaximizerequest" && RULE != "fakefullscreen" && RULE != "nomaxsize" && RULE != "pin" && RULE != "noanim" && RULE != "dimaround" && RULE != "windowdance" && RULE != "maximize" && RULE != "keepaspectratio" && RULE.find("animation") != 0 && RULE.find("rounding") != 0 && RULE.find("workspace") != 0 && RULE.find("bordercolor") != 0 && RULE != "forcergbx" && RULE != "noinitialfocus" && RULE != "stayfocused" && RULE.find("bordersize") != 0 && RULE.find("xray") != 0 && - RULE.find("center") != 0 && RULE.find("group") != 0); + RULE.find("center") != 0 && RULE.find("group") != 0 && RULE != "immediate"); } bool layerRuleValid(const std::string& RULE) { @@ -1244,7 +1245,7 @@ void CConfigManager::handleSource(const std::string& command, const std::string& int linenum = 1; if (ifs.is_open()) { auto configCurrentPathBackup = configCurrentPath; - + while (std::getline(ifs, line)) { // Read line by line. try { @@ -1265,7 +1266,7 @@ void CConfigManager::handleSource(const std::string& command, const std::string& } ifs.close(); - + configCurrentPath = configCurrentPathBackup; } } diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 0aafe664..8eced97d 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -1009,6 +1009,8 @@ std::string dispatchSetProp(std::string request) { PWINDOW->m_sSpecialRenderData.borderSize.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else if (PROP == "keepaspectratio") { PWINDOW->m_sAdditionalConfigData.keepAspectRatio.forceSetIgnoreLocked(configStringToInt(VAL), lock); + } else if (PROP == "immediate") { + PWINDOW->m_sAdditionalConfigData.forceTearing.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else { return "prop not found"; } diff --git a/src/events/Events.hpp b/src/events/Events.hpp index f89032ea..fc68880e 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -174,4 +174,7 @@ namespace Events { // Cursor shape LISTENER(setCursorShape); + + // Tearing hints + LISTENER(newTearingHint); }; diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index d4e7bad9..1df4be3a 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -237,3 +237,43 @@ void Events::listener_setCursorShape(wl_listener* listener, void* data) { g_pInputManager->processMouseRequest(E); } + +void Events::listener_newTearingHint(wl_listener* listener, void* data) { + const auto TCTL = (wlr_tearing_control_v1*)data; + + const auto PWINDOW = g_pCompositor->getWindowFromSurface(TCTL->surface); + + if (!PWINDOW) { + Debug::log(ERR, "Tearing hint {} was attached to an unknown surface", (uintptr_t)data); + return; + } + + Debug::log(LOG, "New tearing hint for window {} at {}", PWINDOW, (uintptr_t)data); + + const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique()).get(); + NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data; + + NEWCTRL->hyprListener_destroy.initCallback( + &NEWCTRL->pWlrHint->events.destroy, + [&](void* owner, void* data) { + Debug::log(LOG, "Destroyed {} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint); + + std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; }); + }, + NEWCTRL, "TearingController"); + + NEWCTRL->hyprListener_set.initCallback( + &NEWCTRL->pWlrHint->events.set_hint, + [&](void* owner, void* data) { + const auto TEARINGHINT = (STearingController*)owner; + + const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface); + + if (PWINDOW) { + PWINDOW->m_bTearingHint = TEARINGHINT->pWlrHint->hint; + + Debug::log(LOG, "Hint {} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->hint); + } + }, + NEWCTRL, "TearingController"); +} diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index 947f9e7f..d006c2f2 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -149,6 +149,14 @@ void Events::listener_monitorFrame(void* owner, void* data) { if (!PMONITOR->m_bEnabled) return; + if (PMONITOR->ignoreNextFlipEvent) { + Debug::log(LOG, "Ignore next flip event for {}", PMONITOR->szName); + PMONITOR->ignoreNextFlipEvent = false; + return; + } + + PMONITOR->renderingFromVblankEvent = true; + static auto* const PENABLERAT = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time")->intValue; static auto* const PRATSAFE = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone")->intValue; @@ -181,6 +189,8 @@ void Events::listener_monitorFrame(void* owner, void* data) { } else { g_pHyprRenderer->renderMonitor(PMONITOR); } + + PMONITOR->renderingFromVblankEvent = false; } void Events::listener_monitorDestroy(void* owner, void* data) { diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index dd9580d1..0d0b2a64 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -40,6 +40,8 @@ void CMonitor::onConnect(bool noRule) { hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this); hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this); + canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm + if (m_bEnabled) { wlr_output_enable(output, 1); wlr_output_commit(output); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 93f345ba..f8b8554c 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -81,6 +81,13 @@ class CMonitor { CRegion lastFrameDamage; // stores last frame damage + // for tearing + CWindow* solitaryClient = nullptr; + bool canTear = false; + bool nextRenderTorn = false; + bool ignoreNextFlipEvent = false; + bool renderingFromVblankEvent = false; + // for the special workspace. 0 means not open. int specialWorkspaceID = 0; diff --git a/src/helpers/SubsurfaceTree.cpp b/src/helpers/SubsurfaceTree.cpp index 5e3e9e67..5b86f928 100644 --- a/src/helpers/SubsurfaceTree.cpp +++ b/src/helpers/SubsurfaceTree.cpp @@ -245,6 +245,15 @@ void Events::listener_commitSubsurface(void* owner, void* data) { if (pNode->pSurface && pNode->pSurface->exists()) g_pHyprRenderer->damageSurface(pNode->pSurface->wlr(), lx, ly, SCALE); + + if (pNode->pWindowOwner) { + // tearing: if solitary, redraw it. This still might be a single surface window + const auto PMONITOR = g_pCompositor->getMonitorFromID(pNode->pWindowOwner->m_iMonitorID); + if (PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->canTear) { + PMONITOR->nextRenderTorn = true; + g_pHyprRenderer->renderMonitor(PMONITOR); + } + } } void Events::listener_destroySubsurface(void* owner, void* data) { diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index d33af797..e8647426 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -180,7 +180,7 @@ struct SConstraint { Vector2D getLogicConstraintSize(); bool operator==(const SConstraint& b) const { - return constraint == b.constraint; + return constraint == b.constraint; } }; @@ -393,3 +393,14 @@ struct SSwitchDevice { return pWlrDevice == other.pWlrDevice; } }; + +struct STearingController { + wlr_tearing_control_v1* pWlrHint = nullptr; + + DYNLISTENER(set); + DYNLISTENER(destroy); + + bool operator==(const STearingController& other) { + return pWlrHint == other.pWlrHint; + } +}; diff --git a/src/includes.hpp b/src/includes.hpp index c81ee9fe..1205f55c 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -105,6 +105,7 @@ extern "C" { #include #include #include +#include #include diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index beb55c5d..e790bf7f 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -118,7 +118,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (*PZOOMFACTOR != 1.f) g_pHyprRenderer->damageMonitor(PMONITOR); - g_pCompositor->scheduleFrameForMonitor(PMONITOR); + if (!PMONITOR->solitaryClient) // if there is a solitary client we can't schedule a frame here, this will completely fuck up drm + g_pCompositor->scheduleFrameForMonitor(PMONITOR); CWindow* forcedFocus = m_pForcedFocus; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 6dcf0b44..982edd4b 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -3,6 +3,13 @@ #include "linux-dmabuf-unstable-v1-protocol.h" #include "../helpers/Region.hpp" +CHyprRenderer::CHyprRenderer() { + const auto ENV = getenv("WLR_DRM_NO_ATOMIC"); + + if (ENV && std::string(ENV) == "1") + m_bTearingEnvSatisfied = true; +} + void renderSurface(struct wlr_surface* surface, int x, int y, void* data) { const auto TEXTURE = wlr_surface_get_texture(surface); const auto RDATA = (SRenderData*)data; @@ -702,51 +709,12 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { if (!wlr_output_is_direct_scanout_allowed(pMonitor->output)) return false; - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pMonitor->activeWorkspace); - - if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || g_pCompositor->m_sSeat.exclusiveClient || pMonitor->specialWorkspaceID) - return false; - - const auto PCANDIDATE = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); + const auto PCANDIDATE = pMonitor->solitaryClient; if (!PCANDIDATE) - return false; // ???? - - if (PCANDIDATE->m_fAlpha.fl() != 1.f || PCANDIDATE->m_fActiveInactiveAlpha.fl() != 1.f || PWORKSPACE->m_fAlpha.fl() != 1.f) return false; - if (PCANDIDATE->m_vRealSize.vec() != pMonitor->vecSize || PCANDIDATE->m_vRealPosition.vec() != pMonitor->vecPosition || PCANDIDATE->m_vRealPosition.isBeingAnimated() || - PCANDIDATE->m_vRealSize.isBeingAnimated()) - return false; - - if (!pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY].empty()) - return false; - - for (auto& topls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { - if (topls->alpha.fl() != 0.f) - return false; - } - - // check if it did not open any subsurfaces or shit - int surfaceCount = 0; - if (PCANDIDATE->m_bIsX11) { - surfaceCount = 1; - - // check opaque - if (PCANDIDATE->m_uSurface.xwayland->has_alpha) - return false; - } else { - wlr_xdg_surface_for_each_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); - wlr_xdg_surface_for_each_popup_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); - - if (!PCANDIDATE->m_uSurface.xdg->surface->opaque) - return false; - } - - if (surfaceCount != 1) - return false; - - const auto PSURFACE = PCANDIDATE->m_pWLSurface.wlr(); + const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE); if (!PSURFACE || PSURFACE->current.scale != pMonitor->output->scale || PSURFACE->current.transform != pMonitor->output->transform) return false; @@ -754,9 +722,8 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { // finally, we should be GTG. wlr_output_attach_buffer(pMonitor->output, &PSURFACE->buffer->base); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test(pMonitor->output)) return false; - } timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -790,6 +757,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue; static auto* const PBACKGROUNDCOLOR = &g_pConfigManager->getConfigValuePtr("misc:background_color")->intValue; static auto* const PANIMENABLED = &g_pConfigManager->getConfigValuePtr("animations:enabled")->intValue; + static auto* const PTEARINGENABLED = &g_pConfigManager->getConfigValuePtr("general:allow_tearing")->intValue; static int damageBlinkCleanup = 0; // because double-buffered @@ -876,8 +844,38 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { } } - // Direct scanout first - if (!*PNODIRECTSCANOUT) { + // tearing and DS first + bool shouldTear = false; + bool canTear = *PTEARINGENABLED && g_pHyprOpenGL->m_RenderData.mouseZoomFactor == 1.0; + recheckSolitaryForMonitor(pMonitor); + if (pMonitor->nextRenderTorn) { + pMonitor->nextRenderTorn = false; + + if (!*PTEARINGENABLED) { + Debug::log(WARN, "Tearing commit requested but the master switch general:allow_tearing is off, ignoring"); + return; + } + + if (g_pHyprOpenGL->m_RenderData.mouseZoomFactor != 1.0) { + Debug::log(WARN, "Tearing commit requested but scale factor is not 1, ignoring"); + return; + } + + if (!pMonitor->canTear) { + Debug::log(WARN, "Tearing commit requested but monitor doesn't support it, ignoring"); + return; + } + + if (pMonitor->solitaryClient) + shouldTear = true; + } else { + // if this is a non-tearing commit, and we are in a state where we should tear + // then this is a vblank commit that we should ignore + if (canTear && pMonitor->solitaryClient && pMonitor->canTear && pMonitor->solitaryClient->canBeTorn() && pMonitor->renderingFromVblankEvent) + return; + } + + if (!*PNODIRECTSCANOUT && !shouldTear) { if (attemptDirectScanout(pMonitor)) { return; } else if (m_pLastScanout) { @@ -1061,6 +1059,8 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { EMIT_HOOK_EVENT("render", RENDER_POST); + pMonitor->output->pending.tearing_page_flip = shouldTear; + if (!wlr_output_commit(pMonitor->output)) { if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, false); @@ -1068,6 +1068,9 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { return; } + if (shouldTear) + pMonitor->ignoreNextFlipEvent = true; + wlr_damage_ring_rotate(&pMonitor->damage); if (UNLOCK_SC) @@ -1986,3 +1989,47 @@ bool CHyprRenderer::canSkipBackBufferClear(CMonitor* pMonitor) { return false; } + +void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { + pMonitor->solitaryClient = nullptr; // reset it, if we find one it will be set. + + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pMonitor->activeWorkspace); + + if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || g_pCompositor->m_sSeat.exclusiveClient || pMonitor->specialWorkspaceID) + return; + + const auto PCANDIDATE = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); + + if (!PCANDIDATE) + return; // ???? + + if (!PCANDIDATE->opaque()) + return; + + if (PCANDIDATE->m_vRealSize.vec() != pMonitor->vecSize || PCANDIDATE->m_vRealPosition.vec() != pMonitor->vecPosition || PCANDIDATE->m_vRealPosition.isBeingAnimated() || + PCANDIDATE->m_vRealSize.isBeingAnimated()) + return; + + if (!pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY].empty()) + return; + + for (auto& topls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + if (topls->alpha.fl() != 0.f) + return; + } + + // check if it did not open any subsurfaces or shit + int surfaceCount = 0; + if (PCANDIDATE->m_bIsX11) { + surfaceCount = 1; + } else { + wlr_xdg_surface_for_each_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); + wlr_xdg_surface_for_each_popup_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount); + } + + if (surfaceCount != 1) + Debug::log(LOG, "fuf: >1 surf"); + + // found one! + pMonitor->solitaryClient = PCANDIDATE; +} diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index f0e19c38..2b437b00 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -33,6 +33,8 @@ struct SSessionLockSurface; class CHyprRenderer { public: + CHyprRenderer(); + void renderMonitor(CMonitor* pMonitor); void outputMgrApplyTest(wlr_output_configuration_v1*, bool); void arrangeLayersForMonitor(const int&); @@ -53,6 +55,7 @@ class CHyprRenderer { void renderLockscreen(CMonitor* pMonitor, timespec* now); void setOccludedForBackLayers(CRegion& region, CWorkspace* pWorkspace); bool canSkipBackBufferClear(CMonitor* pMonitor); + void recheckSolitaryForMonitor(CMonitor* pMonitor); bool m_bWindowRequestedCursorHide = false; bool m_bBlockSurfaceFeedback = false; @@ -61,19 +64,22 @@ class CHyprRenderer { CMonitor* m_pMostHzMonitor = nullptr; bool m_bDirectScanoutBlocked = false; bool m_bSoftwareCursorsLocked = false; + bool m_bTearingEnvSatisfied = false; DAMAGETRACKINGMODES damageTrackingModeFromStr(const std::string&); - bool attemptDirectScanout(CMonitor*); - void setWindowScanoutMode(CWindow*); - void initiateManualCrash(); + bool attemptDirectScanout(CMonitor*); + void setWindowScanoutMode(CWindow*); + void initiateManualCrash(); - bool m_bCrashingInProgress = false; - float m_fCrashingDistort = 0.5f; - wl_event_source* m_pCrashingLoop = nullptr; + bool m_bCrashingInProgress = false; + float m_fCrashingDistort = 0.5f; + wl_event_source* m_pCrashingLoop = nullptr; - CTimer m_tRenderTimer; + std::vector> m_vTearingControllers; + + CTimer m_tRenderTimer; private: void arrangeLayerArray(CMonitor*, const std::vector>&, bool, wlr_box*);