diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 609141989b6b..114ddecb57f6 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1404,14 +1404,27 @@ void CWindow::activate(bool force) { } void CWindow::onUpdateState() { - std::optional requestsFS = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsFullscreen : m_pXWaylandSurface->state.requestsFullscreen; - std::optional requestsMX = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsMaximize : m_pXWaylandSurface->state.requestsMaximize; + std::optional requestsFS = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsFullscreen : m_pXWaylandSurface->state.requestsFullscreen; + std::optional requestsID = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsFullscreenMonitor : MONITOR_INVALID; + std::optional requestsMX = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsMaximize : m_pXWaylandSurface->state.requestsMaximize; if (requestsFS.has_value() && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) { + if (requestsID.has_value() && (requestsID.value() != MONITOR_INVALID) && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT)) { + if (m_bIsMapped) { + const auto monitor = g_pCompositor->getMonitorFromID(requestsID.value()); + g_pCompositor->moveWindowToWorkspaceSafe(m_pSelf.lock(), monitor->activeWorkspace); + g_pCompositor->setActiveMonitor(monitor); + } + + if (!m_bIsMapped) + m_iWantsInitialFullscreenMonitor = requestsID.value(); + + Debug::log(LOG, "[window] fullscreen requested on {}", m_iWantsInitialFullscreenMonitor); + } + bool fs = requestsFS.value(); - if (m_bIsMapped) { + if (m_bIsMapped) g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_FULLSCREEN, requestsFS.value()); - } if (!m_bIsMapped) m_bWantsInitialFullscreen = fs; diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 3ceaf5944f52..b6db57b238fd 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -57,6 +57,7 @@ enum eSuppressEvents : uint8_t { SUPPRESS_MAXIMIZE = 1 << 1, SUPPRESS_ACTIVATE = 1 << 2, SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3, + SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4, }; class IWindowTransformer; @@ -288,7 +289,8 @@ class CWindow { bool m_bNoInitialFocus = false; // Fullscreen and Maximize - bool m_bWantsInitialFullscreen = false; + bool m_bWantsInitialFullscreen = false; + MONITORID m_iWantsInitialFullscreenMonitor = MONITOR_INVALID; // bitfield eSuppressEvents uint64_t m_eSuppressedEvents = SUPPRESS_NONE; diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index a20cbcad77b7..0ceba7d1b77f 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -127,6 +127,7 @@ void Events::listener_mapWindow(void* owner, void* data) { std::optional requestedFSState; if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen)) requestedClientFSMode = FSMODE_FULLSCREEN; + MONITORID requestedFSMonitor = PWINDOW->m_iWantsInitialFullscreenMonitor; for (auto const& r : PWINDOW->m_vMatchedRules) { switch (r->ruleType) { @@ -164,6 +165,7 @@ void Events::listener_mapWindow(void* owner, void* data) { PWORKSPACE = PWINDOW->m_pWorkspace; Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW); + requestedFSMonitor = MONITOR_INVALID; } catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r->szRule, r->szValue, e.what()); } break; } @@ -182,6 +184,7 @@ void Events::listener_mapWindow(void* owner, void* data) { requestedWorkspace = ""; Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r->szValue); + requestedFSMonitor = MONITOR_INVALID; break; } case CWindowRule::RULE_FLOAT: { @@ -223,6 +226,8 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE; else if (vars[i] == "activatefocus") PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY; + else if (vars[i] == "fullscreenoutput") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT; else Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]); } @@ -347,6 +352,14 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height); } + if (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT) + requestedFSMonitor = MONITOR_INVALID; + if (requestedFSMonitor != MONITOR_INVALID) { + Debug::log(LOG, "[window] applying requestedFSMonitor {}", requestedFSMonitor); + if (const auto PM = g_pCompositor->getMonitorFromID(requestedFSMonitor); PM) + PWINDOW->m_pMonitor = PM; + } + PWINDOW->updateWindowData(); // Verify window swallowing. Get the swallower before calling onWindowCreated(PWINDOW) because getSwallower() wouldn't get it after if PWINDOW gets auto grouped. diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index 282aec477891..0afba7386851 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -5,6 +5,7 @@ #include "../managers/SeatManager.hpp" #include "core/Seat.hpp" #include "core/Compositor.hpp" +#include "protocols/core/Output.hpp" #include #include @@ -191,9 +192,12 @@ CXDGToplevelResource::CXDGToplevelResource(SP resource_, SPsetSetFullscreen([this](CXdgToplevel* r, wl_resource* output) { + if (output) + state.requestsFullscreenMonitor = CWLOutputResource::fromResource(output)->monitor->ID; state.requestsFullscreen = true; events.stateChanged.emit(); state.requestsFullscreen.reset(); + state.requestsFullscreenMonitor.reset(); }); resource->setUnsetFullscreen([this](CXdgToplevel* r) { @@ -205,7 +209,7 @@ CXDGToplevelResource::CXDGToplevelResource(SP resource_, SPsetSetMinimized([this](CXdgToplevel* r) { state.requestsMinimize = true; events.stateChanged.emit(); - state.requestsFullscreen.reset(); + state.requestsMinimize.reset(); }); resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) { diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index ef847f3be757..6eef99bbcc94 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -123,9 +123,10 @@ class CXDGToplevelResource { std::string appid; // volatile state: is reset after the stateChanged signal fires - std::optional requestsMaximize; - std::optional requestsFullscreen; - std::optional requestsMinimize; + std::optional requestsMaximize; + std::optional requestsFullscreen; + std::optional requestsFullscreenMonitor; + std::optional requestsMinimize; } state; struct {