diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 2bd82ad3..65e1eb79 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1382,15 +1382,26 @@ 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)) { - bool fs = requestsFS.value(); - if (m_bIsMapped) { - g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_FULLSCREEN, requestsFS.value()); + 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(); } + bool fs = requestsFS.value(); + 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 37189a00..d503ac3b 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 8082cdc9..ff69e83f 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -131,6 +131,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) { @@ -168,6 +169,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; } @@ -186,6 +188,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: { @@ -227,6 +230,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]); } @@ -337,10 +342,30 @@ void Events::listener_mapWindow(void* owner, void* data) { PMONITOR = g_pCompositor->m_pLastMonitor.lock(); } + + requestedFSMonitor = MONITOR_INVALID; } else workspaceSilent = false; } + if (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT) + requestedFSMonitor = MONITOR_INVALID; + else if (requestedFSMonitor != MONITOR_INVALID) { + if (const auto PM = g_pCompositor->getMonitorFromID(requestedFSMonitor); PM) + PWINDOW->m_pMonitor = PM; + + const auto PMONITORFROMID = PWINDOW->m_pMonitor.lock(); + + if (PWINDOW->m_pMonitor != PMONITOR) { + g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->monitorID())); + PMONITOR = PMONITORFROMID; + } + PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace; + PWORKSPACE = PWINDOW->m_pWorkspace; + + Debug::log(LOG, "Requested monitor, applying to {:mw}", PWINDOW); + } + if (PWORKSPACE->m_bDefaultFloating) PWINDOW->m_bIsFloating = true; diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index 282aec47..6b7cb3c1 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,14 @@ CXDGToplevelResource::CXDGToplevelResource(SP resource_, SPsetSetFullscreen([this](CXdgToplevel* r, wl_resource* output) { + if (output) + if (const auto PM = CWLOutputResource::fromResource(output)->monitor; PM) + state.requestsFullscreenMonitor = PM->ID; + state.requestsFullscreen = true; events.stateChanged.emit(); state.requestsFullscreen.reset(); + state.requestsFullscreenMonitor.reset(); }); resource->setUnsetFullscreen([this](CXdgToplevel* r) { @@ -205,7 +211,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 ef847f3b..6eef99bb 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 {