2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2014-09-26 12:01:44 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
2014-09-26 12:01:44 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2014-09-26 12:01:44 +00:00
|
|
|
// kwin
|
2021-02-09 18:18:36 +00:00
|
|
|
#include "atoms.h"
|
|
|
|
#include "cursor.h"
|
|
|
|
#include "input.h"
|
|
|
|
#include "gestures.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "screenedge.h"
|
|
|
|
#include "screens.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "virtualdesktops.h"
|
|
|
|
#include "xcbutils.h"
|
|
|
|
#include "platform.h"
|
|
|
|
|
2014-09-26 12:01:44 +00:00
|
|
|
#include "mock_screens.h"
|
|
|
|
#include "mock_workspace.h"
|
2019-09-24 08:48:08 +00:00
|
|
|
#include "mock_x11client.h"
|
2016-09-08 13:08:45 +00:00
|
|
|
#include "testutils.h"
|
2014-09-26 12:01:44 +00:00
|
|
|
// Frameworks
|
|
|
|
#include <KConfigGroup>
|
|
|
|
// Qt
|
2018-06-05 10:52:57 +00:00
|
|
|
#include <QtTest>
|
2016-11-11 08:59:46 +00:00
|
|
|
#include <QX11Info>
|
2014-09-26 12:01:44 +00:00
|
|
|
// xcb
|
|
|
|
#include <xcb/xcb.h>
|
|
|
|
Q_DECLARE_METATYPE(KWin::ElectricBorder)
|
|
|
|
|
2015-07-31 08:17:43 +00:00
|
|
|
Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core")
|
|
|
|
|
2014-09-26 12:01:44 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
Atoms* atoms;
|
|
|
|
int screen_number = 0;
|
|
|
|
|
|
|
|
InputRedirection *InputRedirection::s_self = nullptr;
|
|
|
|
|
|
|
|
void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action)
|
|
|
|
{
|
|
|
|
Q_UNUSED(shortcut)
|
|
|
|
Q_UNUSED(action)
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirection::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
|
|
|
|
{
|
|
|
|
Q_UNUSED(modifiers)
|
|
|
|
Q_UNUSED(axis)
|
|
|
|
Q_UNUSED(action)
|
|
|
|
}
|
|
|
|
|
2017-03-18 10:00:30 +00:00
|
|
|
void InputRedirection::registerTouchpadSwipeShortcut(SwipeDirection, QAction*)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-09-26 12:01:44 +00:00
|
|
|
void updateXTime()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
class TestObject : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public Q_SLOTS:
|
|
|
|
bool callback(ElectricBorder border);
|
|
|
|
Q_SIGNALS:
|
|
|
|
void gotCallback(KWin::ElectricBorder);
|
|
|
|
};
|
|
|
|
|
|
|
|
bool TestObject::callback(KWin::ElectricBorder border)
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT gotCallback(border);
|
2014-09-26 12:01:44 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class TestScreenEdges : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
private Q_SLOTS:
|
|
|
|
void initTestCase();
|
|
|
|
void cleanupTestCase();
|
|
|
|
void init();
|
|
|
|
void cleanup();
|
|
|
|
void testInit();
|
|
|
|
void testCreatingInitialEdges();
|
|
|
|
void testCallback();
|
2014-09-27 10:28:03 +00:00
|
|
|
void testCallbackWithCheck();
|
2020-08-18 17:51:35 +00:00
|
|
|
void testOverlappingEdges_data();
|
|
|
|
void testOverlappingEdges();
|
2014-09-26 12:01:44 +00:00
|
|
|
void testPushBack_data();
|
|
|
|
void testPushBack();
|
|
|
|
void testFullScreenBlocking();
|
2014-09-27 06:18:20 +00:00
|
|
|
void testClientEdge();
|
2017-03-30 05:08:38 +00:00
|
|
|
void testTouchEdge();
|
2017-03-31 05:41:21 +00:00
|
|
|
void testTouchCallback_data();
|
|
|
|
void testTouchCallback();
|
2014-09-26 12:01:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void TestScreenEdges::initTestCase()
|
|
|
|
{
|
2015-02-18 07:31:53 +00:00
|
|
|
qApp->setProperty("x11RootWindow", QVariant::fromValue<quint32>(QX11Info::appRootWindow()));
|
2015-02-18 10:19:04 +00:00
|
|
|
qApp->setProperty("x11Connection", QVariant::fromValue<void*>(QX11Info::connection()));
|
2014-09-26 12:01:44 +00:00
|
|
|
KWin::atoms = new KWin::Atoms;
|
|
|
|
qRegisterMetaType<KWin::ElectricBorder>();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::cleanupTestCase()
|
|
|
|
{
|
|
|
|
delete KWin::atoms;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::init()
|
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
KWin::Cursors::self()->setMouse(new KWin::Cursor(this));
|
|
|
|
|
2014-09-26 12:01:44 +00:00
|
|
|
using namespace KWin;
|
2020-09-02 11:13:16 +00:00
|
|
|
new MockWorkspace(this);
|
2014-09-26 12:01:44 +00:00
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
Screens::create();
|
2020-09-02 15:08:28 +00:00
|
|
|
QSignalSpy sp(screens(), &MockScreens::changed);
|
|
|
|
QVERIFY(sp.wait());
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
auto vd = VirtualDesktopManager::create();
|
|
|
|
vd->setConfig(config);
|
|
|
|
vd->load();
|
|
|
|
auto s = ScreenEdges::create();
|
|
|
|
s->setConfig(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::cleanup()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
delete ScreenEdges::self();
|
|
|
|
delete VirtualDesktopManager::self();
|
|
|
|
delete Screens::self();
|
2014-09-27 06:18:20 +00:00
|
|
|
delete workspace();
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::testInit()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->init();
|
|
|
|
QCOMPARE(s->isDesktopSwitching(), false);
|
|
|
|
QCOMPARE(s->isDesktopSwitchingMovingClients(), false);
|
|
|
|
QCOMPARE(s->timeThreshold(), 150);
|
|
|
|
QCOMPARE(s->reActivationThreshold(), 350);
|
|
|
|
QCOMPARE(s->cursorPushBackDistance(), QSize(1, 1));
|
|
|
|
QCOMPARE(s->actionTopLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionTop(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionTopRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottomRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottom(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottomLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
|
|
|
|
QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 8);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QVERIFY(!e->isReserved());
|
|
|
|
QVERIFY(e->inherits("KWin::WindowBasedEdge"));
|
|
|
|
QVERIFY(!e->inherits("KWin::AreaBasedEdge"));
|
|
|
|
QVERIFY(!e->client());
|
|
|
|
QVERIFY(!e->isApproaching());
|
|
|
|
}
|
|
|
|
Edge *te = edges.at(0);
|
|
|
|
QVERIFY(te->isCorner());
|
|
|
|
QVERIFY(!te->isScreenEdge());
|
|
|
|
QVERIFY(te->isLeft());
|
|
|
|
QVERIFY(te->isTop());
|
|
|
|
QVERIFY(!te->isRight());
|
|
|
|
QVERIFY(!te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricTopLeft);
|
|
|
|
te = edges.at(1);
|
|
|
|
QVERIFY(te->isCorner());
|
|
|
|
QVERIFY(!te->isScreenEdge());
|
|
|
|
QVERIFY(te->isLeft());
|
|
|
|
QVERIFY(!te->isTop());
|
|
|
|
QVERIFY(!te->isRight());
|
|
|
|
QVERIFY(te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricBottomLeft);
|
|
|
|
te = edges.at(2);
|
|
|
|
QVERIFY(!te->isCorner());
|
|
|
|
QVERIFY(te->isScreenEdge());
|
|
|
|
QVERIFY(te->isLeft());
|
|
|
|
QVERIFY(!te->isTop());
|
|
|
|
QVERIFY(!te->isRight());
|
|
|
|
QVERIFY(!te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricLeft);
|
|
|
|
te = edges.at(3);
|
|
|
|
QVERIFY(te->isCorner());
|
|
|
|
QVERIFY(!te->isScreenEdge());
|
|
|
|
QVERIFY(!te->isLeft());
|
|
|
|
QVERIFY(te->isTop());
|
|
|
|
QVERIFY(te->isRight());
|
|
|
|
QVERIFY(!te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricTopRight);
|
|
|
|
te = edges.at(4);
|
|
|
|
QVERIFY(te->isCorner());
|
|
|
|
QVERIFY(!te->isScreenEdge());
|
|
|
|
QVERIFY(!te->isLeft());
|
|
|
|
QVERIFY(!te->isTop());
|
|
|
|
QVERIFY(te->isRight());
|
|
|
|
QVERIFY(te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricBottomRight);
|
|
|
|
te = edges.at(5);
|
|
|
|
QVERIFY(!te->isCorner());
|
|
|
|
QVERIFY(te->isScreenEdge());
|
|
|
|
QVERIFY(!te->isLeft());
|
|
|
|
QVERIFY(!te->isTop());
|
|
|
|
QVERIFY(te->isRight());
|
|
|
|
QVERIFY(!te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricRight);
|
|
|
|
te = edges.at(6);
|
|
|
|
QVERIFY(!te->isCorner());
|
|
|
|
QVERIFY(te->isScreenEdge());
|
|
|
|
QVERIFY(!te->isLeft());
|
|
|
|
QVERIFY(te->isTop());
|
|
|
|
QVERIFY(!te->isRight());
|
|
|
|
QVERIFY(!te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricTop);
|
|
|
|
te = edges.at(7);
|
|
|
|
QVERIFY(!te->isCorner());
|
|
|
|
QVERIFY(te->isScreenEdge());
|
|
|
|
QVERIFY(!te->isLeft());
|
|
|
|
QVERIFY(!te->isTop());
|
|
|
|
QVERIFY(!te->isRight());
|
|
|
|
QVERIFY(te->isBottom());
|
|
|
|
QCOMPARE(te->border(), ElectricBorder::ElectricBottom);
|
|
|
|
|
|
|
|
// we shouldn't have any x windows, though
|
|
|
|
QCOMPARE(s->windows().size(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::testCreatingInitialEdges()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
config->group("Windows").writeEntry("ElectricBorders", 2/*ElectricAlways*/);
|
|
|
|
config->sync();
|
|
|
|
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->setConfig(config);
|
|
|
|
s->init();
|
|
|
|
// we don't have multiple desktops, so it's returning false
|
|
|
|
QCOMPARE(s->isDesktopSwitching(), true);
|
|
|
|
QCOMPARE(s->isDesktopSwitchingMovingClients(), true);
|
|
|
|
QCOMPARE(s->actionTopLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionTop(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionTopRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottomRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottom(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottomLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
|
|
|
|
QEXPECT_FAIL("", "needs fixing", Continue);
|
|
|
|
QCOMPARE(s->windows().size(), 0);
|
|
|
|
|
|
|
|
// set some reasonable virtual desktops
|
|
|
|
config->group("Desktops").writeEntry("Number", 4);
|
|
|
|
config->sync();
|
|
|
|
auto vd = VirtualDesktopManager::self();
|
|
|
|
vd->setConfig(config);
|
|
|
|
vd->load();
|
2019-01-11 14:18:51 +00:00
|
|
|
vd->updateLayout();
|
2014-09-26 12:01:44 +00:00
|
|
|
QCOMPARE(vd->count(), 4u);
|
|
|
|
QCOMPARE(vd->grid().width(), 2);
|
|
|
|
QCOMPARE(vd->grid().height(), 2);
|
|
|
|
|
|
|
|
// approach windows for edges not created as screen too small
|
|
|
|
s->updateLayout();
|
|
|
|
auto edgeWindows = s->windows();
|
|
|
|
QCOMPARE(edgeWindows.size(), 12);
|
|
|
|
|
|
|
|
auto testWindowGeometry = [&](int index) {
|
|
|
|
Xcb::WindowGeometry geo(edgeWindows[index]);
|
|
|
|
return geo.rect();
|
|
|
|
};
|
|
|
|
QRect sg = screens()->geometry();
|
|
|
|
const int co = s->cornerOffset();
|
|
|
|
QList<QRect> expectedGeometries{
|
|
|
|
QRect(0, 0, 1, 1),
|
|
|
|
QRect(0, 0, co, co),
|
|
|
|
QRect(0, sg.bottom(), 1, 1),
|
|
|
|
QRect(0, sg.height() - co, co, co),
|
|
|
|
QRect(0, co, 1, sg.height() - co*2),
|
|
|
|
// QRect(0, co * 2 + 1, co, sg.height() - co*4),
|
|
|
|
QRect(sg.right(), 0, 1, 1),
|
|
|
|
QRect(sg.right() - co + 1, 0, co, co),
|
|
|
|
QRect(sg.right(), sg.bottom(), 1, 1),
|
|
|
|
QRect(sg.right() - co + 1, sg.bottom() - co + 1, co, co),
|
|
|
|
QRect(sg.right(), co, 1, sg.height() - co*2),
|
|
|
|
// QRect(sg.right() - co + 1, co * 2, co, sg.height() - co*4),
|
|
|
|
QRect(co, 0, sg.width() - co * 2, 1),
|
|
|
|
// QRect(co * 2, 0, sg.width() - co * 4, co),
|
|
|
|
QRect(co, sg.bottom(), sg.width() - co * 2, 1),
|
|
|
|
// QRect(co * 2, sg.height() - co, sg.width() - co * 4, co)
|
|
|
|
};
|
|
|
|
for (int i = 0; i < 12; ++i) {
|
|
|
|
QCOMPARE(testWindowGeometry(i), expectedGeometries.at(i));
|
|
|
|
}
|
|
|
|
QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 8);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QVERIFY(e->isReserved());
|
2017-03-30 05:08:38 +00:00
|
|
|
QCOMPARE(e->activatesForPointer(), true);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static_cast<MockScreens*>(screens())->setGeometries(QList<QRect>{QRect{0, 0, 1024, 768}});
|
2019-07-29 19:45:19 +00:00
|
|
|
QSignalSpy changedSpy(screens(), &Screens::changed);
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(changedSpy.isValid());
|
|
|
|
QVERIFY(changedSpy.wait());
|
|
|
|
|
|
|
|
// let's update the layout and verify that we have edges
|
|
|
|
s->recreateEdges();
|
|
|
|
edgeWindows = s->windows();
|
|
|
|
QCOMPARE(edgeWindows.size(), 16);
|
|
|
|
sg = screens()->geometry();
|
|
|
|
expectedGeometries = QList<QRect>{
|
|
|
|
QRect(0, 0, 1, 1),
|
|
|
|
QRect(0, 0, co, co),
|
|
|
|
QRect(0, sg.bottom(), 1, 1),
|
|
|
|
QRect(0, sg.height() - co, co, co),
|
|
|
|
QRect(0, co, 1, sg.height() - co*2),
|
|
|
|
QRect(0, co * 2 + 1, co, sg.height() - co*4),
|
|
|
|
QRect(sg.right(), 0, 1, 1),
|
|
|
|
QRect(sg.right() - co + 1, 0, co, co),
|
|
|
|
QRect(sg.right(), sg.bottom(), 1, 1),
|
|
|
|
QRect(sg.right() - co + 1, sg.bottom() - co + 1, co, co),
|
|
|
|
QRect(sg.right(), co, 1, sg.height() - co*2),
|
|
|
|
QRect(sg.right() - co + 1, co * 2, co, sg.height() - co*4),
|
|
|
|
QRect(co, 0, sg.width() - co * 2, 1),
|
|
|
|
QRect(co * 2, 0, sg.width() - co * 4, co),
|
|
|
|
QRect(co, sg.bottom(), sg.width() - co * 2, 1),
|
|
|
|
QRect(co * 2, sg.height() - co, sg.width() - co * 4, co)
|
|
|
|
};
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
|
|
QCOMPARE(testWindowGeometry(i), expectedGeometries.at(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
// disable desktop switching again
|
|
|
|
config->group("Windows").writeEntry("ElectricBorders", 1/*ElectricMoveOnly*/);
|
|
|
|
s->reconfigure();
|
|
|
|
QCOMPARE(s->isDesktopSwitching(), false);
|
|
|
|
QCOMPARE(s->isDesktopSwitchingMovingClients(), true);
|
|
|
|
QCOMPARE(s->windows().size(), 0);
|
|
|
|
edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 8);
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
auto e = edges.at(i);
|
|
|
|
QVERIFY(!e->isReserved());
|
2017-03-30 05:08:38 +00:00
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
2014-09-26 12:01:44 +00:00
|
|
|
QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1));
|
|
|
|
}
|
2017-06-18 11:36:27 +00:00
|
|
|
|
|
|
|
// let's start a move of window.
|
2019-09-24 08:48:08 +00:00
|
|
|
X11Client client(workspace());
|
2019-04-18 12:28:11 +00:00
|
|
|
workspace()->setMoveResizeClient(&client);
|
2017-06-18 11:36:27 +00:00
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
auto e = edges.at(i);
|
|
|
|
QVERIFY(!e->isReserved());
|
|
|
|
QCOMPARE(e->activatesForPointer(), true);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
|
|
|
QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1));
|
|
|
|
}
|
|
|
|
// not for resize
|
2021-04-30 18:06:58 +00:00
|
|
|
client.setInteractiveResize(true);
|
2017-06-18 11:36:27 +00:00
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
auto e = edges.at(i);
|
|
|
|
QVERIFY(!e->isReserved());
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
|
|
|
QCOMPARE(e->approachGeometry(), expectedGeometries.at(i*2+1));
|
|
|
|
}
|
2019-04-18 12:28:11 +00:00
|
|
|
workspace()->setMoveResizeClient(nullptr);
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::testCallback()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
MockWorkspace ws;
|
|
|
|
static_cast<MockScreens*>(screens())->setGeometries(QList<QRect>{QRect{0, 0, 1024, 768}, QRect{200, 768, 1024, 768}});
|
2019-07-29 19:45:19 +00:00
|
|
|
QSignalSpy changedSpy(screens(), &Screens::changed);
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(changedSpy.isValid());
|
|
|
|
// first is before it's updated
|
|
|
|
QVERIFY(changedSpy.wait());
|
|
|
|
// second is after it's updated
|
|
|
|
QVERIFY(changedSpy.wait());
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->init();
|
|
|
|
TestObject callback;
|
2019-07-29 19:45:19 +00:00
|
|
|
QSignalSpy spy(&callback, &TestObject::gotCallback);
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isValid());
|
|
|
|
s->reserve(ElectricLeft, &callback, "callback");
|
|
|
|
s->reserve(ElectricTopLeft, &callback, "callback");
|
|
|
|
s->reserve(ElectricTop, &callback, "callback");
|
|
|
|
s->reserve(ElectricTopRight, &callback, "callback");
|
|
|
|
s->reserve(ElectricRight, &callback, "callback");
|
|
|
|
s->reserve(ElectricBottomRight, &callback, "callback");
|
|
|
|
s->reserve(ElectricBottom, &callback, "callback");
|
|
|
|
s->reserve(ElectricBottomLeft, &callback, "callback");
|
|
|
|
|
|
|
|
QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 10);
|
|
|
|
for (auto e: edges) {
|
|
|
|
QVERIFY(e->isReserved());
|
2017-03-30 05:08:38 +00:00
|
|
|
QCOMPARE(e->activatesForPointer(), true);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
auto it = std::find_if(edges.constBegin(), edges.constEnd(), [](Edge *e) {
|
|
|
|
return e->isScreenEdge() && e->isLeft() && e->approachGeometry().bottom() < 768;
|
|
|
|
});
|
|
|
|
QVERIFY(it != edges.constEnd());
|
|
|
|
|
|
|
|
xcb_enter_notify_event_t event;
|
|
|
|
auto setPos = [&event] (const QPoint &pos) {
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(pos);
|
2014-09-26 12:01:44 +00:00
|
|
|
event.root_x = pos.x();
|
|
|
|
event.root_y = pos.y();
|
|
|
|
event.event_x = pos.x();
|
|
|
|
event.event_y = pos.y();
|
|
|
|
};
|
|
|
|
event.root = XCB_WINDOW_NONE;
|
|
|
|
event.child = XCB_WINDOW_NONE;
|
2016-04-15 11:19:22 +00:00
|
|
|
event.event = (*it)->window();
|
2014-09-26 12:01:44 +00:00
|
|
|
event.same_screen_focus = 1;
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
setPos(QPoint(0, 50));
|
2017-08-19 08:58:05 +00:00
|
|
|
auto isEntered = [s] (xcb_enter_notify_event_t *event) {
|
2020-02-03 10:55:46 +00:00
|
|
|
return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time, Qt::UTC));
|
2017-08-19 08:58:05 +00:00
|
|
|
};
|
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
// doesn't trigger as the edge was not triggered yet
|
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 50));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// test doesn't trigger due to too much offset
|
|
|
|
QTest::qWait(160);
|
|
|
|
setPos(QPoint(0, 100));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 100));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// doesn't trigger as we are waiting too long already
|
|
|
|
QTest::qWait(200);
|
|
|
|
setPos(QPoint(0, 101));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 101));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// doesn't activate as we are waiting too short
|
|
|
|
QTest::qWait(50);
|
|
|
|
setPos(QPoint(0, 100));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 100));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// and this one triggers
|
|
|
|
QTest::qWait(110);
|
|
|
|
setPos(QPoint(0, 101));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(!spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 101));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// now let's try to trigger again
|
2015-09-30 08:29:17 +00:00
|
|
|
QTest::qWait(351);
|
2014-09-26 12:01:44 +00:00
|
|
|
setPos(QPoint(0, 100));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QCOMPARE(spy.count(), 1);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 100));
|
2014-09-26 12:01:44 +00:00
|
|
|
// it's still under the reactivation
|
|
|
|
QTest::qWait(50);
|
|
|
|
setPos(QPoint(0, 100));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QCOMPARE(spy.count(), 1);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 100));
|
2014-09-26 12:01:44 +00:00
|
|
|
// now it should trigger again
|
|
|
|
QTest::qWait(250);
|
|
|
|
setPos(QPoint(0, 100));
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QCOMPARE(spy.count(), 2);
|
|
|
|
QCOMPARE(spy.first().first().value<ElectricBorder>(), ElectricLeft);
|
|
|
|
QCOMPARE(spy.last().first().value<ElectricBorder>(), ElectricLeft);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 100));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// let's disable pushback
|
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
config->group("Windows").writeEntry("ElectricBorderPushbackPixels", 0);
|
|
|
|
config->sync();
|
|
|
|
s->setConfig(config);
|
|
|
|
s->reconfigure();
|
|
|
|
// it should trigger directly
|
2015-09-30 08:29:17 +00:00
|
|
|
QTest::qWait(350);
|
2014-09-26 12:01:44 +00:00
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QCOMPARE(spy.count(), 3);
|
|
|
|
QCOMPARE(spy.at(0).first().value<ElectricBorder>(), ElectricLeft);
|
|
|
|
QCOMPARE(spy.at(1).first().value<ElectricBorder>(), ElectricLeft);
|
|
|
|
QCOMPARE(spy.at(2).first().value<ElectricBorder>(), ElectricLeft);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(0, 100));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// now let's unreserve again
|
|
|
|
s->unreserve(ElectricTopLeft, &callback);
|
|
|
|
s->unreserve(ElectricTop, &callback);
|
|
|
|
s->unreserve(ElectricTopRight, &callback);
|
|
|
|
s->unreserve(ElectricRight, &callback);
|
|
|
|
s->unreserve(ElectricBottomRight, &callback);
|
|
|
|
s->unreserve(ElectricBottom, &callback);
|
|
|
|
s->unreserve(ElectricBottomLeft, &callback);
|
|
|
|
s->unreserve(ElectricLeft, &callback);
|
|
|
|
for (auto e: s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly)) {
|
|
|
|
QVERIFY(!e->isReserved());
|
2017-03-30 05:08:38 +00:00
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-27 10:28:03 +00:00
|
|
|
void TestScreenEdges::testCallbackWithCheck()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->init();
|
|
|
|
TestObject callback;
|
2019-07-29 19:45:19 +00:00
|
|
|
QSignalSpy spy(&callback, &TestObject::gotCallback);
|
2014-09-27 10:28:03 +00:00
|
|
|
QVERIFY(spy.isValid());
|
|
|
|
s->reserve(ElectricLeft, &callback, "callback");
|
|
|
|
|
|
|
|
// check activating a different edge doesn't do anything
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(50, 0), QDateTime::currentDateTimeUtc(), true);
|
2014-09-27 10:28:03 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
|
|
|
|
|
|
|
// try a direct activate without pushback
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(0, 50), QDateTime::currentDateTimeUtc(), true);
|
2014-09-27 10:28:03 +00:00
|
|
|
QCOMPARE(spy.count(), 1);
|
|
|
|
QEXPECT_FAIL("", "Argument says force no pushback, but it gets pushed back. Needs investigation", Continue);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(0, 50));
|
2014-09-27 10:28:03 +00:00
|
|
|
|
|
|
|
// use a different edge, this time with pushback
|
|
|
|
s->reserve(KWin::ElectricRight, &callback, "callback");
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(99, 50);
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(99, 50), QDateTime::currentDateTimeUtc());
|
2014-09-27 10:28:03 +00:00
|
|
|
QCOMPARE(spy.count(), 1);
|
|
|
|
QCOMPARE(spy.last().first().value<ElectricBorder>(), ElectricLeft);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(98, 50));
|
2014-09-27 10:28:03 +00:00
|
|
|
// and trigger it again
|
|
|
|
QTest::qWait(160);
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(99, 50);
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(99, 50), QDateTime::currentDateTimeUtc());
|
2014-09-27 10:28:03 +00:00
|
|
|
QCOMPARE(spy.count(), 2);
|
|
|
|
QCOMPARE(spy.last().first().value<ElectricBorder>(), ElectricRight);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(98, 50));
|
2014-09-27 10:28:03 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 17:51:35 +00:00
|
|
|
void TestScreenEdges::testOverlappingEdges_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QRect>("geo1");
|
|
|
|
QTest::addColumn<QRect>("geo2");
|
|
|
|
|
|
|
|
QTest::newRow("topleft-1x1") << QRect{0, 1, 1024, 768} << QRect{1, 0, 1024, 768};
|
|
|
|
QTest::newRow("left-1x1-same") << QRect{0, 1, 1024, 766} << QRect{1, 0, 1024, 768};
|
|
|
|
QTest::newRow("left-1x1-exchanged") << QRect{0, 1, 1024, 768} << QRect{1, 0, 1024, 766};
|
|
|
|
QTest::newRow("bottomleft-1x1") << QRect{0, 0, 1024, 768} << QRect{1, 0, 1024, 769};
|
|
|
|
QTest::newRow("bottomright-1x1") << QRect{0, 0, 1024, 768} << QRect{0, 0, 1023, 769};
|
|
|
|
QTest::newRow("right-1x1-same") << QRect{0, 0, 1024, 768} << QRect{0, 1, 1025, 766};
|
|
|
|
QTest::newRow("right-1x1-exchanged") << QRect{0, 0, 1024, 768} << QRect{1, 1, 1024, 768};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TestScreenEdges::testOverlappingEdges()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
QFETCH(QRect, geo1);
|
|
|
|
QFETCH(QRect, geo2);
|
|
|
|
|
2020-09-02 15:08:28 +00:00
|
|
|
MockScreens* mockScreens = static_cast<MockScreens*>(screens());
|
|
|
|
QSignalSpy sp(mockScreens, &MockScreens::changed);
|
|
|
|
mockScreens->setGeometries({geo1, geo2});
|
|
|
|
QVERIFY(sp.wait());
|
2020-08-18 17:51:35 +00:00
|
|
|
|
2020-09-02 15:08:28 +00:00
|
|
|
QCOMPARE(screens()->count(), 2);
|
2020-08-18 17:51:35 +00:00
|
|
|
auto screenEdges = ScreenEdges::self();
|
|
|
|
screenEdges->init();
|
|
|
|
}
|
|
|
|
|
2014-09-26 12:01:44 +00:00
|
|
|
void TestScreenEdges::testPushBack_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<KWin::ElectricBorder>("border");
|
|
|
|
QTest::addColumn<int>("pushback");
|
|
|
|
QTest::addColumn<QPoint>("trigger");
|
|
|
|
QTest::addColumn<QPoint>("expected");
|
|
|
|
|
|
|
|
QTest::newRow("topleft-3") << KWin::ElectricTopLeft << 3 << QPoint(0, 0) << QPoint(3, 3);
|
|
|
|
QTest::newRow("top-5") << KWin::ElectricTop << 5 << QPoint(50, 0) << QPoint(50, 5);
|
|
|
|
QTest::newRow("toprigth-2") << KWin::ElectricTopRight << 2 << QPoint(99, 0) << QPoint(97, 2);
|
|
|
|
QTest::newRow("right-10") << KWin::ElectricRight << 10 << QPoint(99, 50) << QPoint(89, 50);
|
|
|
|
QTest::newRow("bottomright-5") << KWin::ElectricBottomRight << 5 << QPoint(99, 99) << QPoint(94, 94);
|
|
|
|
QTest::newRow("bottom-10") << KWin::ElectricBottom << 10 << QPoint(50, 99) << QPoint(50, 89);
|
|
|
|
QTest::newRow("bottomleft-3") << KWin::ElectricBottomLeft << 3 << QPoint(0, 99) << QPoint(3, 96);
|
|
|
|
QTest::newRow("left-10") << KWin::ElectricLeft << 10 << QPoint(0, 50) << QPoint(10, 50);
|
|
|
|
QTest::newRow("invalid") << KWin::ElectricLeft << 10 << QPoint(50, 0) << QPoint(50, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::testPushBack()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
QFETCH(int, pushback);
|
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
config->group("Windows").writeEntry("ElectricBorderPushbackPixels", pushback);
|
|
|
|
config->sync();
|
|
|
|
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->setConfig(config);
|
|
|
|
s->init();
|
|
|
|
TestObject callback;
|
2019-07-29 19:45:19 +00:00
|
|
|
QSignalSpy spy(&callback, &TestObject::gotCallback);
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isValid());
|
|
|
|
QFETCH(ElectricBorder, border);
|
|
|
|
s->reserve(border, &callback, "callback");
|
|
|
|
|
|
|
|
QFETCH(QPoint, trigger);
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(trigger);
|
2014-09-26 12:01:44 +00:00
|
|
|
xcb_enter_notify_event_t event;
|
|
|
|
event.root_x = trigger.x();
|
|
|
|
event.root_y = trigger.y();
|
|
|
|
event.event_x = trigger.x();
|
|
|
|
event.event_y = trigger.y();
|
|
|
|
event.root = XCB_WINDOW_NONE;
|
|
|
|
event.child = XCB_WINDOW_NONE;
|
|
|
|
event.event = s->windows().first();
|
|
|
|
event.same_screen_focus = 1;
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
auto isEntered = [s] (xcb_enter_notify_event_t *event) {
|
|
|
|
return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time));
|
|
|
|
};
|
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QTEST(Cursors::self()->mouse()->pos(), "expected");
|
2014-09-27 10:28:03 +00:00
|
|
|
|
|
|
|
// do the same without the event, but the check method
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(trigger);
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(trigger, QDateTime::currentDateTimeUtc());
|
2014-09-27 10:28:03 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QTEST(Cursors::self()->mouse()->pos(), "expected");
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::testFullScreenBlocking()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
|
|
|
MockWorkspace ws;
|
2019-09-24 08:48:08 +00:00
|
|
|
X11Client client(&ws);
|
2014-09-26 12:01:44 +00:00
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
config->group("Windows").writeEntry("ElectricBorderPushbackPixels", 1);
|
|
|
|
config->sync();
|
|
|
|
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->setConfig(config);
|
|
|
|
s->init();
|
|
|
|
TestObject callback;
|
2019-07-29 19:45:19 +00:00
|
|
|
QSignalSpy spy(&callback, &TestObject::gotCallback);
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isValid());
|
|
|
|
s->reserve(KWin::ElectricLeft, &callback, "callback");
|
|
|
|
s->reserve(KWin::ElectricBottomRight, &callback, "callback");
|
2017-04-11 18:59:54 +00:00
|
|
|
QAction action;
|
|
|
|
s->reserveTouch(KWin::ElectricRight, &action);
|
2014-09-26 12:01:44 +00:00
|
|
|
// currently there is no active client yet, so check blocking shouldn't do anything
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT s->checkBlocking();
|
2017-04-11 18:59:54 +00:00
|
|
|
for (auto e: s->findChildren<Edge*>()) {
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), e->border() == KWin::ElectricRight);
|
|
|
|
}
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
xcb_enter_notify_event_t event;
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2014-09-26 12:01:44 +00:00
|
|
|
event.root_x = 0;
|
|
|
|
event.root_y = 50;
|
|
|
|
event.event_x = 0;
|
|
|
|
event.event_y = 50;
|
|
|
|
event.root = XCB_WINDOW_NONE;
|
|
|
|
event.child = XCB_WINDOW_NONE;
|
|
|
|
event.event = s->windows().first();
|
|
|
|
event.same_screen_focus = 1;
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
auto isEntered = [s] (xcb_enter_notify_event_t *event) {
|
|
|
|
return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time));
|
|
|
|
};
|
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 50));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(screens()->geometry());
|
2014-09-26 12:01:44 +00:00
|
|
|
client.setActive(true);
|
|
|
|
client.setFullScreen(true);
|
|
|
|
ws.setActiveClient(&client);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT s->checkBlocking();
|
2014-09-26 12:01:44 +00:00
|
|
|
// the signal doesn't trigger for corners, let's go over all windows just to be sure that it doesn't call for corners
|
|
|
|
for (auto e: s->findChildren<Edge*>()) {
|
|
|
|
e->checkBlocking();
|
2017-04-11 18:59:54 +00:00
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
2014-09-26 12:01:44 +00:00
|
|
|
}
|
|
|
|
// calling again should not trigger
|
|
|
|
QTest::qWait(160);
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2014-09-26 12:01:44 +00:00
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
|
|
|
// and no pushback
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(0, 50));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// let's make the client not fullscreen, which should trigger
|
|
|
|
client.setFullScreen(false);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT s->checkBlocking();
|
2017-04-11 18:59:54 +00:00
|
|
|
for (auto e: s->findChildren<Edge*>()) {
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), e->border() == KWin::ElectricRight);
|
|
|
|
}
|
2014-09-26 12:01:44 +00:00
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(!spy.isEmpty());
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 50));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// let's make the client fullscreen again, but with a geometry not intersecting the left edge
|
2015-09-30 08:22:54 +00:00
|
|
|
QTest::qWait(351);
|
2014-09-26 12:01:44 +00:00
|
|
|
client.setFullScreen(true);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(client.frameGeometry().translated(10, 0));
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT s->checkBlocking();
|
2014-09-26 12:01:44 +00:00
|
|
|
spy.clear();
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2014-09-26 12:01:44 +00:00
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
|
|
|
// and a pushback
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 50));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// just to be sure, let's set geometry back
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(screens()->geometry());
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT s->checkBlocking();
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
|
|
|
// and no pushback
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(0, 50));
|
2014-09-26 12:01:44 +00:00
|
|
|
|
|
|
|
// the corner should always trigger
|
|
|
|
s->unreserve(KWin::ElectricLeft, &callback);
|
|
|
|
event.event_x = 99;
|
|
|
|
event.event_y = 99;
|
|
|
|
event.root_x = 99;
|
|
|
|
event.root_y = 99;
|
|
|
|
event.event = s->windows().first();
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(99, 99);
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(spy.isEmpty());
|
|
|
|
// and pushback
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(98, 98));
|
2014-09-26 12:01:44 +00:00
|
|
|
QTest::qWait(160);
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(99, 99);
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event));
|
2014-09-26 12:01:44 +00:00
|
|
|
QVERIFY(!spy.isEmpty());
|
|
|
|
}
|
|
|
|
|
2014-09-27 06:18:20 +00:00
|
|
|
void TestScreenEdges::testClientEdge()
|
|
|
|
{
|
|
|
|
using namespace KWin;
|
2019-09-24 08:48:08 +00:00
|
|
|
X11Client client(workspace());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(QRect(10, 50, 10, 50));
|
2014-09-27 06:18:20 +00:00
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->init();
|
|
|
|
|
|
|
|
s->reserve(&client, KWin::ElectricBottom);
|
|
|
|
|
2015-10-11 20:48:29 +00:00
|
|
|
QPointer<Edge> edge = s->findChildren<Edge*>().last();
|
|
|
|
|
2014-09-27 06:18:20 +00:00
|
|
|
QCOMPARE(edge->isReserved(), true);
|
2017-03-30 05:08:38 +00:00
|
|
|
QCOMPARE(edge->activatesForPointer(), true);
|
|
|
|
QCOMPARE(edge->activatesForTouchGesture(), true);
|
2014-09-27 06:18:20 +00:00
|
|
|
|
2015-10-14 12:44:37 +00:00
|
|
|
//remove old reserves and resize to be in the middle of the screen
|
|
|
|
s->reserve(&client, KWin::ElectricNone);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(QRect(2, 2, 20, 20));
|
2015-10-14 12:44:37 +00:00
|
|
|
|
2014-09-27 06:18:20 +00:00
|
|
|
// for none of the edges it should be able to be set
|
|
|
|
for (int i = 0; i < ELECTRIC_COUNT; ++i) {
|
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, static_cast<ElectricBorder>(i));
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now let's try to set it and activate it
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(screens()->geometry());
|
2014-09-27 06:18:20 +00:00
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, KWin::ElectricLeft);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), true);
|
|
|
|
|
|
|
|
xcb_enter_notify_event_t event;
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2014-09-27 06:18:20 +00:00
|
|
|
event.root_x = 0;
|
|
|
|
event.root_y = 50;
|
|
|
|
event.event_x = 0;
|
|
|
|
event.event_y = 50;
|
|
|
|
event.root = XCB_WINDOW_NONE;
|
|
|
|
event.child = XCB_WINDOW_NONE;
|
|
|
|
event.event = s->windows().first();
|
|
|
|
event.same_screen_focus = 1;
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
auto isEntered = [s] (xcb_enter_notify_event_t *event) {
|
|
|
|
return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time));
|
|
|
|
};
|
|
|
|
QVERIFY(isEntered(&event));
|
2015-06-03 23:43:27 +00:00
|
|
|
// autohiding panels shall activate instantly
|
2014-09-27 06:18:20 +00:00
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 50));
|
2014-09-27 06:18:20 +00:00
|
|
|
|
|
|
|
// now let's reserve the client for each of the edges, in the end for the right one
|
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, KWin::ElectricTop);
|
|
|
|
s->reserve(&client, KWin::ElectricBottom);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), true);
|
|
|
|
// corners shouldn't get reserved
|
|
|
|
s->reserve(&client, KWin::ElectricTopLeft);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, KWin::ElectricTopRight);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, KWin::ElectricBottomRight);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, KWin::ElectricBottomLeft);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
|
|
|
// now finally reserve on right one
|
|
|
|
client.setHiddenInternal(true);
|
|
|
|
s->reserve(&client, KWin::ElectricRight);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), true);
|
|
|
|
|
|
|
|
// now let's emulate the removal of a Client through Workspace
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT workspace()->clientRemoved(&client);
|
2014-09-27 06:18:20 +00:00
|
|
|
for (auto e : s->findChildren<Edge*>()) {
|
|
|
|
QVERIFY(!e->client());
|
|
|
|
}
|
|
|
|
QCOMPARE(client.isHiddenInternal(), true);
|
2014-09-27 10:28:03 +00:00
|
|
|
|
|
|
|
// now let's try to trigger the client showing with the check method instead of enter notify
|
|
|
|
s->reserve(&client, KWin::ElectricTop);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), true);
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(50, 0);
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(50, 0), QDateTime::currentDateTimeUtc());
|
2014-09-27 10:28:03 +00:00
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(50, 1));
|
2014-09-27 10:28:03 +00:00
|
|
|
|
|
|
|
// unreserve by setting to none edge
|
|
|
|
s->reserve(&client, KWin::ElectricNone);
|
|
|
|
// check on previous edge again, should fail
|
|
|
|
client.setHiddenInternal(true);
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(50, 0);
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(50, 0), QDateTime::currentDateTimeUtc());
|
2014-09-27 10:28:03 +00:00
|
|
|
QCOMPARE(client.isHiddenInternal(), true);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(50, 0));
|
2015-10-11 20:48:29 +00:00
|
|
|
|
|
|
|
// set to windows can cover
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
client.moveResize(screens()->geometry());
|
2015-10-11 20:48:29 +00:00
|
|
|
client.setHiddenInternal(false);
|
|
|
|
client.setKeepBelow(true);
|
|
|
|
s->reserve(&client, KWin::ElectricLeft);
|
|
|
|
QCOMPARE(client.keepBelow(), true);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
|
|
|
|
|
|
|
xcb_enter_notify_event_t event2;
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(0, 50);
|
2015-10-11 20:48:29 +00:00
|
|
|
event2.root_x = 0;
|
|
|
|
event2.root_y = 50;
|
|
|
|
event2.event_x = 0;
|
|
|
|
event2.event_y = 50;
|
|
|
|
event2.root = XCB_WINDOW_NONE;
|
|
|
|
event2.child = XCB_WINDOW_NONE;
|
|
|
|
event2.event = s->windows().first();
|
|
|
|
event2.same_screen_focus = 1;
|
|
|
|
event2.time = QDateTime::currentMSecsSinceEpoch();
|
2017-08-19 08:58:05 +00:00
|
|
|
QVERIFY(isEntered(&event2));
|
2015-10-11 20:48:29 +00:00
|
|
|
QCOMPARE(client.keepBelow(), false);
|
|
|
|
QCOMPARE(client.isHiddenInternal(), false);
|
2020-04-02 16:18:01 +00:00
|
|
|
QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(1, 50));
|
2014-09-27 06:18:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-30 05:08:38 +00:00
|
|
|
void TestScreenEdges::testTouchEdge()
|
|
|
|
{
|
|
|
|
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
|
|
|
|
using namespace KWin;
|
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
auto group = config->group("TouchEdges");
|
|
|
|
group.writeEntry("Top", "krunner");
|
|
|
|
group.writeEntry("Left", "krunner");
|
|
|
|
group.writeEntry("Bottom", "krunner");
|
|
|
|
group.writeEntry("Right", "krunner");
|
|
|
|
config->sync();
|
|
|
|
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->setConfig(config);
|
|
|
|
s->init();
|
|
|
|
// we don't have multiple desktops, so it's returning false
|
|
|
|
QCOMPARE(s->isDesktopSwitching(), false);
|
|
|
|
QCOMPARE(s->isDesktopSwitchingMovingClients(), false);
|
|
|
|
QCOMPARE(s->actionTopLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionTop(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionTopRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottomRight(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottom(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionBottomLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
QCOMPARE(s->actionLeft(), ElectricBorderAction::ElectricActionNone);
|
|
|
|
|
|
|
|
QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 8);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), e->isScreenEdge());
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), e->isScreenEdge());
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to activate the edge through pointer, should not be possible
|
|
|
|
auto it = std::find_if(edges.constBegin(), edges.constEnd(), [](Edge *e) {
|
|
|
|
return e->isScreenEdge() && e->isLeft();
|
|
|
|
});
|
|
|
|
QVERIFY(it != edges.constEnd());
|
|
|
|
|
|
|
|
QSignalSpy approachingSpy(s, &ScreenEdges::approaching);
|
|
|
|
QVERIFY(approachingSpy.isValid());
|
|
|
|
|
|
|
|
xcb_enter_notify_event_t event;
|
|
|
|
auto setPos = [&event] (const QPoint &pos) {
|
2020-04-02 16:18:01 +00:00
|
|
|
Cursors::self()->mouse()->setPos(pos);
|
2017-03-30 05:08:38 +00:00
|
|
|
event.root_x = pos.x();
|
|
|
|
event.root_y = pos.y();
|
|
|
|
event.event_x = pos.x();
|
|
|
|
event.event_y = pos.y();
|
|
|
|
};
|
|
|
|
event.root = XCB_WINDOW_NONE;
|
|
|
|
event.child = XCB_WINDOW_NONE;
|
|
|
|
event.event = (*it)->window();
|
|
|
|
event.same_screen_focus = 1;
|
|
|
|
event.time = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
setPos(QPoint(0, 50));
|
2017-08-19 08:58:05 +00:00
|
|
|
auto isEntered = [s] (xcb_enter_notify_event_t *event) {
|
2020-02-03 10:55:46 +00:00
|
|
|
return s->handleEnterNotifiy(event->event, QPoint(event->root_x, event->root_y), QDateTime::fromMSecsSinceEpoch(event->time, Qt::UTC));
|
2017-08-19 08:58:05 +00:00
|
|
|
};
|
|
|
|
QCOMPARE(isEntered(&event), false);
|
2017-03-30 05:08:38 +00:00
|
|
|
QVERIFY(approachingSpy.isEmpty());
|
2017-04-13 05:00:30 +00:00
|
|
|
// let's also verify the check
|
2020-02-03 10:55:46 +00:00
|
|
|
s->check(QPoint(0, 50), QDateTime::currentDateTimeUtc(), false);
|
2017-04-13 05:00:30 +00:00
|
|
|
QVERIFY(approachingSpy.isEmpty());
|
2017-03-30 05:08:38 +00:00
|
|
|
|
|
|
|
s->gestureRecognizer()->startSwipeGesture(QPoint(0, 50));
|
|
|
|
QCOMPARE(approachingSpy.count(), 1);
|
|
|
|
s->gestureRecognizer()->cancelSwipeGesture();
|
|
|
|
QCOMPARE(approachingSpy.count(), 2);
|
|
|
|
|
|
|
|
// let's reconfigure
|
|
|
|
group.writeEntry("Top", "none");
|
|
|
|
group.writeEntry("Left", "none");
|
|
|
|
group.writeEntry("Bottom", "none");
|
|
|
|
group.writeEntry("Right", "none");
|
|
|
|
config->sync();
|
|
|
|
s->reconfigure();
|
|
|
|
|
|
|
|
edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 8);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), false);
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-03-31 05:41:21 +00:00
|
|
|
void TestScreenEdges::testTouchCallback_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<KWin::ElectricBorder>("border");
|
|
|
|
QTest::addColumn<QPoint>("startPos");
|
|
|
|
QTest::addColumn<QSizeF>("delta");
|
|
|
|
|
|
|
|
QTest::newRow("left") << KWin::ElectricLeft << QPoint(0, 50) << QSizeF(250, 20);
|
|
|
|
QTest::newRow("top") << KWin::ElectricTop << QPoint(50, 0) << QSizeF(20, 250);
|
|
|
|
QTest::newRow("right") << KWin::ElectricRight << QPoint(99, 50) << QSizeF(-200, 0);
|
|
|
|
QTest::newRow("bottom") << KWin::ElectricBottom << QPoint(50, 99) << QSizeF(0, -200);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestScreenEdges::testTouchCallback()
|
|
|
|
{
|
|
|
|
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
|
|
|
|
using namespace KWin;
|
|
|
|
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
|
|
|
|
auto group = config->group("TouchEdges");
|
|
|
|
group.writeEntry("Top", "none");
|
|
|
|
group.writeEntry("Left", "none");
|
|
|
|
group.writeEntry("Bottom", "none");
|
|
|
|
group.writeEntry("Right", "none");
|
|
|
|
config->sync();
|
|
|
|
|
|
|
|
auto s = ScreenEdges::self();
|
|
|
|
s->setConfig(config);
|
|
|
|
s->init();
|
|
|
|
|
|
|
|
// none of our actions should be reserved
|
|
|
|
const QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
|
|
|
|
QCOMPARE(edges.size(), 8);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), false);
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// let's reserve an action
|
|
|
|
QAction action;
|
|
|
|
QSignalSpy actionTriggeredSpy(&action, &QAction::triggered);
|
|
|
|
QVERIFY(actionTriggeredSpy.isValid());
|
|
|
|
QSignalSpy approachingSpy(s, &ScreenEdges::approaching);
|
|
|
|
QVERIFY(approachingSpy.isValid());
|
|
|
|
|
|
|
|
// reserve on edge
|
|
|
|
QFETCH(KWin::ElectricBorder, border);
|
|
|
|
s->reserveTouch(border, &action);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), e->border() == border);
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), e->border() == border);
|
|
|
|
}
|
|
|
|
|
|
|
|
QVERIFY(approachingSpy.isEmpty());
|
|
|
|
QFETCH(QPoint, startPos);
|
|
|
|
QCOMPARE(s->gestureRecognizer()->startSwipeGesture(startPos), 1);
|
|
|
|
QVERIFY(actionTriggeredSpy.isEmpty());
|
|
|
|
QCOMPARE(approachingSpy.count(), 1);
|
|
|
|
QFETCH(QSizeF, delta);
|
|
|
|
s->gestureRecognizer()->updateSwipeGesture(delta);
|
|
|
|
QCOMPARE(approachingSpy.count(), 2);
|
|
|
|
QVERIFY(actionTriggeredSpy.isEmpty());
|
|
|
|
s->gestureRecognizer()->endSwipeGesture();
|
|
|
|
QVERIFY(actionTriggeredSpy.wait());
|
|
|
|
QCOMPARE(actionTriggeredSpy.count(), 1);
|
|
|
|
QCOMPARE(approachingSpy.count(), 3);
|
|
|
|
|
|
|
|
// unreserve again
|
|
|
|
s->unreserveTouch(border, &action);
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), false);
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// reserve another action
|
|
|
|
QScopedPointer<QAction> action2(new QAction);
|
|
|
|
s->reserveTouch(border, action2.data());
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), e->border() == border);
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), e->border() == border);
|
|
|
|
}
|
|
|
|
// and unreserve by destroying
|
|
|
|
action2.reset();
|
|
|
|
for (auto e : edges) {
|
|
|
|
QCOMPARE(e->isReserved(), false);
|
|
|
|
QCOMPARE(e->activatesForPointer(), false);
|
|
|
|
QCOMPARE(e->activatesForTouchGesture(), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:08:45 +00:00
|
|
|
Q_CONSTRUCTOR_FUNCTION(forceXcb)
|
2014-09-26 12:01:44 +00:00
|
|
|
QTEST_MAIN(TestScreenEdges)
|
|
|
|
#include "test_screen_edges.moc"
|