kwin/autotests/integration/maximize_test.cpp
Vlad Zahorodnii d2fb4147fc Move multi-purpose code in its own directory
Things such as Output, InputDevice and so on are made to be
multi-purpose. In order to make this separation more clear, this change
moves that code in the core directory. Some things still link to the
abstraction level above (kwin), they can be tackled in future refactors.
Ideally code in core/ should depend either on other code in core/ or
system libs.
2022-09-06 11:21:40 +03:00

335 lines
15 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wayland_test.h"
#include "core/output.h"
#include "core/platform.h"
#include "cursor.h"
#include "decorations/decorationbridge.h"
#include "decorations/settings.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/plasmashell.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
#include <KDecoration2/DecoratedClient>
#include <KDecoration2/Decoration>
#include <KDecoration2/DecorationSettings>
using namespace KWin;
using namespace KWayland::Client;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_maximized-0");
class TestMaximized : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testMaximizedPassedToDeco();
void testInitiallyMaximizedBorderless();
void testBorderlessMaximizedWindow();
void testMaximizedGainFocusAndBeActivated();
};
void TestMaximized::initTestCase()
{
qRegisterMetaType<KWin::Window *>();
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(applicationStartedSpy.isValid());
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
QVERIFY(waylandServer()->init(s_socketName));
QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
const auto outputs = workspace()->outputs();
QCOMPARE(outputs.count(), 2);
QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
}
void TestMaximized::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::PlasmaShell));
workspace()->setActiveOutput(QPoint(640, 512));
KWin::Cursors::self()->mouse()->setPos(QPoint(640, 512));
}
void TestMaximized::cleanup()
{
Test::destroyWaylandConnection();
// adjust config
auto group = kwinApp()->config()->group("Windows");
group.writeEntry("BorderlessMaximizedWindows", false);
group.sync();
Workspace::self()->slotReconfigure();
QCOMPARE(options->borderlessMaximizedWindows(), false);
}
void TestMaximized::testMaximizedPassedToDeco()
{
// this test verifies that when a XdgShellClient gets maximized the Decoration receives the signal
// Create the test window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
std::unique_ptr<Test::XdgToplevelDecorationV1> xdgDecoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(surfaceConfigureRequestedSpy.wait());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
QVERIFY(window->isDecorated());
auto decoration = window->decoration();
QVERIFY(decoration);
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
// Wait for configure event that signals the window is active now.
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
// When there are no borders, there is no change to them when maximizing.
// TODO: we should test both cases with fixed fake decoration for autotests.
const bool hasBorders = Workspace::self()->decorationBridge()->settings()->borderSize() != KDecoration2::BorderSize::None;
// now maximize
QSignalSpy bordersChangedSpy(decoration, &KDecoration2::Decoration::bordersChanged);
QVERIFY(bordersChangedSpy.isValid());
QSignalSpy maximizedChangedSpy(decoration->client().toStrongRef().get(), &KDecoration2::DecoratedClient::maximizedChanged);
QVERIFY(maximizedChangedSpy.isValid());
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
QVERIFY(frameGeometryChangedSpy.isValid());
workspace()->slotWindowMaximize();
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024 - decoration->borderTop()));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
// If no borders, there is only the initial geometry shape change, but none through border resizing.
QCOMPARE(frameGeometryChangedSpy.count(), hasBorders ? 2 : 1);
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeFull);
QCOMPARE(maximizedChangedSpy.count(), 1);
QCOMPARE(maximizedChangedSpy.last().first().toBool(), true);
QCOMPARE(bordersChangedSpy.count(), hasBorders ? 1 : 0);
QCOMPARE(decoration->borderLeft(), 0);
QCOMPARE(decoration->borderBottom(), 0);
QCOMPARE(decoration->borderRight(), 0);
QVERIFY(decoration->borderTop() != 0);
// now unmaximize again
workspace()->slotWindowMaximize();
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), QSize(100, 50), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(frameGeometryChangedSpy.count(), hasBorders ? 4 : 2);
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(maximizedChangedSpy.count(), 2);
QCOMPARE(maximizedChangedSpy.last().first().toBool(), false);
QCOMPARE(bordersChangedSpy.count(), hasBorders ? 2 : 0);
QVERIFY(decoration->borderTop() != 0);
QVERIFY(decoration->borderLeft() != !hasBorders);
QVERIFY(decoration->borderRight() != !hasBorders);
QVERIFY(decoration->borderBottom() != !hasBorders);
// Destroy the test window.
shellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(window));
}
void TestMaximized::testInitiallyMaximizedBorderless()
{
// This test verifies that a window created as maximized, will be maximized and without Border with BorderlessMaximizedWindows
// adjust config
auto group = kwinApp()->config()->group("Windows");
group.writeEntry("BorderlessMaximizedWindows", true);
group.sync();
Workspace::self()->slotReconfigure();
QCOMPARE(options->borderlessMaximizedWindows(), true);
// Create the test window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
shellSurface->set_maximized();
QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
// Wait for the initial configure event.
Test::XdgToplevel::States states;
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(1280, 1024), Qt::blue);
QVERIFY(window);
QVERIFY(!window->isDecorated());
QVERIFY(window->isActive());
QVERIFY(window->isMaximizable());
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(),
Test::XdgToplevelDecorationV1::mode_server_side);
// Destroy the window.
shellSurface.reset();
surface.reset();
QVERIFY(Test::waitForWindowDestroyed(window));
}
void TestMaximized::testBorderlessMaximizedWindow()
{
// This test verifies that a maximized window looses it's server-side
// decoration when the borderless maximized option is on.
// Enable the borderless maximized windows option.
auto group = kwinApp()->config()->group("Windows");
group.writeEntry("BorderlessMaximizedWindows", true);
group.sync();
Workspace::self()->slotReconfigure();
QCOMPARE(options->borderlessMaximizedWindows(), true);
// Create the test window.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
// Wait for the initial configure event.
Test::XdgToplevel::States states;
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
// Map the window.
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
QVERIFY(window->isActive());
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(window->isDecorated(), true);
// We should receive a configure event when the window becomes active.
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
// Maximize the window.
const QRectF maximizeRestoreGeometry = window->frameGeometry();
workspace()->slotWindowMaximize();
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
QVERIFY(frameGeometryChangedSpy.isValid());
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeFull);
QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
QCOMPARE(window->isDecorated(), false);
// Restore the window.
workspace()->slotWindowMaximize();
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
Test::render(surface.get(), QSize(100, 50), Qt::red);
QVERIFY(frameGeometryChangedSpy.wait());
QCOMPARE(window->frameGeometry(), maximizeRestoreGeometry);
QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
QCOMPARE(window->isDecorated(), true);
// Destroy the window.
shellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(window));
}
void TestMaximized::testMaximizedGainFocusAndBeActivated()
{
// This test verifies that a window will be raised and gain focus when it's maximized
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> xdgShellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> xdgShellSurface2(Test::createXdgToplevelSurface(surface2.get()));
auto window2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
QVERIFY(!window->isActive());
QVERIFY(window2->isActive());
QCOMPARE(workspace()->stackingOrder(), (QList<Window *>{window, window2}));
workspace()->performWindowOperation(window, Options::MaximizeOp);
QVERIFY(window->isActive());
QVERIFY(!window2->isActive());
QCOMPARE(workspace()->stackingOrder(), (QList<Window *>{window2, window}));
xdgShellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(window));
xdgShellSurface2.reset();
QVERIFY(Test::waitForWindowDestroyed(window2));
}
WAYLANDTEST_MAIN(TestMaximized)
#include "maximize_test.moc"