2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2023-03-07 16:12:08 +00:00
|
|
|
SPDX-FileCopyrightText: 2023 Natalie Clarius <natalie_clarius@yahoo.de>
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2022-03-23 10:13:38 +00:00
|
|
|
#include "kwin_wayland_test.h"
|
|
|
|
|
2022-08-29 07:55:49 +00:00
|
|
|
#include "core/output.h"
|
2022-08-31 20:29:15 +00:00
|
|
|
#include "placement.h"
|
2023-02-09 13:07:56 +00:00
|
|
|
#include "pointer_input.h"
|
2019-07-10 17:41:41 +00:00
|
|
|
#include "wayland_server.h"
|
2022-04-22 17:39:12 +00:00
|
|
|
#include "window.h"
|
2019-07-10 17:41:41 +00:00
|
|
|
#include "workspace.h"
|
|
|
|
|
|
|
|
#include <KWayland/Client/compositor.h>
|
|
|
|
#include <KWayland/Client/shm_pool.h>
|
|
|
|
#include <KWayland/Client/surface.h>
|
|
|
|
|
|
|
|
using namespace KWin;
|
|
|
|
|
|
|
|
static const QString s_socketName = QStringLiteral("wayland_test_kwin_placement-0");
|
|
|
|
|
|
|
|
struct PlaceWindowResult
|
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
QSizeF initiallyConfiguredSize;
|
2021-05-11 05:26:51 +00:00
|
|
|
Test::XdgToplevel::States initiallyConfiguredStates;
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF finalGeometry;
|
2019-07-10 17:41:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class TestPlacement : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
2019-09-12 16:43:18 +00:00
|
|
|
|
2019-07-10 17:41:41 +00:00
|
|
|
private Q_SLOTS:
|
|
|
|
void init();
|
|
|
|
void cleanup();
|
|
|
|
void initTestCase();
|
|
|
|
|
|
|
|
void testPlaceSmart();
|
2019-09-12 16:43:18 +00:00
|
|
|
void testPlaceMaximized();
|
2019-10-11 08:46:08 +00:00
|
|
|
void testPlaceMaximizedLeavesFullscreen();
|
2019-09-12 16:43:18 +00:00
|
|
|
void testPlaceCentered();
|
|
|
|
void testPlaceUnderMouse();
|
2022-11-30 14:49:05 +00:00
|
|
|
void testPlaceZeroCornered();
|
2019-09-12 16:43:18 +00:00
|
|
|
void testPlaceRandom();
|
2021-01-20 18:03:45 +00:00
|
|
|
void testFullscreen();
|
2023-03-07 16:12:08 +00:00
|
|
|
void testCascadeIfCovering();
|
|
|
|
void testCascadeIfCoveringIgnoreNonCovering();
|
|
|
|
void testCascadeIfCoveringIgnoreOutOfArea();
|
|
|
|
void testCascadeIfCoveringIgnoreAlreadyCovered();
|
2019-07-10 17:41:41 +00:00
|
|
|
|
|
|
|
private:
|
2022-08-31 20:29:15 +00:00
|
|
|
void setPlacementPolicy(PlacementPolicy policy);
|
2019-07-10 17:41:41 +00:00
|
|
|
/*
|
2022-08-16 11:43:33 +00:00
|
|
|
* Create a window and return relevant results for testing
|
2019-07-10 17:41:41 +00:00
|
|
|
* defaultSize is the buffer size to use if the compositor returns an empty size in the first configure
|
|
|
|
* event.
|
|
|
|
*/
|
2022-08-16 11:43:33 +00:00
|
|
|
std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> createAndPlaceWindow(const QSize &defaultSize);
|
2019-07-10 17:41:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void TestPlacement::init()
|
|
|
|
{
|
2023-05-23 08:02:59 +00:00
|
|
|
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::LayerShellV1));
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2021-08-28 18:58:29 +00:00
|
|
|
workspace()->setActiveOutput(QPoint(640, 512));
|
2023-02-09 13:07:56 +00:00
|
|
|
KWin::input()->pointer()->warp(QPoint(640, 512));
|
2019-07-10 17:41:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::cleanup()
|
|
|
|
{
|
|
|
|
Test::destroyWaylandConnection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::initTestCase()
|
|
|
|
{
|
2022-04-22 17:39:12 +00:00
|
|
|
qRegisterMetaType<KWin::Window *>();
|
2020-07-07 09:32:29 +00:00
|
|
|
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
2020-12-09 13:06:15 +00:00
|
|
|
QVERIFY(waylandServer()->init(s_socketName));
|
2023-05-08 10:16:00 +00:00
|
|
|
Test::setOutputConfig({
|
|
|
|
QRect(0, 0, 1280, 1024),
|
|
|
|
QRect(1280, 0, 1280, 1024),
|
|
|
|
});
|
2019-07-10 17:41:41 +00:00
|
|
|
|
|
|
|
kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
|
|
|
|
|
|
|
|
kwinApp()->start();
|
2020-07-07 09:32:29 +00:00
|
|
|
QVERIFY(applicationStartedSpy.wait());
|
2022-07-11 10:41:15 +00:00
|
|
|
const auto outputs = workspace()->outputs();
|
2021-08-31 07:03:05 +00:00
|
|
|
QCOMPARE(outputs.count(), 2);
|
|
|
|
QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
|
|
|
|
QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
|
2019-07-10 17:41:41 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 20:29:15 +00:00
|
|
|
void TestPlacement::setPlacementPolicy(PlacementPolicy policy)
|
2019-07-10 17:41:41 +00:00
|
|
|
{
|
|
|
|
auto group = kwinApp()->config()->group("Windows");
|
|
|
|
group.writeEntry("Placement", Placement::policyToString(policy));
|
|
|
|
group.sync();
|
|
|
|
Workspace::self()->slotReconfigure();
|
|
|
|
}
|
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> TestPlacement::createAndPlaceWindow(const QSize &defaultSize)
|
2019-07-10 17:41:41 +00:00
|
|
|
{
|
|
|
|
PlaceWindowResult rc;
|
|
|
|
|
|
|
|
// create a new window
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
|
|
|
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
|
2021-05-11 05:26:51 +00:00
|
|
|
|
|
|
|
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
|
|
|
|
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
2021-09-03 17:54:03 +00:00
|
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
2021-05-11 05:26:51 +00:00
|
|
|
surfaceConfigureRequestedSpy.wait();
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
rc.initiallyConfiguredSize = toplevelConfigureRequestedSpy[0][0].toSize();
|
|
|
|
rc.initiallyConfiguredStates = toplevelConfigureRequestedSpy[0][1].value<Test::XdgToplevel::States>();
|
|
|
|
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy[0][0].toUInt());
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
QSizeF size = rc.initiallyConfiguredSize;
|
2019-07-10 17:41:41 +00:00
|
|
|
|
|
|
|
if (size.isEmpty()) {
|
|
|
|
size = defaultSize;
|
|
|
|
}
|
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
auto window = Test::renderAndWaitForShown(surface.get(), size.toSize(), Qt::red);
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
rc.finalGeometry = window->frameGeometry();
|
2022-08-16 11:43:33 +00:00
|
|
|
return {rc, std::move(surface)};
|
2019-07-10 17:41:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::testPlaceSmart()
|
|
|
|
{
|
2022-08-31 20:29:15 +00:00
|
|
|
setPlacementPolicy(PlacementSmart);
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
2019-07-10 17:41:41 +00:00
|
|
|
QRegion usedArea;
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
2022-08-16 11:43:33 +00:00
|
|
|
auto [windowPlacement, surface] = createAndPlaceWindow(QSize(600, 500));
|
2022-04-23 19:51:16 +00:00
|
|
|
// smart placement shouldn't define a size on windows
|
2019-07-10 17:41:41 +00:00
|
|
|
QCOMPARE(windowPlacement.initiallyConfiguredSize, QSize(0, 0));
|
|
|
|
QCOMPARE(windowPlacement.finalGeometry.size(), QSize(600, 500));
|
|
|
|
|
|
|
|
// exact placement isn't a defined concept that should be tested
|
|
|
|
// but the goal of smart placement is to make sure windows don't overlap until they need to
|
|
|
|
// 4 windows of 600, 500 should fit without overlap
|
2022-05-16 20:13:39 +00:00
|
|
|
QVERIFY(!usedArea.intersects(windowPlacement.finalGeometry.toRect()));
|
|
|
|
usedArea += windowPlacement.finalGeometry.toRect();
|
2022-08-16 11:43:33 +00:00
|
|
|
surfaces.push_back(std::move(surface));
|
2019-07-10 17:41:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-12 16:43:18 +00:00
|
|
|
void TestPlacement::testPlaceMaximized()
|
2019-07-10 17:41:41 +00:00
|
|
|
{
|
2022-08-31 20:29:15 +00:00
|
|
|
setPlacementPolicy(PlacementMaximizing);
|
2019-07-10 17:41:41 +00:00
|
|
|
|
|
|
|
// add a top panel
|
2023-05-23 08:02:59 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> panelSurface{Test::createSurface()};
|
|
|
|
std::unique_ptr<Test::LayerSurfaceV1> panelShellSurface{Test::createLayerSurfaceV1(panelSurface.get(), QStringLiteral("dock"))};
|
|
|
|
panelShellSurface->set_size(1280, 20);
|
|
|
|
panelShellSurface->set_anchor(Test::LayerSurfaceV1::anchor_top);
|
|
|
|
panelShellSurface->set_exclusive_zone(20);
|
|
|
|
panelSurface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
|
|
QSignalSpy panelConfigureRequestedSpy(panelShellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
|
|
|
|
QVERIFY(panelConfigureRequestedSpy.wait());
|
|
|
|
Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
|
2019-07-10 17:41:41 +00:00
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
2019-07-10 17:41:41 +00:00
|
|
|
|
|
|
|
// all windows should be initially maximized with an initial configure size sent
|
|
|
|
for (int i = 0; i < 4; i++) {
|
2022-08-16 11:43:33 +00:00
|
|
|
auto [windowPlacement, surface] = createAndPlaceWindow(QSize(600, 500));
|
2021-05-11 05:26:51 +00:00
|
|
|
QVERIFY(windowPlacement.initiallyConfiguredStates & Test::XdgToplevel::State::Maximized);
|
2019-07-10 17:41:41 +00:00
|
|
|
QCOMPARE(windowPlacement.initiallyConfiguredSize, QSize(1280, 1024 - 20));
|
|
|
|
QCOMPARE(windowPlacement.finalGeometry, QRect(0, 20, 1280, 1024 - 20)); // under the panel
|
2022-08-16 11:43:33 +00:00
|
|
|
surfaces.push_back(std::move(surface));
|
2019-07-10 17:41:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-11 08:46:08 +00:00
|
|
|
void TestPlacement::testPlaceMaximizedLeavesFullscreen()
|
|
|
|
{
|
2022-08-31 20:29:15 +00:00
|
|
|
setPlacementPolicy(PlacementMaximizing);
|
2019-10-11 08:46:08 +00:00
|
|
|
|
|
|
|
// add a top panel
|
2023-05-23 08:02:59 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> panelSurface{Test::createSurface()};
|
|
|
|
std::unique_ptr<Test::LayerSurfaceV1> panelShellSurface{Test::createLayerSurfaceV1(panelSurface.get(), QStringLiteral("dock"))};
|
|
|
|
panelShellSurface->set_size(1280, 20);
|
|
|
|
panelShellSurface->set_anchor(Test::LayerSurfaceV1::anchor_top);
|
|
|
|
panelShellSurface->set_exclusive_zone(20);
|
|
|
|
panelSurface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
|
|
QSignalSpy panelConfigureRequestedSpy(panelShellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
|
|
|
|
QVERIFY(panelConfigureRequestedSpy.wait());
|
|
|
|
Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
|
2019-10-11 08:46:08 +00:00
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
2019-10-11 08:46:08 +00:00
|
|
|
|
|
|
|
// all windows should be initially fullscreen with an initial configure size sent, despite the policy
|
|
|
|
for (int i = 0; i < 4; i++) {
|
2022-08-16 11:43:33 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
|
|
|
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
|
2021-05-11 05:26:51 +00:00
|
|
|
shellSurface->set_fullscreen(nullptr);
|
|
|
|
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
|
|
|
|
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
2021-09-03 17:54:03 +00:00
|
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
2021-05-11 05:26:51 +00:00
|
|
|
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
2019-10-11 08:46:08 +00:00
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
auto initiallyConfiguredSize = toplevelConfigureRequestedSpy[0][0].toSize();
|
|
|
|
auto initiallyConfiguredStates = toplevelConfigureRequestedSpy[0][1].value<Test::XdgToplevel::States>();
|
|
|
|
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy[0][0].toUInt());
|
2019-10-11 08:46:08 +00:00
|
|
|
|
2022-08-16 11:43:33 +00:00
|
|
|
auto window = Test::renderAndWaitForShown(surface.get(), initiallyConfiguredSize, Qt::red);
|
2019-10-11 08:46:08 +00:00
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
QVERIFY(initiallyConfiguredStates & Test::XdgToplevel::State::Fullscreen);
|
2022-03-23 10:13:38 +00:00
|
|
|
QCOMPARE(initiallyConfiguredSize, QSize(1280, 1024));
|
2022-04-23 19:51:16 +00:00
|
|
|
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
|
2022-08-16 11:43:33 +00:00
|
|
|
|
|
|
|
surfaces.push_back(std::move(surface));
|
2019-10-11 08:46:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-12 16:43:18 +00:00
|
|
|
void TestPlacement::testPlaceCentered()
|
|
|
|
{
|
|
|
|
// This test verifies that Centered placement policy works.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
2022-08-31 20:29:15 +00:00
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementCentered));
|
2019-09-12 16:43:18 +00:00
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::red);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
|
|
|
shellSurface.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window));
|
2019-09-12 16:43:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::testPlaceUnderMouse()
|
|
|
|
{
|
|
|
|
// This test verifies that Under Mouse placement policy works.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
2022-08-31 20:29:15 +00:00
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementUnderMouse));
|
2019-09-12 16:43:18 +00:00
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
2023-02-09 13:07:56 +00:00
|
|
|
KWin::input()->pointer()->warp(QPoint(200, 300));
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(KWin::Cursors::self()->mouse()->pos(), QPoint(200, 300));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::red);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
2022-05-16 20:13:39 +00:00
|
|
|
QCOMPARE(window->frameGeometry(), QRect(150, 275, 100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
|
|
|
shellSurface.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window));
|
2019-09-12 16:43:18 +00:00
|
|
|
}
|
|
|
|
|
2022-11-30 14:49:05 +00:00
|
|
|
void TestPlacement::testPlaceZeroCornered()
|
2019-09-12 16:43:18 +00:00
|
|
|
{
|
2022-11-30 14:49:05 +00:00
|
|
|
// This test verifies that the Zero-Cornered placement policy works.
|
2019-09-12 16:43:18 +00:00
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
2022-11-30 14:49:05 +00:00
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementZeroCornered));
|
2019-09-12 16:43:18 +00:00
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
|
|
|
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::red);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window1);
|
|
|
|
QCOMPARE(window1->pos(), QPoint(0, 0));
|
|
|
|
QCOMPARE(window1->size(), QSize(100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
|
|
|
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window2);
|
|
|
|
QCOMPARE(window2->pos(), window1->pos() + workspace()->cascadeOffset(window2));
|
|
|
|
QCOMPARE(window2->size(), QSize(100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
|
|
|
|
Window *window3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::green);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window3);
|
|
|
|
QCOMPARE(window3->pos(), window2->pos() + workspace()->cascadeOffset(window3));
|
|
|
|
QCOMPARE(window3->size(), QSize(100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
|
|
|
shellSurface3.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window3));
|
2019-09-12 16:43:18 +00:00
|
|
|
shellSurface2.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window2));
|
2019-09-12 16:43:18 +00:00
|
|
|
shellSurface1.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window1));
|
2019-09-12 16:43:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::testPlaceRandom()
|
|
|
|
{
|
|
|
|
// This test verifies that Random placement policy works.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
2022-08-31 20:29:15 +00:00
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementRandom));
|
2019-09-12 16:43:18 +00:00
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
|
|
|
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::red);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window1);
|
|
|
|
QCOMPARE(window1->size(), QSize(100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
|
|
|
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window2);
|
|
|
|
QVERIFY(window2->pos() != window1->pos());
|
|
|
|
QCOMPARE(window2->size(), QSize(100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
|
|
|
|
Window *window3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::green);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window3);
|
|
|
|
QVERIFY(window3->pos() != window1->pos());
|
|
|
|
QVERIFY(window3->pos() != window2->pos());
|
|
|
|
QCOMPARE(window3->size(), QSize(100, 50));
|
2019-09-12 16:43:18 +00:00
|
|
|
|
|
|
|
shellSurface3.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window3));
|
2019-09-12 16:43:18 +00:00
|
|
|
shellSurface2.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window2));
|
2019-09-12 16:43:18 +00:00
|
|
|
shellSurface1.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window1));
|
2019-09-12 16:43:18 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 18:03:45 +00:00
|
|
|
void TestPlacement::testFullscreen()
|
|
|
|
{
|
2022-07-11 10:41:15 +00:00
|
|
|
const QList<Output *> outputs = workspace()->outputs();
|
2021-08-28 14:51:00 +00:00
|
|
|
|
2022-08-31 20:29:15 +00:00
|
|
|
setPlacementPolicy(PlacementSmart);
|
2022-08-01 21:29:02 +00:00
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
2021-01-20 18:03:45 +00:00
|
|
|
|
2022-08-01 21:29:02 +00:00
|
|
|
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::red);
|
2022-04-23 19:51:16 +00:00
|
|
|
QVERIFY(window);
|
|
|
|
window->sendToOutput(outputs[0]);
|
2021-01-20 18:03:45 +00:00
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
// Wait for the configure event with the activated state.
|
2022-08-01 21:29:02 +00:00
|
|
|
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
2021-05-11 05:26:51 +00:00
|
|
|
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
|
|
|
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
window->setFullScreen(true);
|
2021-01-20 18:03:45 +00:00
|
|
|
|
2022-04-23 19:51:16 +00:00
|
|
|
QSignalSpy geometryChangedSpy(window, &Window::frameGeometryChanged);
|
2021-05-11 05:26:51 +00:00
|
|
|
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
|
|
|
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
2022-08-01 21:29:02 +00:00
|
|
|
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
|
2021-01-20 18:03:45 +00:00
|
|
|
QVERIFY(geometryChangedSpy.wait());
|
2022-07-11 12:20:35 +00:00
|
|
|
QCOMPARE(window->frameGeometry(), outputs[0]->geometry());
|
2021-01-20 18:03:45 +00:00
|
|
|
|
|
|
|
// this doesn't require a round trip, so should be immediate
|
2022-04-23 19:51:16 +00:00
|
|
|
window->sendToOutput(outputs[1]);
|
2022-07-11 12:20:35 +00:00
|
|
|
QCOMPARE(window->frameGeometry(), outputs[1]->geometry());
|
2021-01-20 18:03:45 +00:00
|
|
|
QCOMPARE(geometryChangedSpy.count(), 2);
|
|
|
|
}
|
|
|
|
|
2023-03-07 16:12:08 +00:00
|
|
|
void TestPlacement::testCascadeIfCovering()
|
|
|
|
{
|
|
|
|
// This test verifies that the cascade-if-covering adjustment works for the Centered placement
|
|
|
|
// policy.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementCentered));
|
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
|
|
|
// window should be in center
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
|
|
|
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::red);
|
|
|
|
QVERIFY(window1);
|
|
|
|
QCOMPARE(window1->pos(), QPoint(590, 487));
|
|
|
|
QCOMPARE(window1->size(), QSize(100, 50));
|
|
|
|
|
|
|
|
// window should be cascaded to avoid overlapping window 1
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
|
|
|
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
|
|
|
|
QVERIFY(window2);
|
|
|
|
QCOMPARE(window2->pos(), window1->pos() + workspace()->cascadeOffset(window2));
|
|
|
|
QCOMPARE(window2->size(), QSize(100, 50));
|
|
|
|
|
|
|
|
// window should be cascaded to avoid overlapping window 1 and 2
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
|
|
|
|
Window *window3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::green);
|
|
|
|
QVERIFY(window3);
|
|
|
|
QCOMPARE(window3->pos(), window2->pos() + workspace()->cascadeOffset(window3));
|
|
|
|
QCOMPARE(window3->size(), QSize(100, 50));
|
|
|
|
|
|
|
|
shellSurface3.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window3));
|
2023-03-07 16:12:08 +00:00
|
|
|
shellSurface2.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window2));
|
2023-03-07 16:12:08 +00:00
|
|
|
shellSurface1.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window1));
|
2023-03-07 16:12:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::testCascadeIfCoveringIgnoreNonCovering()
|
|
|
|
{
|
|
|
|
// This test verifies that the cascade-if-covering adjustment doesn't take effect when the
|
|
|
|
// other window wouldn't be fully covered.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementCentered));
|
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
|
|
|
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::red);
|
|
|
|
QVERIFY(window1);
|
|
|
|
|
|
|
|
// window should not be cascaded since it wouldn't fully overlap
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
|
|
|
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(50, 50), Qt::blue);
|
|
|
|
QVERIFY(window2);
|
|
|
|
QCOMPARE(window2->pos(), QPoint(615, 487));
|
|
|
|
QCOMPARE(window2->size(), QSize(50, 50));
|
|
|
|
|
|
|
|
shellSurface2.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window2));
|
2023-03-07 16:12:08 +00:00
|
|
|
shellSurface1.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window1));
|
2023-03-07 16:12:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::testCascadeIfCoveringIgnoreOutOfArea()
|
|
|
|
{
|
|
|
|
// This test verifies that the cascade-if-covering adjustment doesn't take effect when there is
|
|
|
|
// not enough space on the placement area to cascade.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementCentered));
|
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
|
|
|
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::red);
|
|
|
|
QVERIFY(window1);
|
|
|
|
|
|
|
|
// window should not be cascaded since it would be out of bounds of work area
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
|
|
|
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(1280, 1024), Qt::blue);
|
|
|
|
QVERIFY(window2);
|
|
|
|
QCOMPARE(window2->pos(), QPoint(0, 0));
|
|
|
|
QCOMPARE(window2->size(), QSize(1280, 1024));
|
|
|
|
|
|
|
|
shellSurface2.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window2));
|
2023-03-07 16:12:08 +00:00
|
|
|
shellSurface1.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window1));
|
2023-03-07 16:12:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestPlacement::testCascadeIfCoveringIgnoreAlreadyCovered()
|
|
|
|
{
|
|
|
|
// This test verifies that the cascade-if-covering adjustment doesn't take effect when the
|
|
|
|
// other window is already fully covered by other windows anyway.
|
|
|
|
|
|
|
|
KConfigGroup group = kwinApp()->config()->group("Windows");
|
|
|
|
group.writeEntry("Placement", Placement::policyToString(PlacementCentered));
|
|
|
|
group.sync();
|
|
|
|
workspace()->slotReconfigure();
|
|
|
|
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
|
|
|
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::red);
|
|
|
|
QVERIFY(window1);
|
|
|
|
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
|
|
|
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(1280, 1024), Qt::blue);
|
|
|
|
QVERIFY(window2);
|
|
|
|
|
|
|
|
// window should not be cascaded since the small window is already fully covered by the
|
|
|
|
// large window anyway
|
|
|
|
std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
|
|
|
|
std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
|
|
|
|
Window *window3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::green);
|
|
|
|
QVERIFY(window3);
|
|
|
|
QCOMPARE(window3->pos(), QPoint(590, 487));
|
|
|
|
QCOMPARE(window3->size(), QSize(100, 50));
|
|
|
|
|
|
|
|
shellSurface3.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window3));
|
2023-03-07 16:12:08 +00:00
|
|
|
shellSurface2.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window2));
|
2023-03-07 16:12:08 +00:00
|
|
|
shellSurface1.reset();
|
2023-04-21 20:28:48 +00:00
|
|
|
QVERIFY(Test::waitForWindowClosed(window1));
|
2023-03-07 16:12:08 +00:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:41:41 +00:00
|
|
|
WAYLANDTEST_MAIN(TestPlacement)
|
2019-09-12 16:45:53 +00:00
|
|
|
#include "placement_test.moc"
|