From e9f226797e43cedf17b094bc498272213ed53687 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Tue, 13 Sep 2022 15:25:42 +0200 Subject: [PATCH] Added monitor mirroring --- src/config/ConfigManager.cpp | 19 +++- src/config/ConfigManager.hpp | 1 + src/events/Monitors.cpp | 67 +++++++------ src/helpers/Monitor.cpp | 187 ++++++++++++++++++++++++++++------- src/helpers/Monitor.hpp | 11 +++ src/render/OpenGL.cpp | 29 +++++- src/render/OpenGL.hpp | 9 +- src/render/Renderer.cpp | 24 ++++- src/render/Renderer.hpp | 1 + 9 files changed, 271 insertions(+), 77 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 96a8f4da..7df288fe 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -493,11 +493,16 @@ void CConfigManager::handleMonitor(const std::string& command, const std::string nextItem(); - if (curitem != "") { - // warning for old cfg - Debug::log(ERR, "Error in parsing rule for %s, possibly old config!", newrule.name.c_str()); - parseError = "Error in setting monitor rule. Are you using the old syntax? Confront the wiki."; - return; + while (curitem != "") { + if (curitem == "mirror") { + nextItem(); + newrule.mirrorOf = curitem; + nextItem(); + } else { + Debug::log(ERR, "Config error: invalid monitor syntax"); + parseError = "invalid syntax at \"" + curitem + "\""; + return; + } } if (std::find_if(m_dMonitorRules.begin(), m_dMonitorRules.end(), [&](const auto& other) { return other.name == newrule.name; }) != m_dMonitorRules.end()) @@ -1444,6 +1449,10 @@ void CConfigManager::performMonitorReload() { for (auto& m : g_pCompositor->m_vRealMonitors) { auto rule = getMonitorRuleFor(m->szName); + + // ensure mirror + m->setMirror(rule.mirrorOf); + if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) { overAgain = true; break; diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 1f14d019..cb73e102 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -36,6 +36,7 @@ struct SMonitorRule { std::string defaultWorkspace = ""; bool disabled = false; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + std::string mirrorOf = ""; }; struct SMonitorAdditionalReservedArea { diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index 0fe481c8..211562b0 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -190,7 +190,7 @@ void Events::listener_monitorFrame(void* owner, void* data) { } // if we have no tracking or full tracking, invalidate the entire monitor - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || PMONITOR->forceFullFrames > 0 || damageBlinkCleanup > 0) { + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || PMONITOR->forceFullFrames > 0 || damageBlinkCleanup > 0 || PMONITOR->isMirror() /* why??? */) { pixman_region32_union_rect(&damage, &damage, 0, 0, (int)PMONITOR->vecTransformedSize.x * 10, (int)PMONITOR->vecTransformedSize.y * 10); // wot? pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); @@ -224,38 +224,45 @@ void Events::listener_monitorFrame(void* owner, void* data) { // potentially can save on resources. g_pHyprOpenGL->begin(PMONITOR, &damage); - g_pHyprOpenGL->clear(CColor(17, 17, 17, 255)); - g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" - g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now); + if (PMONITOR->isMirror()) { + g_pHyprOpenGL->renderMirrored(); - // if correct monitor draw hyprerror - if (PMONITOR->ID == 0) - g_pHyprError->draw(); + Debug::log(LOG, "Mirror frame"); + } else { + g_pHyprOpenGL->clear(CColor(17, 17, 17, 255)); + g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" - // for drawing the debug overlay - if (PMONITOR->ID == 0 && *PDEBUGOVERLAY == 1) { - startRenderOverlay = std::chrono::high_resolution_clock::now(); - g_pDebugOverlay->draw(); - endRenderOverlay = std::chrono::high_resolution_clock::now(); + g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now); + + // if correct monitor draw hyprerror + if (PMONITOR->ID == 0) + g_pHyprError->draw(); + + // for drawing the debug overlay + if (PMONITOR->ID == 0 && *PDEBUGOVERLAY == 1) { + startRenderOverlay = std::chrono::high_resolution_clock::now(); + g_pDebugOverlay->draw(); + endRenderOverlay = std::chrono::high_resolution_clock::now(); + } + + if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { + wlr_box monrect = {0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y}; + g_pHyprOpenGL->renderRect(&monrect, CColor(255, 0, 255, 100), 0); + damageBlinkCleanup = 1; + } else if (*PDAMAGEBLINK) { + damageBlinkCleanup++; + if (damageBlinkCleanup > 3) + damageBlinkCleanup = 0; + } + + wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); + + wlr_output_render_software_cursors(PMONITOR->output, NULL); + + wlr_renderer_end(g_pCompositor->m_sWLRRenderer); } - if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { - wlr_box monrect = {0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y}; - g_pHyprOpenGL->renderRect(&monrect, CColor(255,0,255,100), 0); - damageBlinkCleanup = 1; - } else if (*PDAMAGEBLINK) { - damageBlinkCleanup++; - if (damageBlinkCleanup > 3) - damageBlinkCleanup = 0; - } - - wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); - - wlr_output_render_software_cursors(PMONITOR->output, NULL); - - wlr_renderer_end(g_pCompositor->m_sWLRRenderer); - g_pHyprOpenGL->end(); // calc frame damage @@ -272,6 +279,10 @@ void Events::listener_monitorFrame(void* owner, void* data) { pixman_region32_union(&frameDamage, &frameDamage, &damage); wlr_output_set_damage(PMONITOR->output, &frameDamage); + + if (!PMONITOR->mirrors.empty()) + g_pHyprRenderer->damageMirrorsWith(PMONITOR, &frameDamage); + pixman_region32_fini(&frameDamage); pixman_region32_fini(&damage); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 360bc977..509f292d 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -115,50 +115,14 @@ void CMonitor::onConnect(bool noRule) { } wlr_ext_workspace_group_handle_v1_output_enter(pWLRWorkspaceGroupHandle, output); + + setupDefaultWS(monitorRule); - // Workspace - std::string newDefaultWorkspaceName = ""; - auto WORKSPACEID = monitorRule.defaultWorkspace == "" ? g_pCompositor->m_vWorkspaces.size() + 1 : getWorkspaceIDFromString(monitorRule.defaultWorkspace, newDefaultWorkspaceName); - - if (WORKSPACEID == INT_MAX || WORKSPACEID == (long unsigned int)SPECIAL_WORKSPACE_ID) { - WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1; - newDefaultWorkspaceName = std::to_string(WORKSPACEID); - - Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"%s\" is invalid.", monitorRule.defaultWorkspace.c_str()); - } - - auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID); - - Debug::log(LOG, "New monitor: WORKSPACEID %d, exists: %d", WORKSPACEID, (int)(PNEWWORKSPACE != nullptr)); - - if (PNEWWORKSPACE) { - // workspace exists, move it to the newly connected monitor - g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, this); - activeWorkspace = PNEWWORKSPACE->m_iID; - g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); - PNEWWORKSPACE->startAnim(true, true, true); - } else { - - if (newDefaultWorkspaceName == "") - newDefaultWorkspaceName = std::to_string(WORKSPACEID); - - PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(std::make_unique(ID, newDefaultWorkspaceName)).get(); - - // We are required to set the name here immediately - wlr_ext_workspace_handle_v1_set_name(PNEWWORKSPACE->m_pWlrHandle, newDefaultWorkspaceName.c_str()); - - PNEWWORKSPACE->m_iID = WORKSPACEID; - } - - activeWorkspace = PNEWWORKSPACE->m_iID; scale = monitorRule.scale; m_pThisWrap = nullptr; forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. - - g_pCompositor->deactivateAllWLRWorkspaces(PNEWWORKSPACE->m_pWlrHandle); - PNEWWORKSPACE->setActive(true); // if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet @@ -186,6 +150,20 @@ void CMonitor::onDisconnect() { } } + // remove mirror + if (pMirrorOf) { + pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == this; })); + pMirrorOf = nullptr; + } + + if (!mirrors.empty()) { + for (auto& m : mirrors) { + m->setMirror(""); + } + + g_pConfigManager->m_bWantsMonitorReload = true; + } + m_bEnabled = false; m_bRenderingInitPassed = false; @@ -246,3 +224,136 @@ void CMonitor::addDamage(pixman_region32_t* rg) { void CMonitor::addDamage(wlr_box* box) { wlr_output_damage_add_box(damage, box); } + +bool CMonitor::isMirror() { + return pMirrorOf != nullptr; +} + +void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) { + // Workspace + std::string newDefaultWorkspaceName = ""; + auto WORKSPACEID = monitorRule.defaultWorkspace == "" ? g_pCompositor->m_vWorkspaces.size() + 1 : getWorkspaceIDFromString(monitorRule.defaultWorkspace, newDefaultWorkspaceName); + + if (WORKSPACEID == INT_MAX || WORKSPACEID == (long unsigned int)SPECIAL_WORKSPACE_ID) { + WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1; + newDefaultWorkspaceName = std::to_string(WORKSPACEID); + + Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"%s\" is invalid.", monitorRule.defaultWorkspace.c_str()); + } + + auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID); + + Debug::log(LOG, "New monitor: WORKSPACEID %d, exists: %d", WORKSPACEID, (int)(PNEWWORKSPACE != nullptr)); + + if (PNEWWORKSPACE) { + // workspace exists, move it to the newly connected monitor + g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, this); + activeWorkspace = PNEWWORKSPACE->m_iID; + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); + PNEWWORKSPACE->startAnim(true, true, true); + } else { + if (newDefaultWorkspaceName == "") + newDefaultWorkspaceName = std::to_string(WORKSPACEID); + + PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(std::make_unique(ID, newDefaultWorkspaceName)).get(); + + // We are required to set the name here immediately + wlr_ext_workspace_handle_v1_set_name(PNEWWORKSPACE->m_pWlrHandle, newDefaultWorkspaceName.c_str()); + + PNEWWORKSPACE->m_iID = WORKSPACEID; + } + + activeWorkspace = PNEWWORKSPACE->m_iID; + + g_pCompositor->deactivateAllWLRWorkspaces(PNEWWORKSPACE->m_pWlrHandle); + PNEWWORKSPACE->setActive(true); +} + +void CMonitor::setMirror(const std::string& mirrorOf) { + const auto PMIRRORMON = g_pCompositor->getMonitorFromString(mirrorOf); + + if (PMIRRORMON == pMirrorOf) + return; + + if (PMIRRORMON && PMIRRORMON->isMirror()) { + Debug::log(ERR, "Cannot mirror a mirror!"); + return; + } + + if (PMIRRORMON == this) { + Debug::log(ERR, "Cannot mirror self!"); + return; + } + + if (!PMIRRORMON) { + // disable mirroring + + if (pMirrorOf) { + pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == this; })); + } + + pMirrorOf = nullptr; + + // set rule + const auto RULE = g_pConfigManager->getMonitorRuleFor(this->szName); + + vecPosition = RULE.offset; + + // push to mvmonitors + if (!m_pThisWrap) { + // find the wrap + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (m->ID == ID) { + m_pThisWrap = &m; + break; + } + } + } + + if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) { + g_pCompositor->m_vMonitors.push_back(*m_pThisWrap); + } + + setupDefaultWS(RULE); + + wlr_output_layout_add(g_pCompositor->m_sWLROutputLayout, output, (int)vecPosition.x, (int)vecPosition.y); + } else { + CMonitor* BACKUPMON = nullptr; + for (auto& m : g_pCompositor->m_vMonitors) { + if (m.get() != this) { + BACKUPMON = m.get(); + break; + } + } + + // move all the WS + std::deque wspToMove; + for (auto& w : g_pCompositor->m_vWorkspaces) { + if (w->m_iMonitorID == ID) { + wspToMove.push_back(w.get()); + } + } + + for (auto& w : wspToMove) { + g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); + w->startAnim(true, true, true); + } + + activeWorkspace = -1; + + wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output); + + vecPosition = Vector2D(-1337420, -1337420); + + pMirrorOf = PMIRRORMON; + + pMirrorOf->mirrors.push_back(this); + + // remove from mvmonitors + if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) != g_pCompositor->m_vMonitors.end()) { + g_pCompositor->m_vMonitors.erase(std::remove_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](const auto& other) { return other.get() == this; })); + } + + g_pCompositor->m_pLastMonitor = g_pCompositor->m_vMonitors.front().get(); + } +} \ No newline at end of file diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index c3f52108..1af93efb 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -7,6 +7,8 @@ #include #include +struct SMonitorRule; + class CMonitor { public: Vector2D vecPosition = Vector2D(0,0); @@ -35,6 +37,10 @@ public: bool scheduledRecalc = false; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + // mirroring + CMonitor* pMirrorOf = nullptr; + std::vector mirrors; + // for the special workspace bool specialWorkspaceOpen = false; @@ -57,6 +63,8 @@ public: void onDisconnect(); void addDamage(pixman_region32_t* rg); void addDamage(wlr_box* box); + void setMirror(const std::string&); + bool isMirror(); std::shared_ptr* m_pThisWrap = nullptr; bool m_bEnabled = false; @@ -67,4 +75,7 @@ public: bool operator==(const CMonitor& rhs) { return vecPosition == rhs.vecPosition && vecSize == rhs.vecSize && szName == rhs.szName; } + +private: + void setupDefaultWS(const SMonitorRule&); }; \ No newline at end of file diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 48581548..ea11bbb7 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -102,6 +102,7 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, pixman_region32_t* pDamage, bool m_RenderData.pCurrentMonData->primaryFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); + m_RenderData.pCurrentMonData->monitorMirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); createBGTextureForMonitor(pMonitor); } @@ -120,11 +121,14 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, pixman_region32_t* pDamage, bool void CHyprOpenGLImpl::end() { // end the render, copy the data to the WLR framebuffer if (!m_bFakeFrame) { + pixman_region32_copy(m_RenderData.pDamage, &m_rOriginalDamageRegion); + + if (!m_RenderData.pMonitor->mirrors.empty()) + g_pHyprOpenGL->saveBufferForMirror(); // save with original damage region + glBindFramebuffer(GL_FRAMEBUFFER, m_iWLROutputFb); wlr_box monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; - pixman_region32_copy(m_RenderData.pDamage, &m_rOriginalDamageRegion); - clear(CColor(11, 11, 11, 255)); m_bEndFrame = true; @@ -1088,6 +1092,27 @@ void CHyprOpenGLImpl::renderRoundedShadow(wlr_box* box, int round, int range, fl glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } +void CHyprOpenGLImpl::saveBufferForMirror() { + m_RenderData.pCurrentMonData->monitorMirrorFB.bind(); + + wlr_box monbox = {0, 0, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y}; + + renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 255.f, 0, false, false); + + m_RenderData.pCurrentMonData->primaryFB.bind(); +} + +void CHyprOpenGLImpl::renderMirrored() { + wlr_box monbox = {0, 0, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y}; + + const auto PFB = &m_mMonitorRenderResources[m_RenderData.pMonitor->pMirrorOf].monitorMirrorFB; + + if (PFB->m_cTex.m_iTexID <= 0) + return; + + renderTexture(PFB->m_cTex, &monbox, 255.f, 0, false, false); +} + void CHyprOpenGLImpl::renderSplash(cairo_t *const CAIRO, cairo_surface_t *const CAIROSURFACE) { cairo_select_font_face(CAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index b81fd0f8..e3350c15 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -33,8 +33,10 @@ inline const float fanVertsFull[] = { struct SMonitorRenderData { CFramebuffer primaryFB; - CFramebuffer mirrorFB; - CFramebuffer mirrorSwapFB; + CFramebuffer mirrorFB; // these are used for some effects, + CFramebuffer mirrorSwapFB; // etc + + CFramebuffer monitorMirrorFB; // used for mirroring outputs CTexture stencilTex; @@ -102,6 +104,9 @@ public: void preWindowPass(); void preRender(CMonitor*); + void saveBufferForMirror(); + void renderMirrored(); + SCurrentRenderData m_RenderData; GLint m_iCurrentOutputFb = 0; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index fc02af61..a1699f36 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -778,7 +778,7 @@ void CHyprRenderer::damageWindow(CWindow* pWindow) { } void CHyprRenderer::damageMonitor(CMonitor* pMonitor) { - if (g_pCompositor->m_bUnsafeState) + if (g_pCompositor->m_bUnsafeState || pMonitor->isMirror()) return; wlr_box damageBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}; @@ -795,6 +795,9 @@ void CHyprRenderer::damageBox(wlr_box* pBox) { return; for (auto& m : g_pCompositor->m_vMonitors) { + if (m->isMirror()) + continue; // don't damage mirrors traditionally + wlr_box damageBox = {pBox->x - m->vecPosition.x, pBox->y - m->vecPosition.y, pBox->width, pBox->height}; scaleBox(&damageBox, m->scale); m->addDamage(&damageBox); @@ -818,6 +821,19 @@ void CHyprRenderer::damageRegion(pixman_region32_t* rg) { } } +void CHyprRenderer::damageMirrorsWith(CMonitor* pMonitor, pixman_region32_t* pRegion) { + for (auto& mirror : pMonitor->mirrors) { + Vector2D scale = {mirror->vecSize.x / pMonitor->vecSize.x, mirror->vecSize.y / pMonitor->vecSize.y}; + + pixman_region32_t rg; + pixman_region32_init(&rg); + pixman_region32_copy(&rg, pRegion); + wlr_region_scale_xy(&rg, &rg, scale.x, scale.y); + pMonitor->addDamage(&rg); + pixman_region32_fini(&rg); + } +} + void CHyprRenderer::renderDragIcon(CMonitor* pMonitor, timespec* time) { if (!(g_pInputManager->m_sDrag.dragIcon && g_pInputManager->m_sDrag.iconMapped && g_pInputManager->m_sDrag.dragIcon->surface)) return; @@ -1029,7 +1045,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->vecPosition = finalPos; } - wlr_output_layout_add(g_pCompositor->m_sWLROutputLayout, pMonitor->output, (int)pMonitor->vecPosition.x, (int)pMonitor->vecPosition.y); + if (!pMonitor->isMirror()) + wlr_output_layout_add(g_pCompositor->m_sWLROutputLayout, pMonitor->output, (int)pMonitor->vecPosition.x, (int)pMonitor->vecPosition.y); wlr_output_enable(pMonitor->output, true); @@ -1042,6 +1059,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR // frame skip pMonitor->framesToSkip = 1; + // reload to fix mirrors + g_pConfigManager->m_bWantsMonitorReload = true; + return true; } diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 04b2bb45..067e618c 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -35,6 +35,7 @@ public: void damageBox(const int& x, const int& y, const int& w, const int& h); void damageRegion(pixman_region32_t*); void damageMonitor(CMonitor*); + void damageMirrorsWith(CMonitor*, pixman_region32_t*); bool applyMonitorRule(CMonitor*, SMonitorRule*, bool force = false); bool shouldRenderWindow(CWindow*, CMonitor*); bool shouldRenderWindow(CWindow*);