23#include <KConfigGroup>
24#include <KWayland/Client/surface.h>
26#include <QAbstractEventDispatcher>
28#include <QSocketNotifier>
30#include <xcb/xcb_icccm.h>
37static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_screen-edges-0");
39class TestObject :
public QObject
62 void testTouchCallback_data();
63 void testTouchCallback();
64 void testPushBack_data();
66 void testObjectEdge_data();
67 void testObjectEdge();
68 void testKdeNetWmScreenEdgeShow();
71void ScreenEdgesTest::initTestCase()
73 qRegisterMetaType<KWin::Window *>();
74 qRegisterMetaType<KWin::ElectricBorder>(
"ElectricBorder");
81 auto config = kwinApp()->config();
82 KConfigGroup plugins(config, QStringLiteral(
"Plugins"));
83 const auto builtinNames = EffectLoader().listOfKnownEffects();
84 for (
const QString &name : builtinNames) {
85 plugins.writeEntry(name + QStringLiteral(
"Enabled"),
false);
89 kwinApp()->setConfig(config);
92 QVERIFY(applicationStartedSpy.wait());
95void ScreenEdgesTest::init()
104void ScreenEdgesTest::cleanup()
109void ScreenEdgesTest::testTouchCallback_data()
111 QTest::addColumn<KWin::ElectricBorder>(
"border");
112 QTest::addColumn<QPointF>(
"startPos");
113 QTest::addColumn<QPointF>(
"delta");
115 QTest::newRow(
"left") <<
ElectricLeft << QPointF(0, 50) << QPointF(256, 20);
116 QTest::newRow(
"top") <<
ElectricTop << QPointF(50, 0) << QPointF(20, 250);
117 QTest::newRow(
"right") <<
ElectricRight << QPointF(1279, 50) << QPointF(-256, 0);
118 QTest::newRow(
"bottom") <<
ElectricBottom << QPointF(50, 1023) << QPointF(0, -205);
121void ScreenEdgesTest::testTouchCallback()
125 auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
126 auto group = config->group(QStringLiteral(
"TouchEdges"));
127 group.writeEntry(
"Top",
"none");
128 group.writeEntry(
"Left",
"none");
129 group.writeEntry(
"Bottom",
"none");
130 group.writeEntry(
"Right",
"none");
138 const auto &edges = s->edges();
139 QCOMPARE(edges.size(), 8);
140 for (
auto &edge : edges) {
141 QCOMPARE(edge->isReserved(),
false);
142 QCOMPARE(edge->activatesForPointer(),
false);
143 QCOMPARE(edge->activatesForTouchGesture(),
false);
148 QSignalSpy actionTriggeredSpy(&action, &QAction::triggered);
152 s->reserveTouch(border, &action);
153 for (
auto &edge : edges) {
154 QCOMPARE(edge->isReserved(), edge->border() == border);
155 QCOMPARE(edge->activatesForPointer(),
false);
156 QCOMPARE(edge->activatesForTouchGesture(), edge->border() == border);
159 quint32 timestamp = 0;
162 QFETCH(QPointF, startPos);
164 QVERIFY(actionTriggeredSpy.isEmpty());
167 QFETCH(QPointF, delta);
169 QVERIFY(actionTriggeredSpy.isEmpty());
173 QVERIFY(actionTriggeredSpy.wait());
174 QCOMPARE(actionTriggeredSpy.count(), 1);
177 s->unreserveTouch(border, &action);
178 for (
auto &edge : edges) {
179 QCOMPARE(edge->isReserved(),
false);
180 QCOMPARE(edge->activatesForPointer(),
false);
181 QCOMPARE(edge->activatesForTouchGesture(),
false);
185 std::unique_ptr<QAction> action2(
new QAction);
186 s->reserveTouch(border, action2.get());
187 for (
auto &edge : edges) {
188 QCOMPARE(edge->isReserved(), edge->border() == border);
189 QCOMPARE(edge->activatesForPointer(),
false);
190 QCOMPARE(edge->activatesForTouchGesture(), edge->border() == border);
195 for (
auto &edge : edges) {
196 QCOMPARE(edge->isReserved(),
false);
197 QCOMPARE(edge->activatesForPointer(),
false);
198 QCOMPARE(edge->activatesForTouchGesture(),
false);
202void ScreenEdgesTest::testPushBack_data()
204 QTest::addColumn<KWin::ElectricBorder>(
"border");
205 QTest::addColumn<int>(
"pushback");
206 QTest::addColumn<QPointF>(
"trigger");
207 QTest::addColumn<QPointF>(
"expected");
209 QTest::newRow(
"top-left-3") <<
ElectricTopLeft << 3 << QPointF(0, 0) << QPointF(3, 3);
210 QTest::newRow(
"top-5") <<
ElectricTop << 5 << QPointF(50, 0) << QPointF(50, 5);
211 QTest::newRow(
"top-right-2") <<
ElectricTopRight << 2 << QPointF(1279, 0) << QPointF(1277, 2);
212 QTest::newRow(
"right-10") <<
ElectricRight << 10 << QPointF(1279, 50) << QPointF(1269, 50);
213 QTest::newRow(
"bottom-right-5") <<
ElectricBottomRight << 5 << QPointF(1279, 1023) << QPointF(1274, 1018);
214 QTest::newRow(
"bottom-10") <<
ElectricBottom << 10 << QPointF(50, 1023) << QPointF(50, 1013);
215 QTest::newRow(
"bottom-left-3") <<
ElectricBottomLeft << 3 << QPointF(0, 1023) << QPointF(3, 1020);
216 QTest::newRow(
"left-10") <<
ElectricLeft << 10 << QPointF(0, 50) << QPointF(10, 50);
217 QTest::newRow(
"invalid") <<
ElectricLeft << 10 << QPointF(50, 0) << QPointF(50, 0);
220void ScreenEdgesTest::testPushBack()
224 QFETCH(
int, pushback);
225 auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
226 config->group(QStringLiteral(
"Windows")).writeEntry(
"ElectricBorderPushbackPixels", pushback);
237 s->reserve(border, &callback,
"callback");
239 QFETCH(QPointF, trigger);
241 QVERIFY(spy.isEmpty());
245void ScreenEdgesTest::testObjectEdge_data()
247 QTest::addColumn<ElectricBorder>(
"border");
248 QTest::addColumn<QPointF>(
"triggerPoint");
249 QTest::addColumn<QPointF>(
"delta");
251 QTest::newRow(
"top") <<
ElectricTop << QPointF(640, 0) << QPointF(0, 50);
252 QTest::newRow(
"right") <<
ElectricRight << QPointF(1279, 512) << QPointF(-50, 0);
253 QTest::newRow(
"bottom") <<
ElectricBottom << QPointF(640, 1023) << QPointF(0, -50);
254 QTest::newRow(
"left") <<
ElectricLeft << QPointF(0, 512) << QPointF(50, 0);
257void ScreenEdgesTest::testObjectEdge()
268 QFETCH(QPointF, triggerPoint);
269 QFETCH(QPointF, delta);
272 qint64 timestamp = 0;
274 QVERIFY(spy.isEmpty());
279 QVERIFY(spy.isEmpty());
284 QVERIFY(spy.isEmpty());
289 QVERIFY(!spy.isEmpty());
294 QCOMPARE(spy.count(), 1);
299 QCOMPARE(spy.count(), 1);
304 QCOMPARE(spy.count(), 2);
343 , m_notifier(new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this))
345 connect(m_notifier, &QSocketNotifier::activated,
this, &ScreenEdgePropertyMonitor::processXcbEvents);
346 connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock,
this, &ScreenEdgePropertyMonitor::processXcbEvents);
347 connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake,
this, &ScreenEdgePropertyMonitor::processXcbEvents);
354 void processXcbEvents()
356 while (
auto event = xcb_poll_for_event(m_connection)) {
357 const uint8_t eventType =
event->response_type & ~0x80;
359 case XCB_PROPERTY_NOTIFY: {
360 auto propertyNotifyEvent =
reinterpret_cast<xcb_property_notify_event_t *
>(event);
361 if (propertyNotifyEvent->window == m_window && propertyNotifyEvent->atom ==
atoms->
kde_screen_edge_show && propertyNotifyEvent->state == XCB_PROPERTY_DELETE) {
371 xcb_connection_t *m_connection;
372 xcb_window_t m_window;
373 QSocketNotifier *m_notifier;
376void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
381 auto config = kwinApp()->config();
382 config->group(QStringLiteral(
"Windows")).writeEntry(
"ElectricBorderDelay", 75);
387 QVERIFY(!xcb_connection_has_error(c.get()));
390 const QRect windowGeometry(0, 1024 - 30, 1280, 30);
391 const uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
392 xcb_window_t windowId = xcb_generate_id(c.get());
393 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId,
rootWindow(),
396 windowGeometry.width(),
397 windowGeometry.height(),
398 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
399 XCB_COPY_FROM_PARENT,
400 XCB_CW_EVENT_MASK, values);
401 xcb_size_hints_t hints;
402 memset(&hints, 0,
sizeof(hints));
403 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
404 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
405 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
406 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId,
atoms->
wm_client_leader, XCB_ATOM_WINDOW, 32, 1, &windowId);
407 xcb_map_window(c.get(), windowId);
411 QVERIFY(windowCreatedSpy.wait());
412 Window *window = windowCreatedSpy.first().first().value<
Window *>();
415 ScreenEdgePropertyMonitor screenEdgeMonitor(c.get(), windowId);
419 quint32 timestamp = 0;
425 QVERIFY(windowHiddenSpy.wait());
426 QVERIFY(!window->isShown());
431 QVERIFY(withdrawnSpy.wait());
432 QVERIFY(window->isShown());
435 QVERIFY(window->isShown());
442 QVERIFY(windowHiddenSpy.wait());
443 QVERIFY(!window->isShown());
448 QVERIFY(withdrawnSpy.wait());
449 QVERIFY(window->isShown());
456 QVERIFY(windowHiddenSpy.wait());
457 QVERIFY(!window->isShown());
460 QVERIFY(!withdrawnSpy.wait(50));
461 QVERIFY(!window->isShown());
465 QVERIFY(windowShownSpy.wait());
466 QVERIFY(window->isShown());
473 QVERIFY(windowHiddenSpy.wait());
474 QVERIFY(!window->isShown());
478 QVERIFY(windowShownSpy.wait());
479 QVERIFY(window->isShown());
487 QVERIFY(windowHiddenSpy.wait());
488 QVERIFY(!window->isShown());
491 QVERIFY(approachingSpy.last().at(1).toReal() == 0.0);
493 QVERIFY(approachingSpy.last().at(1).toReal() != 0.0);
497 QVERIFY(windowShownSpy.wait());
498 QVERIFY(window->isShown());
499 QVERIFY(approachingSpy.last().at(1).toReal() == 0.0);
508#include "screenedges_test.moc"
Xcb::Atom wm_client_leader
Xcb::Atom kde_screen_edge_show
ScreenEdgePropertyMonitor(xcb_connection_t *c, xcb_window_t window)
void approaching(ElectricBorder border, qreal factor, const QRect &geometry)
void reserve(ElectricBorder border, QObject *object, const char *callback)
void setConfig(KSharedConfig::Ptr config)
void gotCallback(KWin::ElectricBorder)
bool callback(ElectricBorder border)
void windowShown(KWin::Window *window)
void windowHidden(KWin::Window *window)
ScreenEdges * screenEdges() const
static Workspace * self()
void windowAdded(KWin::Window *)
void setActiveOutput(Output *output)
#define WAYLANDTEST_MAIN(TestObject)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
void touchDown(qint32 id, const QPointF &pos, quint32 time)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
void touchMotion(qint32 id, const QPointF &pos, quint32 time)
XcbConnectionPtr createX11Connection()
void pointerMotion(const QPointF &position, quint32 time)
void touchUp(qint32 id, quint32 time)
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
KWIN_EXPORT xcb_window_t rootWindow()
WaylandServer * waylandServer()
KWIN_EXPORT xcb_connection_t * connection()
InputRedirection * input()
KWIN_EXPORT Atoms * atoms