diff --git a/autotests/integration/xdgshellclient_test.cpp b/autotests/integration/xdgshellclient_test.cpp index e94865b416..ee418e332b 100644 --- a/autotests/integration/xdgshellclient_test.cpp +++ b/autotests/integration/xdgshellclient_test.cpp @@ -9,6 +9,7 @@ */ #include "kwin_wayland_test.h" #include "abstract_client.h" +#include "abstract_wayland_output.h" #include "cursor.h" #include "decorations/decorationbridge.h" #include "decorations/settings.h" @@ -73,6 +74,7 @@ private Q_SLOTS: void testMaximizedToFullscreen_data(); void testMaximizedToFullscreen(); + void testFullscreenMultipleOutputs(); void testWindowOpensLargerThanScreen(); void testHidden(); void testDesktopFileName(); @@ -626,6 +628,59 @@ void TestXdgShellClient::testMaximizedToFullscreen() QVERIFY(Test::waitForWindowDestroyed(client)); } +void TestXdgShellClient::testFullscreenMultipleOutputs() +{ + // this test verifies that kwin will place fullscreen windows in the outputs its instructed to + + for (int i = 0; i < screens()->count(); ++i) { + XdgShellSurface::States states; + + QSharedPointer surface(Test::createSurface()); + QSharedPointer shellSurface(Test::createXdgShellStableSurface(surface.data())); + + QVERIFY(surface); + QVERIFY(shellSurface); + + auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QVERIFY(client); + QVERIFY(client->isActive()); + QVERIFY(!client->isFullScreen()); + QCOMPARE(client->clientSize(), QSize(100, 50)); + QVERIFY(!client->isDecorated()); + + QSignalSpy fullscreenChangedSpy(client, &AbstractClient::fullScreenChanged); + QVERIFY(fullscreenChangedSpy.isValid()); + QSignalSpy frameGeometryChangedSpy(client, &AbstractClient::frameGeometryChanged); + QVERIFY(frameGeometryChangedSpy.isValid()); + QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested); + QVERIFY(configureRequestedSpy.isValid()); + + // Wait for the compositor to send a configure event with the Activated state. + QVERIFY(configureRequestedSpy.wait()); + QCOMPARE(configureRequestedSpy.count(), 1); + states = configureRequestedSpy.last().at(1).value(); + QVERIFY(states & XdgShellSurface::State::Activated); + + // Ask the compositor to show the window in full screen mode. + shellSurface->setFullscreen(true, Test::waylandOutputs()[i]); + QVERIFY(configureRequestedSpy.wait()); + QCOMPARE(configureRequestedSpy.count(), 2); + QCOMPARE(configureRequestedSpy.last().at(0).value(), screens()->size(i)); + + shellSurface->ackConfigure(configureRequestedSpy.last().at(2).value()); + Test::render(surface.data(), configureRequestedSpy.last().at(0).value(), Qt::red); + + QVERIFY(!fullscreenChangedSpy.isEmpty() || fullscreenChangedSpy.wait()); + QCOMPARE(fullscreenChangedSpy.count(), 1); + + QVERIFY(!frameGeometryChangedSpy.isEmpty() || frameGeometryChangedSpy.wait()); + + QVERIFY(client->isFullScreen()); + + QCOMPARE(client->frameGeometry(), screens()->geometry(i)); + } +} + void TestXdgShellClient::testWindowOpensLargerThanScreen() { // this test creates a window which is as large as the screen, but is decorated diff --git a/xdgshellclient.cpp b/xdgshellclient.cpp index 664ce33f01..629f0d8011 100644 --- a/xdgshellclient.cpp +++ b/xdgshellclient.cpp @@ -9,7 +9,9 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "xdgshellclient.h" +#include "abstract_wayland_output.h" #include "deleted.h" +#include "platform.h" #include "screenedge.h" #include "screens.h" #include "subsurfacemonitor.h" @@ -1060,7 +1062,8 @@ void XdgToplevelClient::handleUnmaximizeRequested() void XdgToplevelClient::handleFullscreenRequested(OutputInterface *output) { - Q_UNUSED(output) + m_fullScreenRequestedOutput = waylandServer()->findOutput(output); + if (m_isInitialized) { setFullScreen(/* set */ true, /* user */ false); scheduleConfigure(ConfigureRequired); @@ -1071,6 +1074,7 @@ void XdgToplevelClient::handleFullscreenRequested(OutputInterface *output) void XdgToplevelClient::handleUnfullscreenRequested() { + m_fullScreenRequestedOutput.clear(); if (m_isInitialized) { setFullScreen(/* set */ false, /* user */ false); scheduleConfigure(ConfigureRequired); @@ -1560,8 +1564,10 @@ void XdgToplevelClient::setFullScreen(bool set, bool user) updateDecoration(false, false); if (set) { - setFrameGeometry(workspace()->clientArea(FullScreenArea, this)); + const int screen = m_fullScreenRequestedOutput ? kwinApp()->platform()->enabledOutputs().indexOf(m_fullScreenRequestedOutput) : screens()->number(frameGeometry().center()); + setFrameGeometry(workspace()->clientArea(FullScreenArea, screen, desktop())); } else { + m_fullScreenRequestedOutput.clear(); if (m_fullScreenGeometryRestore.isValid()) { int currentScreen = screen(); setFrameGeometry(QRect(m_fullScreenGeometryRestore.topLeft(), diff --git a/xdgshellclient.h b/xdgshellclient.h index c77ea2498c..aea4b311f2 100644 --- a/xdgshellclient.h +++ b/xdgshellclient.h @@ -29,6 +29,7 @@ class XdgToplevelDecorationV1Interface; namespace KWin { +class AbstractOutput; class XdgSurfaceConfigure { @@ -216,6 +217,7 @@ private: bool m_isInitialized = false; bool m_userNoBorder = false; bool m_isTransient = false; + QPointer m_fullScreenRequestedOutput; }; class XdgPopupClient final : public XdgSurfaceClient