31018c000b
Clients can have valid reasons to change the selection when the same user action that also caused the selection request to lose keyboard focus. This is notbaly the case for menus created from a Plasma panel which itself will not take focus but when clicking on action it only triggers after the menu is closed. This also matches what weston and sway do. BUG:490803
1956 lines
81 KiB
C++
1956 lines
81 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
*/
|
|
// Qt
|
|
#include <QMimeType>
|
|
#include <QSignalSpy>
|
|
#include <QTest>
|
|
// KWin
|
|
#include "wayland/compositor.h"
|
|
#include "wayland/datadevicemanager.h"
|
|
#include "wayland/datasource.h"
|
|
#include "wayland/display.h"
|
|
#include "wayland/keyboard.h"
|
|
#include "wayland/pointer.h"
|
|
#include "wayland/pointergestures_v1.h"
|
|
#include "wayland/relativepointer_v1.h"
|
|
#include "wayland/seat.h"
|
|
#include "wayland/subcompositor.h"
|
|
#include "wayland/surface.h"
|
|
|
|
#include "KWayland/Client/compositor.h"
|
|
#include "KWayland/Client/connection_thread.h"
|
|
#include "KWayland/Client/datadevice.h"
|
|
#include "KWayland/Client/datadevicemanager.h"
|
|
#include "KWayland/Client/datasource.h"
|
|
#include "KWayland/Client/event_queue.h"
|
|
#include "KWayland/Client/keyboard.h"
|
|
#include "KWayland/Client/pointer.h"
|
|
#include "KWayland/Client/pointergestures.h"
|
|
#include "KWayland/Client/registry.h"
|
|
#include "KWayland/Client/relativepointer.h"
|
|
#include "KWayland/Client/seat.h"
|
|
#include "KWayland/Client/shm_pool.h"
|
|
#include "KWayland/Client/subcompositor.h"
|
|
#include "KWayland/Client/subsurface.h"
|
|
#include "KWayland/Client/surface.h"
|
|
#include "KWayland/Client/touch.h"
|
|
|
|
// Wayland
|
|
#include "qwayland-pointer-gestures-unstable-v1.h"
|
|
#include <wayland-client-protocol.h>
|
|
|
|
#include <linux/input.h>
|
|
// System
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
using namespace std::literals;
|
|
|
|
class WaylandSyncPoint : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit WaylandSyncPoint(KWayland::Client::ConnectionThread *connection, KWayland::Client::EventQueue *eventQueue)
|
|
{
|
|
static const wl_callback_listener listener = {
|
|
.done = [](void *data, wl_callback *callback, uint32_t callback_data) {
|
|
auto syncPoint = static_cast<WaylandSyncPoint *>(data);
|
|
Q_EMIT syncPoint->done();
|
|
},
|
|
};
|
|
|
|
m_callback = wl_display_sync(connection->display());
|
|
eventQueue->addProxy(m_callback);
|
|
wl_callback_add_listener(m_callback, &listener, this);
|
|
}
|
|
|
|
~WaylandSyncPoint() override
|
|
{
|
|
wl_callback_destroy(m_callback);
|
|
}
|
|
|
|
Q_SIGNALS:
|
|
void done();
|
|
|
|
private:
|
|
wl_callback *m_callback;
|
|
};
|
|
|
|
class TestWaylandSeat : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit TestWaylandSeat(QObject *parent = nullptr);
|
|
private Q_SLOTS:
|
|
void init();
|
|
void cleanup();
|
|
|
|
void testName();
|
|
void testCapabilities_data();
|
|
void testCapabilities();
|
|
void testPointer();
|
|
void testPointerTransformation_data();
|
|
void testPointerTransformation();
|
|
void testPointerButton_data();
|
|
void testPointerButton();
|
|
void testPointerSubSurfaceTree();
|
|
void testPointerSwipeGesture_data();
|
|
void testPointerSwipeGesture();
|
|
void testPointerPinchGesture_data();
|
|
void testPointerPinchGesture();
|
|
void testPointerHoldGesture_data();
|
|
void testPointerHoldGesture();
|
|
void testPointerAxis();
|
|
void testCursor();
|
|
void testKeyboard();
|
|
void testSelection();
|
|
void testDataDeviceForKeyboardSurface();
|
|
void testTouch();
|
|
void testKeymap();
|
|
|
|
private:
|
|
bool sync();
|
|
|
|
KWin::Display *m_display;
|
|
KWin::CompositorInterface *m_compositorInterface;
|
|
KWin::SeatInterface *m_seatInterface;
|
|
KWin::SubCompositorInterface *m_subCompositorInterface;
|
|
KWin::RelativePointerManagerV1Interface *m_relativePointerManagerV1Interface;
|
|
KWin::PointerGesturesV1Interface *m_pointerGesturesV1Interface;
|
|
KWayland::Client::ConnectionThread *m_connection;
|
|
KWayland::Client::Compositor *m_compositor;
|
|
KWayland::Client::Seat *m_seat;
|
|
KWayland::Client::ShmPool *m_shm;
|
|
KWayland::Client::SubCompositor *m_subCompositor;
|
|
KWayland::Client::RelativePointerManager *m_relativePointerManager;
|
|
KWayland::Client::PointerGestures *m_pointerGestures;
|
|
KWayland::Client::EventQueue *m_queue;
|
|
QThread *m_thread;
|
|
};
|
|
|
|
static const QString s_socketName = QStringLiteral("kwin-test-wayland-seat-0");
|
|
|
|
TestWaylandSeat::TestWaylandSeat(QObject *parent)
|
|
: QObject(parent)
|
|
, m_display(nullptr)
|
|
, m_compositorInterface(nullptr)
|
|
, m_seatInterface(nullptr)
|
|
, m_subCompositorInterface(nullptr)
|
|
, m_relativePointerManagerV1Interface(nullptr)
|
|
, m_pointerGesturesV1Interface(nullptr)
|
|
, m_connection(nullptr)
|
|
, m_compositor(nullptr)
|
|
, m_seat(nullptr)
|
|
, m_shm(nullptr)
|
|
, m_subCompositor(nullptr)
|
|
, m_relativePointerManager(nullptr)
|
|
, m_pointerGestures(nullptr)
|
|
, m_queue(nullptr)
|
|
, m_thread(nullptr)
|
|
{
|
|
}
|
|
|
|
void TestWaylandSeat::init()
|
|
{
|
|
using namespace KWin;
|
|
delete m_display;
|
|
m_display = new KWin::Display(this);
|
|
m_display->addSocketName(s_socketName);
|
|
m_display->start();
|
|
QVERIFY(m_display->isRunning());
|
|
m_display->createShm();
|
|
|
|
m_compositorInterface = new CompositorInterface(m_display, m_display);
|
|
m_subCompositorInterface = new SubCompositorInterface(m_display, m_display);
|
|
QVERIFY(m_subCompositorInterface);
|
|
|
|
m_relativePointerManagerV1Interface = new RelativePointerManagerV1Interface(m_display, m_display);
|
|
m_pointerGesturesV1Interface = new PointerGesturesV1Interface(m_display, m_display);
|
|
|
|
// setup connection
|
|
m_connection = new KWayland::Client::ConnectionThread;
|
|
QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
|
|
m_connection->setSocketName(s_socketName);
|
|
|
|
m_thread = new QThread(this);
|
|
m_connection->moveToThread(m_thread);
|
|
m_thread->start();
|
|
|
|
m_connection->initConnection();
|
|
QVERIFY(connectedSpy.wait());
|
|
|
|
m_queue = new KWayland::Client::EventQueue(this);
|
|
m_queue->setup(m_connection);
|
|
|
|
KWayland::Client::Registry registry;
|
|
QSignalSpy compositorSpy(®istry, &KWayland::Client::Registry::compositorAnnounced);
|
|
QSignalSpy seatSpy(®istry, &KWayland::Client::Registry::seatAnnounced);
|
|
QSignalSpy shmSpy(®istry, &KWayland::Client::Registry::shmAnnounced);
|
|
registry.setEventQueue(m_queue);
|
|
registry.create(m_connection->display());
|
|
QVERIFY(registry.isValid());
|
|
registry.setup();
|
|
QVERIFY(compositorSpy.wait());
|
|
|
|
m_seatInterface = new SeatInterface(m_display, m_display);
|
|
QVERIFY(m_seatInterface);
|
|
m_seatInterface->setName(QStringLiteral("seat0"));
|
|
QVERIFY(seatSpy.wait());
|
|
|
|
m_compositor = new KWayland::Client::Compositor(this);
|
|
m_compositor->setup(registry.bindCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>()));
|
|
QVERIFY(m_compositor->isValid());
|
|
|
|
m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this);
|
|
QSignalSpy nameSpy(m_seat, &KWayland::Client::Seat::nameChanged);
|
|
QVERIFY(nameSpy.wait());
|
|
|
|
m_shm = new KWayland::Client::ShmPool(this);
|
|
m_shm->setup(registry.bindShm(shmSpy.first().first().value<quint32>(), shmSpy.first().last().value<quint32>()));
|
|
QVERIFY(m_shm->isValid());
|
|
|
|
m_subCompositor = registry.createSubCompositor(registry.interface(KWayland::Client::Registry::Interface::SubCompositor).name,
|
|
registry.interface(KWayland::Client::Registry::Interface::SubCompositor).version,
|
|
this);
|
|
QVERIFY(m_subCompositor->isValid());
|
|
|
|
m_relativePointerManager =
|
|
registry.createRelativePointerManager(registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).name,
|
|
registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).version,
|
|
this);
|
|
QVERIFY(m_relativePointerManager->isValid());
|
|
|
|
m_pointerGestures = registry.createPointerGestures(registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).name,
|
|
registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).version,
|
|
this);
|
|
QVERIFY(m_pointerGestures->isValid());
|
|
}
|
|
|
|
void TestWaylandSeat::cleanup()
|
|
{
|
|
if (m_pointerGestures) {
|
|
delete m_pointerGestures;
|
|
m_pointerGestures = nullptr;
|
|
}
|
|
if (m_relativePointerManager) {
|
|
delete m_relativePointerManager;
|
|
m_relativePointerManager = nullptr;
|
|
}
|
|
if (m_subCompositor) {
|
|
delete m_subCompositor;
|
|
m_subCompositor = nullptr;
|
|
}
|
|
if (m_shm) {
|
|
delete m_shm;
|
|
m_shm = nullptr;
|
|
}
|
|
if (m_seat) {
|
|
delete m_seat;
|
|
m_seat = nullptr;
|
|
}
|
|
if (m_compositor) {
|
|
delete m_compositor;
|
|
m_compositor = nullptr;
|
|
}
|
|
if (m_queue) {
|
|
delete m_queue;
|
|
m_queue = nullptr;
|
|
}
|
|
if (m_connection) {
|
|
m_connection->deleteLater();
|
|
m_connection = nullptr;
|
|
}
|
|
if (m_thread) {
|
|
m_thread->quit();
|
|
m_thread->wait();
|
|
delete m_thread;
|
|
m_thread = nullptr;
|
|
}
|
|
|
|
delete m_display;
|
|
m_display = nullptr;
|
|
|
|
// these are the children of the display
|
|
m_compositorInterface = nullptr;
|
|
m_seatInterface = nullptr;
|
|
m_subCompositorInterface = nullptr;
|
|
m_relativePointerManagerV1Interface = nullptr;
|
|
m_pointerGesturesV1Interface = nullptr;
|
|
}
|
|
|
|
bool TestWaylandSeat::sync()
|
|
{
|
|
WaylandSyncPoint syncPoint(m_connection, m_queue);
|
|
QSignalSpy doneSpy(&syncPoint, &WaylandSyncPoint::done);
|
|
return doneSpy.wait();
|
|
}
|
|
|
|
void TestWaylandSeat::testName()
|
|
{
|
|
// no name set yet
|
|
QCOMPARE(m_seat->name(), QStringLiteral("seat0"));
|
|
|
|
QSignalSpy spy(m_seat, &KWayland::Client::Seat::nameChanged);
|
|
|
|
const QString name = QStringLiteral("foobar");
|
|
m_seatInterface->setName(name);
|
|
QVERIFY(spy.wait());
|
|
QCOMPARE(m_seat->name(), name);
|
|
QCOMPARE(spy.count(), 1);
|
|
QCOMPARE(spy.first().first().toString(), name);
|
|
}
|
|
|
|
void TestWaylandSeat::testCapabilities_data()
|
|
{
|
|
QTest::addColumn<bool>("pointer");
|
|
QTest::addColumn<bool>("keyboard");
|
|
QTest::addColumn<bool>("touch");
|
|
|
|
QTest::newRow("none") << false << false << false;
|
|
QTest::newRow("pointer") << true << false << false;
|
|
QTest::newRow("keyboard") << false << true << false;
|
|
QTest::newRow("touch") << false << false << true;
|
|
QTest::newRow("pointer/keyboard") << true << true << false;
|
|
QTest::newRow("pointer/touch") << true << false << true;
|
|
QTest::newRow("keyboard/touch") << false << true << true;
|
|
QTest::newRow("all") << true << true << true;
|
|
}
|
|
|
|
void TestWaylandSeat::testCapabilities()
|
|
{
|
|
QVERIFY(!m_seat->hasPointer());
|
|
QVERIFY(!m_seat->hasKeyboard());
|
|
QVERIFY(!m_seat->hasTouch());
|
|
|
|
QFETCH(bool, pointer);
|
|
QFETCH(bool, keyboard);
|
|
QFETCH(bool, touch);
|
|
|
|
QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
QSignalSpy keyboardSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
|
|
QSignalSpy touchSpy(m_seat, &KWayland::Client::Seat::hasTouchChanged);
|
|
|
|
m_seatInterface->setHasPointer(pointer);
|
|
m_seatInterface->setHasKeyboard(keyboard);
|
|
m_seatInterface->setHasTouch(touch);
|
|
|
|
QVERIFY(sync());
|
|
|
|
// do processing
|
|
QCOMPARE(pointerSpy.isEmpty(), !pointer);
|
|
if (!pointerSpy.isEmpty()) {
|
|
QCOMPARE(pointerSpy.first().first().toBool(), pointer);
|
|
}
|
|
|
|
QCOMPARE(keyboardSpy.isEmpty(), !keyboard);
|
|
if (!keyboardSpy.isEmpty()) {
|
|
QCOMPARE(keyboardSpy.first().first().toBool(), keyboard);
|
|
}
|
|
|
|
QCOMPARE(touchSpy.isEmpty(), !touch);
|
|
if (!touchSpy.isEmpty()) {
|
|
QCOMPARE(touchSpy.first().first().toBool(), touch);
|
|
}
|
|
|
|
QCOMPARE(m_seat->hasPointer(), pointer);
|
|
QCOMPARE(m_seat->hasKeyboard(), keyboard);
|
|
QCOMPARE(m_seat->hasTouch(), touch);
|
|
}
|
|
|
|
void TestWaylandSeat::testPointer()
|
|
{
|
|
using namespace KWin;
|
|
|
|
QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(pointerSpy.wait());
|
|
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
KWayland::Client::Surface *s = m_compositor->createSurface(m_compositor);
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
s->attachBuffer(m_shm->createBuffer(image));
|
|
s->damage(image.rect());
|
|
s->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(20, 18), QPointF(10, 15));
|
|
|
|
KWayland::Client::Pointer *p = m_seat->createPointer(m_seat);
|
|
QSignalSpy frameSpy(p, &KWayland::Client::Pointer::frame);
|
|
const KWayland::Client::Pointer &cp = *p;
|
|
QVERIFY(p->isValid());
|
|
std::unique_ptr<KWayland::Client::RelativePointer> relativePointer(m_relativePointerManager->createRelativePointer(p));
|
|
QVERIFY(relativePointer->isValid());
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 1);
|
|
|
|
m_seatInterface->notifyPointerLeave();
|
|
serverSurface->client()->flush();
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 2);
|
|
|
|
QSignalSpy enteredSpy(p, &KWayland::Client::Pointer::entered);
|
|
|
|
QSignalSpy leftSpy(p, &KWayland::Client::Pointer::left);
|
|
|
|
QSignalSpy motionSpy(p, &KWayland::Client::Pointer::motion);
|
|
|
|
QSignalSpy axisSpy(p, &KWayland::Client::Pointer::axisChanged);
|
|
|
|
QSignalSpy buttonSpy(p, &KWayland::Client::Pointer::buttonStateChanged);
|
|
|
|
QSignalSpy relativeMotionSpy(relativePointer.get(), &KWayland::Client::RelativePointer::relativeMotion);
|
|
|
|
QVERIFY(!p->enteredSurface());
|
|
QVERIFY(!cp.enteredSurface());
|
|
uint32_t serial = m_display->serial();
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(20, 18), QPointF(10, 15));
|
|
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE_GT(enteredSpy.first().first().value<quint32>(), serial);
|
|
QCOMPARE(enteredSpy.first().last().toPoint(), QPoint(10, 3));
|
|
QCOMPARE(frameSpy.count(), 3);
|
|
QCOMPARE(p->enteredSurface(), s);
|
|
QCOMPARE(cp.enteredSurface(), s);
|
|
|
|
auto timestamp = 1ms;
|
|
// test motion
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerMotion(QPoint(10, 16));
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(motionSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 4);
|
|
QCOMPARE(motionSpy.first().first().toPoint(), QPoint(0, 1));
|
|
QCOMPARE(motionSpy.first().last().value<quint32>(), quint32(1));
|
|
|
|
// test relative motion
|
|
m_seatInterface->relativePointerMotion(QPointF(1, 2), QPointF(3, 4), 1234us);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(relativeMotionSpy.wait());
|
|
QCOMPARE(relativeMotionSpy.count(), 1);
|
|
QCOMPARE(frameSpy.count(), 5);
|
|
QCOMPARE(relativeMotionSpy.first().at(0).toSizeF(), QSizeF(1, 2));
|
|
QCOMPARE(relativeMotionSpy.first().at(1).toSizeF(), QSizeF(3, 4));
|
|
QCOMPARE(relativeMotionSpy.first().at(2).value<quint64>(), 1234);
|
|
|
|
// test axis
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerAxis(Qt::Horizontal, 10, 120, PointerAxisSource::Wheel);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(axisSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 6);
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerAxis(Qt::Vertical, 20, 240, PointerAxisSource::Wheel);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(axisSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 7);
|
|
QCOMPARE(axisSpy.first().at(0).value<quint32>(), quint32(2));
|
|
QCOMPARE(axisSpy.first().at(1).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Horizontal);
|
|
QCOMPARE(axisSpy.first().at(2).value<qreal>(), qreal(10));
|
|
|
|
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(3));
|
|
QCOMPARE(axisSpy.last().at(1).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Vertical);
|
|
QCOMPARE(axisSpy.last().at(2).value<qreal>(), qreal(20));
|
|
|
|
// test button
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(buttonSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 8);
|
|
QCOMPARE(buttonSpy.at(0).at(0).value<quint32>(), m_display->serial());
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerButton(2, PointerButtonState::Pressed);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(buttonSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 9);
|
|
QCOMPARE(buttonSpy.at(1).at(0).value<quint32>(), m_display->serial());
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerButton(2, PointerButtonState::Released);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(buttonSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 10);
|
|
QCOMPARE(buttonSpy.at(2).at(0).value<quint32>(), m_display->serial());
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(buttonSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 11);
|
|
QCOMPARE(buttonSpy.count(), 4);
|
|
|
|
// timestamp
|
|
QCOMPARE(buttonSpy.at(0).at(1).value<quint32>(), quint32(4));
|
|
// button
|
|
QCOMPARE(buttonSpy.at(0).at(2).value<quint32>(), quint32(1));
|
|
QCOMPARE(buttonSpy.at(0).at(3).value<KWayland::Client::Pointer::ButtonState>(), KWayland::Client::Pointer::ButtonState::Pressed);
|
|
|
|
// timestamp
|
|
QCOMPARE(buttonSpy.at(1).at(1).value<quint32>(), quint32(5));
|
|
// button
|
|
QCOMPARE(buttonSpy.at(1).at(2).value<quint32>(), quint32(2));
|
|
QCOMPARE(buttonSpy.at(1).at(3).value<KWayland::Client::Pointer::ButtonState>(), KWayland::Client::Pointer::ButtonState::Pressed);
|
|
|
|
QCOMPARE(buttonSpy.at(2).at(0).value<quint32>(), m_seatInterface->pointerButtonSerial(2));
|
|
// timestamp
|
|
QCOMPARE(buttonSpy.at(2).at(1).value<quint32>(), quint32(6));
|
|
// button
|
|
QCOMPARE(buttonSpy.at(2).at(2).value<quint32>(), quint32(2));
|
|
QCOMPARE(buttonSpy.at(2).at(3).value<KWayland::Client::Pointer::ButtonState>(), KWayland::Client::Pointer::ButtonState::Released);
|
|
|
|
QCOMPARE(buttonSpy.at(3).at(0).value<quint32>(), m_seatInterface->pointerButtonSerial(1));
|
|
// timestamp
|
|
QCOMPARE(buttonSpy.at(3).at(1).value<quint32>(), quint32(7));
|
|
// button
|
|
QCOMPARE(buttonSpy.at(3).at(2).value<quint32>(), quint32(1));
|
|
QCOMPARE(buttonSpy.at(3).at(3).value<KWayland::Client::Pointer::ButtonState>(), KWayland::Client::Pointer::ButtonState::Released);
|
|
|
|
// leave the surface
|
|
serial = m_display->serial();
|
|
m_seatInterface->notifyPointerLeave();
|
|
QVERIFY(leftSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 12);
|
|
QCOMPARE_GT(leftSpy.first().first().value<quint32>(), serial);
|
|
QVERIFY(!p->enteredSurface());
|
|
QVERIFY(!cp.enteredSurface());
|
|
|
|
// now a relative motion should not be sent to the relative pointer
|
|
m_seatInterface->relativePointerMotion(QPointF(1, 2), QPointF(3, 4), std::chrono::milliseconds::zero());
|
|
QVERIFY(sync());
|
|
QCOMPARE(relativeMotionSpy.count(), 1);
|
|
|
|
// enter it again
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(10, 16), QPointF(0, 0));
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 13);
|
|
QCOMPARE(p->enteredSurface(), s);
|
|
QCOMPARE(cp.enteredSurface(), s);
|
|
|
|
// send another relative motion event
|
|
m_seatInterface->relativePointerMotion(QPointF(4, 5), QPointF(6, 7), 1234us);
|
|
QVERIFY(relativeMotionSpy.wait());
|
|
QCOMPARE(relativeMotionSpy.count(), 2);
|
|
QCOMPARE(relativeMotionSpy.last().at(0).toSizeF(), QSizeF(4, 5));
|
|
QCOMPARE(relativeMotionSpy.last().at(1).toSizeF(), QSizeF(6, 7));
|
|
QCOMPARE(relativeMotionSpy.last().at(2).value<quint64>(), 1234);
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerTransformation_data()
|
|
{
|
|
QTest::addColumn<QMatrix4x4>("enterTransformation");
|
|
// global position at 20/18
|
|
QTest::addColumn<QPointF>("expectedEnterPoint");
|
|
// global position at 10/16
|
|
QTest::addColumn<QPointF>("expectedMovePoint");
|
|
|
|
QMatrix4x4 tm;
|
|
tm.translate(-10, -15);
|
|
QTest::newRow("translation") << tm << QPointF(10, 3) << QPointF(0, 1);
|
|
QMatrix4x4 sm;
|
|
sm.scale(2, 2);
|
|
QTest::newRow("scale") << sm << QPointF(40, 36) << QPointF(20, 32);
|
|
QMatrix4x4 rotate;
|
|
rotate.rotate(90, 0, 0, 1);
|
|
QTest::newRow("rotate") << rotate << QPointF(-18, 20) << QPointF(-16, 10);
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerTransformation()
|
|
{
|
|
using namespace KWin;
|
|
|
|
QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(pointerSpy.wait());
|
|
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
KWayland::Client::Surface *s = m_compositor->createSurface(m_compositor);
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
s->attachBuffer(m_shm->createBuffer(image));
|
|
s->damage(image.rect());
|
|
s->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
KWayland::Client::Pointer *p = m_seat->createPointer(m_seat);
|
|
QVERIFY(p->isValid());
|
|
const KWayland::Client::Pointer &cp = *p;
|
|
|
|
QSignalSpy enteredSpy(p, &KWayland::Client::Pointer::entered);
|
|
QSignalSpy leftSpy(p, &KWayland::Client::Pointer::left);
|
|
QSignalSpy motionSpy(p, &KWayland::Client::Pointer::motion);
|
|
|
|
QVERIFY(!p->enteredSurface());
|
|
QVERIFY(!cp.enteredSurface());
|
|
uint32_t serial = m_display->serial();
|
|
QFETCH(QMatrix4x4, enterTransformation);
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(20, 18), enterTransformation);
|
|
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE_GT(enteredSpy.first().first().value<quint32>(), serial);
|
|
QTEST(enteredSpy.first().last().toPointF(), "expectedEnterPoint");
|
|
QCOMPARE(p->enteredSurface(), s);
|
|
QCOMPARE(cp.enteredSurface(), s);
|
|
|
|
// test motion
|
|
m_seatInterface->setTimestamp(std::chrono::milliseconds(1));
|
|
m_seatInterface->notifyPointerMotion(QPoint(10, 16));
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(motionSpy.wait());
|
|
QTEST(motionSpy.first().first().toPointF(), "expectedMovePoint");
|
|
QCOMPARE(motionSpy.first().last().value<quint32>(), quint32(1));
|
|
|
|
// leave the surface
|
|
serial = m_display->serial();
|
|
m_seatInterface->notifyPointerLeave();
|
|
QVERIFY(leftSpy.wait());
|
|
QCOMPARE_GT(leftSpy.first().first().value<quint32>(), serial);
|
|
QVERIFY(!p->enteredSurface());
|
|
QVERIFY(!cp.enteredSurface());
|
|
|
|
// enter it again
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(10, 16));
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE(p->enteredSurface(), s);
|
|
QCOMPARE(cp.enteredSurface(), s);
|
|
|
|
QSignalSpy serverSurfaceDestroyedSpy(serverSurface, &QObject::destroyed);
|
|
delete s;
|
|
QVERIFY(serverSurfaceDestroyedSpy.wait());
|
|
QVERIFY(!m_seatInterface->focusedPointerSurface());
|
|
}
|
|
|
|
Q_DECLARE_METATYPE(Qt::MouseButton)
|
|
|
|
void TestWaylandSeat::testPointerButton_data()
|
|
{
|
|
QTest::addColumn<Qt::MouseButton>("qtButton");
|
|
QTest::addColumn<quint32>("waylandButton");
|
|
|
|
QTest::newRow("left") << Qt::LeftButton << quint32(BTN_LEFT);
|
|
QTest::newRow("right") << Qt::RightButton << quint32(BTN_RIGHT);
|
|
QTest::newRow("middle") << Qt::MiddleButton << quint32(BTN_MIDDLE);
|
|
QTest::newRow("back") << Qt::BackButton << quint32(BTN_BACK);
|
|
QTest::newRow("x1") << Qt::XButton1 << quint32(BTN_BACK);
|
|
QTest::newRow("extra1") << Qt::ExtraButton1 << quint32(BTN_BACK);
|
|
QTest::newRow("forward") << Qt::ForwardButton << quint32(BTN_FORWARD);
|
|
QTest::newRow("x2") << Qt::XButton2 << quint32(BTN_FORWARD);
|
|
QTest::newRow("extra2") << Qt::ExtraButton2 << quint32(BTN_FORWARD);
|
|
QTest::newRow("task") << Qt::TaskButton << quint32(BTN_TASK);
|
|
QTest::newRow("extra3") << Qt::ExtraButton3 << quint32(BTN_TASK);
|
|
QTest::newRow("extra4") << Qt::ExtraButton4 << quint32(BTN_EXTRA);
|
|
QTest::newRow("extra5") << Qt::ExtraButton5 << quint32(BTN_SIDE);
|
|
QTest::newRow("extra6") << Qt::ExtraButton6 << quint32(0x118);
|
|
QTest::newRow("extra7") << Qt::ExtraButton7 << quint32(0x119);
|
|
QTest::newRow("extra8") << Qt::ExtraButton8 << quint32(0x11a);
|
|
QTest::newRow("extra9") << Qt::ExtraButton9 << quint32(0x11b);
|
|
QTest::newRow("extra10") << Qt::ExtraButton10 << quint32(0x11c);
|
|
QTest::newRow("extra11") << Qt::ExtraButton11 << quint32(0x11d);
|
|
QTest::newRow("extra12") << Qt::ExtraButton12 << quint32(0x11e);
|
|
QTest::newRow("extra13") << Qt::ExtraButton13 << quint32(0x11f);
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerButton()
|
|
{
|
|
using namespace KWin;
|
|
|
|
QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(pointerSpy.wait());
|
|
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
KWayland::Client::Surface *s = m_compositor->createSurface(m_compositor);
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
s->attachBuffer(m_shm->createBuffer(image));
|
|
s->damage(image.rect());
|
|
s->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
std::unique_ptr<KWayland::Client::Pointer> p(m_seat->createPointer());
|
|
QVERIFY(p->isValid());
|
|
QSignalSpy buttonChangedSpy(p.get(), &KWayland::Client::Pointer::buttonStateChanged);
|
|
wl_display_flush(m_connection->display());
|
|
QCoreApplication::processEvents();
|
|
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(20, 18), QPointF(10, 15));
|
|
QVERIFY(m_seatInterface->focusedPointerSurface());
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QFETCH(Qt::MouseButton, qtButton);
|
|
QFETCH(quint32, waylandButton);
|
|
std::chrono::milliseconds timestamp(1);
|
|
QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), false);
|
|
QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), false);
|
|
m_seatInterface->setTimestamp(timestamp);
|
|
m_seatInterface->notifyPointerButton(qtButton, PointerButtonState::Pressed);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), true);
|
|
QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), true);
|
|
QVERIFY(buttonChangedSpy.wait());
|
|
QCOMPARE(buttonChangedSpy.count(), 1);
|
|
QCOMPARE(buttonChangedSpy.last().at(0).value<quint32>(), m_seatInterface->pointerButtonSerial(waylandButton));
|
|
QCOMPARE(buttonChangedSpy.last().at(0).value<quint32>(), m_seatInterface->pointerButtonSerial(qtButton));
|
|
QCOMPARE(buttonChangedSpy.last().at(1).value<quint32>(), timestamp.count());
|
|
QCOMPARE(buttonChangedSpy.last().at(2).value<quint32>(), waylandButton);
|
|
QCOMPARE(buttonChangedSpy.last().at(3).value<KWayland::Client::Pointer::ButtonState>(), KWayland::Client::Pointer::ButtonState::Pressed);
|
|
timestamp++;
|
|
m_seatInterface->setTimestamp(timestamp);
|
|
m_seatInterface->notifyPointerButton(qtButton, PointerButtonState::Released);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), false);
|
|
QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), false);
|
|
QVERIFY(buttonChangedSpy.wait());
|
|
QCOMPARE(buttonChangedSpy.count(), 2);
|
|
QCOMPARE(buttonChangedSpy.last().at(0).value<quint32>(), m_seatInterface->pointerButtonSerial(waylandButton));
|
|
QCOMPARE(buttonChangedSpy.last().at(0).value<quint32>(), m_seatInterface->pointerButtonSerial(qtButton));
|
|
QCOMPARE(buttonChangedSpy.last().at(1).value<quint32>(), timestamp.count());
|
|
QCOMPARE(buttonChangedSpy.last().at(2).value<quint32>(), waylandButton);
|
|
QCOMPARE(buttonChangedSpy.last().at(3).value<KWayland::Client::Pointer::ButtonState>(), KWayland::Client::Pointer::ButtonState::Released);
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerSubSurfaceTree()
|
|
{
|
|
// this test verifies that pointer motion on a surface with sub-surfaces sends motion enter/leave to the sub-surface
|
|
using namespace KWin;
|
|
|
|
// first create the pointer
|
|
QSignalSpy hasPointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(hasPointerChangedSpy.wait());
|
|
std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
|
|
|
|
// create a sub surface tree
|
|
// parent surface (100, 100) with one sub surface taking the half of it's size (50, 100)
|
|
// which has two further children (50, 50) which are overlapping
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> parentSurface(m_compositor->createSurface());
|
|
std::unique_ptr<KWayland::Client::Surface> childSurface(m_compositor->createSurface());
|
|
std::unique_ptr<KWayland::Client::Surface> grandChild1Surface(m_compositor->createSurface());
|
|
std::unique_ptr<KWayland::Client::Surface> grandChild2Surface(m_compositor->createSurface());
|
|
std::unique_ptr<KWayland::Client::SubSurface> childSubSurface(m_subCompositor->createSubSurface(childSurface.get(), parentSurface.get()));
|
|
std::unique_ptr<KWayland::Client::SubSurface> grandChild1SubSurface(m_subCompositor->createSubSurface(grandChild1Surface.get(), childSurface.get()));
|
|
std::unique_ptr<KWayland::Client::SubSurface> grandChild2SubSurface(m_subCompositor->createSubSurface(grandChild2Surface.get(), childSurface.get()));
|
|
grandChild2SubSurface->setPosition(QPoint(0, 25));
|
|
|
|
// let's map the surfaces
|
|
auto render = [this](KWayland::Client::Surface *s, const QSize &size) {
|
|
QImage image(size, QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
s->attachBuffer(m_shm->createBuffer(image));
|
|
s->damage(QRect(QPoint(0, 0), size));
|
|
s->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
};
|
|
render(grandChild2Surface.get(), QSize(50, 50));
|
|
render(grandChild1Surface.get(), QSize(50, 50));
|
|
render(childSurface.get(), QSize(50, 100));
|
|
render(parentSurface.get(), QSize(100, 100));
|
|
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(serverSurface->isMapped());
|
|
|
|
// send in pointer events
|
|
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
|
|
QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
|
|
QSignalSpy motionSpy(pointer.get(), &KWayland::Client::Pointer::motion);
|
|
// first to the grandChild2 in the overlapped area
|
|
std::chrono::milliseconds timestamp(1);
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(25, 50));
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE(enteredSpy.count(), 1);
|
|
QCOMPARE(leftSpy.count(), 0);
|
|
QCOMPARE(motionSpy.count(), 0);
|
|
QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(25, 25));
|
|
QCOMPARE(pointer->enteredSurface(), grandChild2Surface.get());
|
|
// a motion on grandchild2
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerMotion(QPointF(25, 60));
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(motionSpy.wait());
|
|
QCOMPARE(enteredSpy.count(), 1);
|
|
QCOMPARE(leftSpy.count(), 0);
|
|
QCOMPARE(motionSpy.count(), 1);
|
|
QCOMPARE(motionSpy.last().first().toPointF(), QPointF(25, 35));
|
|
// motion which changes to childSurface
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerMotion(QPointF(25, 80));
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE(enteredSpy.count(), 2);
|
|
QCOMPARE(leftSpy.count(), 1);
|
|
QCOMPARE(motionSpy.count(), 2);
|
|
QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(25, 80));
|
|
QCOMPARE(pointer->enteredSurface(), childSurface.get());
|
|
// a leave for the whole surface
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerLeave();
|
|
QVERIFY(leftSpy.wait());
|
|
QCOMPARE(enteredSpy.count(), 2);
|
|
QCOMPARE(leftSpy.count(), 2);
|
|
QCOMPARE(motionSpy.count(), 2);
|
|
// a new enter on the main surface
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(75, 50));
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE(enteredSpy.count(), 3);
|
|
QCOMPARE(leftSpy.count(), 2);
|
|
QCOMPARE(motionSpy.count(), 2);
|
|
QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(75, 50));
|
|
QCOMPARE(pointer->enteredSurface(), parentSurface.get());
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerSwipeGesture_data()
|
|
{
|
|
QTest::addColumn<bool>("cancel");
|
|
QTest::addColumn<int>("expectedEndCount");
|
|
QTest::addColumn<int>("expectedCancelCount");
|
|
|
|
QTest::newRow("end") << false << 1 << 0;
|
|
QTest::newRow("cancel") << true << 0 << 1;
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerSwipeGesture()
|
|
{
|
|
using namespace KWin;
|
|
|
|
// first create the pointer and pointer swipe gesture
|
|
QSignalSpy hasPointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(hasPointerChangedSpy.wait());
|
|
std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
|
|
std::unique_ptr<KWayland::Client::PointerSwipeGesture> gesture(m_pointerGestures->createSwipeGesture(pointer.get()));
|
|
QVERIFY(gesture);
|
|
QVERIFY(gesture->isValid());
|
|
QVERIFY(gesture->surface().isNull());
|
|
QCOMPARE(gesture->fingerCount(), 0u);
|
|
|
|
QSignalSpy startSpy(gesture.get(), &KWayland::Client::PointerSwipeGesture::started);
|
|
QSignalSpy updateSpy(gesture.get(), &KWayland::Client::PointerSwipeGesture::updated);
|
|
QSignalSpy endSpy(gesture.get(), &KWayland::Client::PointerSwipeGesture::ended);
|
|
QSignalSpy cancelledSpy(gesture.get(), &KWayland::Client::PointerSwipeGesture::cancelled);
|
|
|
|
// now create a surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
surface->attachBuffer(m_shm->createBuffer(image));
|
|
surface->damage(image.rect());
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
|
|
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
|
|
QVERIFY(m_seatInterface->pointer());
|
|
|
|
// send in the start
|
|
std::chrono::milliseconds timestamp(1);
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->startPointerSwipeGesture(2);
|
|
QVERIFY(startSpy.wait());
|
|
QCOMPARE(startSpy.count(), 1);
|
|
QCOMPARE(startSpy.first().at(0).value<quint32>(), m_display->serial());
|
|
QCOMPARE(startSpy.first().at(1).value<quint32>(), 1u);
|
|
QCOMPARE(gesture->fingerCount(), 2u);
|
|
QCOMPARE(gesture->surface().data(), surface.get());
|
|
|
|
// another start should not be possible
|
|
m_seatInterface->startPointerSwipeGesture(2);
|
|
QVERIFY(sync());
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
// send in some updates
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->updatePointerSwipeGesture(QPointF(2, 3));
|
|
QVERIFY(updateSpy.wait());
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->updatePointerSwipeGesture(QPointF(4, 5));
|
|
QVERIFY(updateSpy.wait());
|
|
QCOMPARE(updateSpy.count(), 2);
|
|
QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3));
|
|
QCOMPARE(updateSpy.at(0).at(1).value<quint32>(), 2u);
|
|
QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5));
|
|
QCOMPARE(updateSpy.at(1).at(1).value<quint32>(), 3u);
|
|
|
|
// now end or cancel
|
|
QFETCH(bool, cancel);
|
|
QSignalSpy *spy;
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
if (cancel) {
|
|
m_seatInterface->cancelPointerSwipeGesture();
|
|
spy = &cancelledSpy;
|
|
} else {
|
|
m_seatInterface->endPointerSwipeGesture();
|
|
spy = &endSpy;
|
|
}
|
|
QVERIFY(spy->wait());
|
|
QFETCH(int, expectedEndCount);
|
|
QCOMPARE(endSpy.count(), expectedEndCount);
|
|
QFETCH(int, expectedCancelCount);
|
|
QCOMPARE(cancelledSpy.count(), expectedCancelCount);
|
|
QCOMPARE(spy->count(), 1);
|
|
QCOMPARE(spy->first().at(0).value<quint32>(), m_display->serial());
|
|
QCOMPARE(spy->first().at(1).value<quint32>(), 4u);
|
|
|
|
QCOMPARE(gesture->fingerCount(), 0u);
|
|
QVERIFY(gesture->surface().isNull());
|
|
|
|
// now a start should be possible again
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->startPointerSwipeGesture(2);
|
|
QVERIFY(startSpy.wait());
|
|
|
|
// unsetting the focused pointer surface should not change anything
|
|
m_seatInterface->notifyPointerLeave();
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->updatePointerSwipeGesture(QPointF(6, 7));
|
|
QVERIFY(updateSpy.wait());
|
|
// and end
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
if (cancel) {
|
|
m_seatInterface->cancelPointerSwipeGesture();
|
|
} else {
|
|
m_seatInterface->endPointerSwipeGesture();
|
|
}
|
|
QVERIFY(spy->wait());
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerPinchGesture_data()
|
|
{
|
|
QTest::addColumn<bool>("cancel");
|
|
QTest::addColumn<int>("expectedEndCount");
|
|
QTest::addColumn<int>("expectedCancelCount");
|
|
|
|
QTest::newRow("end") << false << 1 << 0;
|
|
QTest::newRow("cancel") << true << 0 << 1;
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerPinchGesture()
|
|
{
|
|
using namespace KWin;
|
|
|
|
// first create the pointer and pointer swipe gesture
|
|
QSignalSpy hasPointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(hasPointerChangedSpy.wait());
|
|
std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
|
|
std::unique_ptr<KWayland::Client::PointerPinchGesture> gesture(m_pointerGestures->createPinchGesture(pointer.get()));
|
|
QVERIFY(gesture);
|
|
QVERIFY(gesture->isValid());
|
|
QVERIFY(gesture->surface().isNull());
|
|
QCOMPARE(gesture->fingerCount(), 0u);
|
|
|
|
QSignalSpy startSpy(gesture.get(), &KWayland::Client::PointerPinchGesture::started);
|
|
QSignalSpy updateSpy(gesture.get(), &KWayland::Client::PointerPinchGesture::updated);
|
|
QSignalSpy endSpy(gesture.get(), &KWayland::Client::PointerPinchGesture::ended);
|
|
QSignalSpy cancelledSpy(gesture.get(), &KWayland::Client::PointerPinchGesture::cancelled);
|
|
|
|
// now create a surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
surface->attachBuffer(m_shm->createBuffer(image));
|
|
surface->damage(image.rect());
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
|
|
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
|
|
QVERIFY(m_seatInterface->pointer());
|
|
|
|
// send in the start
|
|
std::chrono::milliseconds timestamp(1);
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->startPointerPinchGesture(3);
|
|
QVERIFY(startSpy.wait());
|
|
QCOMPARE(startSpy.count(), 1);
|
|
QCOMPARE(startSpy.first().at(0).value<quint32>(), m_display->serial());
|
|
QCOMPARE(startSpy.first().at(1).value<quint32>(), 1u);
|
|
QCOMPARE(gesture->fingerCount(), 3u);
|
|
QCOMPARE(gesture->surface().data(), surface.get());
|
|
|
|
// another start should not be possible
|
|
m_seatInterface->startPointerPinchGesture(3);
|
|
QVERIFY(sync());
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
// send in some updates
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->updatePointerPinchGesture(QPointF(2, 3), 2, 45);
|
|
QVERIFY(updateSpy.wait());
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->updatePointerPinchGesture(QPointF(4, 5), 1, 90);
|
|
QVERIFY(updateSpy.wait());
|
|
QCOMPARE(updateSpy.count(), 2);
|
|
QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3));
|
|
QCOMPARE(updateSpy.at(0).at(1).value<quint32>(), 2u);
|
|
QCOMPARE(updateSpy.at(0).at(2).value<quint32>(), 45u);
|
|
QCOMPARE(updateSpy.at(0).at(3).value<quint32>(), 2u);
|
|
QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5));
|
|
QCOMPARE(updateSpy.at(1).at(1).value<quint32>(), 1u);
|
|
QCOMPARE(updateSpy.at(1).at(2).value<quint32>(), 90u);
|
|
QCOMPARE(updateSpy.at(1).at(3).value<quint32>(), 3u);
|
|
|
|
// now end or cancel
|
|
QFETCH(bool, cancel);
|
|
QSignalSpy *spy;
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
if (cancel) {
|
|
m_seatInterface->cancelPointerPinchGesture();
|
|
spy = &cancelledSpy;
|
|
} else {
|
|
m_seatInterface->endPointerPinchGesture();
|
|
spy = &endSpy;
|
|
}
|
|
QVERIFY(spy->wait());
|
|
QFETCH(int, expectedEndCount);
|
|
QCOMPARE(endSpy.count(), expectedEndCount);
|
|
QFETCH(int, expectedCancelCount);
|
|
QCOMPARE(cancelledSpy.count(), expectedCancelCount);
|
|
QCOMPARE(spy->count(), 1);
|
|
QCOMPARE(spy->first().at(0).value<quint32>(), m_display->serial());
|
|
QCOMPARE(spy->first().at(1).value<quint32>(), 4u);
|
|
|
|
QCOMPARE(gesture->fingerCount(), 0u);
|
|
QVERIFY(gesture->surface().isNull());
|
|
|
|
// now a start should be possible again
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->startPointerPinchGesture(3);
|
|
QVERIFY(startSpy.wait());
|
|
|
|
// unsetting the focused pointer surface should not change anything
|
|
m_seatInterface->notifyPointerLeave();
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->updatePointerPinchGesture(QPointF(6, 7), 2, -45);
|
|
QVERIFY(updateSpy.wait());
|
|
// and end
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
if (cancel) {
|
|
m_seatInterface->cancelPointerPinchGesture();
|
|
} else {
|
|
m_seatInterface->endPointerPinchGesture();
|
|
}
|
|
QVERIFY(spy->wait());
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerHoldGesture_data()
|
|
{
|
|
QTest::addColumn<bool>("cancel");
|
|
QTest::addColumn<int>("expectedEndCount");
|
|
QTest::addColumn<int>("expectedCancelCount");
|
|
|
|
QTest::newRow("end") << false << 1 << 0;
|
|
QTest::newRow("cancel") << true << 0 << 1;
|
|
}
|
|
|
|
class PointerHoldGesture : public QObject, public QtWayland::zwp_pointer_gesture_hold_v1
|
|
{
|
|
using zwp_pointer_gesture_hold_v1::zwp_pointer_gesture_hold_v1;
|
|
Q_OBJECT
|
|
void zwp_pointer_gesture_hold_v1_begin(uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers) override
|
|
{
|
|
Q_EMIT started(serial, time, surface, fingers);
|
|
}
|
|
|
|
void zwp_pointer_gesture_hold_v1_end(uint32_t serial, uint32_t time, int32_t cancelled) override
|
|
{
|
|
cancelled ? Q_EMIT this->cancelled(serial, time) : Q_EMIT ended(serial, time);
|
|
}
|
|
Q_SIGNALS:
|
|
void started(quint32 serial, quint32 time, void *surface, quint32 fingers);
|
|
void ended(quint32 serial, quint32 time);
|
|
void cancelled(quint32 serial, quint32 time);
|
|
};
|
|
|
|
void TestWaylandSeat::testPointerHoldGesture()
|
|
{
|
|
using namespace KWin;
|
|
|
|
// first create the pointer and pointer swipe gesture
|
|
QSignalSpy hasPointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(hasPointerChangedSpy.wait());
|
|
std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
|
|
KWayland::Client::Registry registry;
|
|
QSignalSpy gesturesAnnoucedSpy(®istry, &KWayland::Client::Registry::pointerGesturesUnstableV1Announced);
|
|
registry.create(m_connection);
|
|
registry.setup();
|
|
QVERIFY(gesturesAnnoucedSpy.wait());
|
|
QtWayland::zwp_pointer_gestures_v1 gestures(registry, gesturesAnnoucedSpy.first().at(0).value<int>(), gesturesAnnoucedSpy.first().at(1).value<int>());
|
|
PointerHoldGesture gesture(gestures.get_hold_gesture(*pointer));
|
|
QVERIFY(gesture.isInitialized());
|
|
|
|
QSignalSpy startSpy(&gesture, &PointerHoldGesture::started);
|
|
QSignalSpy endSpy(&gesture, &PointerHoldGesture::ended);
|
|
QSignalSpy cancelledSpy(&gesture, &PointerHoldGesture::cancelled);
|
|
|
|
// now create a surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
surface->attachBuffer(m_shm->createBuffer(image));
|
|
surface->damage(image.rect());
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
|
|
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
|
|
QVERIFY(m_seatInterface->pointer());
|
|
|
|
// send in the start
|
|
std::chrono::milliseconds timestamp(1);
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->startPointerHoldGesture(3);
|
|
QVERIFY(startSpy.wait());
|
|
QCOMPARE(startSpy.count(), 1);
|
|
QCOMPARE(startSpy.first().at(0).value<quint32>(), m_display->serial());
|
|
QCOMPARE(startSpy.first().at(1).value<quint32>(), 1u);
|
|
QCOMPARE(startSpy.first().at(2).value<void *>(), *surface.get());
|
|
QCOMPARE(startSpy.first().at(3).value<quint32>(), 3);
|
|
|
|
// another start should not be possible
|
|
m_seatInterface->startPointerPinchGesture(3);
|
|
QVERIFY(sync());
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
// now end or cancel
|
|
QFETCH(bool, cancel);
|
|
QSignalSpy *spy;
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
if (cancel) {
|
|
m_seatInterface->cancelPointerHoldGesture();
|
|
spy = &cancelledSpy;
|
|
} else {
|
|
m_seatInterface->endPointerHoldGesture();
|
|
spy = &endSpy;
|
|
}
|
|
QVERIFY(spy->wait());
|
|
QFETCH(int, expectedEndCount);
|
|
QCOMPARE(endSpy.count(), expectedEndCount);
|
|
QFETCH(int, expectedCancelCount);
|
|
QCOMPARE(cancelledSpy.count(), expectedCancelCount);
|
|
QCOMPARE(spy->count(), 1);
|
|
QCOMPARE(spy->first().at(0).value<quint32>(), m_display->serial());
|
|
QCOMPARE(spy->first().at(1).value<quint32>(), 2);
|
|
|
|
// now a start should be possible again
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->startPointerHoldGesture(3);
|
|
QVERIFY(startSpy.wait());
|
|
|
|
// and end
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
if (cancel) {
|
|
m_seatInterface->cancelPointerHoldGesture();
|
|
} else {
|
|
m_seatInterface->endPointerHoldGesture();
|
|
}
|
|
QVERIFY(spy->wait());
|
|
}
|
|
|
|
void TestWaylandSeat::testPointerAxis()
|
|
{
|
|
using namespace KWin;
|
|
|
|
// first create the pointer
|
|
QSignalSpy hasPointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(hasPointerChangedSpy.wait());
|
|
std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
|
|
QVERIFY(pointer);
|
|
|
|
// now create a surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
surface->attachBuffer(m_shm->createBuffer(image));
|
|
surface->damage(image.rect());
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
|
|
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
|
|
QSignalSpy frameSpy(pointer.get(), &KWayland::Client::Pointer::frame);
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 1);
|
|
|
|
// let's scroll vertically
|
|
QSignalSpy axisSourceSpy(pointer.get(), &KWayland::Client::Pointer::axisSourceChanged);
|
|
QSignalSpy axisSpy(pointer.get(), &KWayland::Client::Pointer::axisChanged);
|
|
QSignalSpy axisDiscreteSpy(pointer.get(), &KWayland::Client::Pointer::axisDiscreteChanged);
|
|
QSignalSpy axisStoppedSpy(pointer.get(), &KWayland::Client::Pointer::axisStopped);
|
|
|
|
std::chrono::milliseconds timestamp(1);
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerAxis(Qt::Vertical, 10, 120, PointerAxisSource::Wheel);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 2);
|
|
QCOMPARE(axisSourceSpy.count(), 1);
|
|
QCOMPARE(axisSourceSpy.last().at(0).value<KWayland::Client::Pointer::AxisSource>(), KWayland::Client::Pointer::AxisSource::Wheel);
|
|
QCOMPARE(axisDiscreteSpy.count(), 1);
|
|
QCOMPARE(axisDiscreteSpy.last().at(0).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Vertical);
|
|
QCOMPARE(axisDiscreteSpy.last().at(1).value<qint32>(), 1);
|
|
QCOMPARE(axisSpy.count(), 1);
|
|
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(1));
|
|
QCOMPARE(axisSpy.last().at(1).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Vertical);
|
|
QCOMPARE(axisSpy.last().at(2).value<qreal>(), 10.0);
|
|
QCOMPARE(axisStoppedSpy.count(), 0);
|
|
|
|
// let's scroll using fingers
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerAxis(Qt::Horizontal, 42, 0, PointerAxisSource::Finger);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 3);
|
|
QCOMPARE(axisSourceSpy.count(), 2);
|
|
QCOMPARE(axisSourceSpy.last().at(0).value<KWayland::Client::Pointer::AxisSource>(), KWayland::Client::Pointer::AxisSource::Finger);
|
|
QCOMPARE(axisDiscreteSpy.count(), 1);
|
|
QCOMPARE(axisSpy.count(), 2);
|
|
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(2));
|
|
QCOMPARE(axisSpy.last().at(1).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Horizontal);
|
|
QCOMPARE(axisSpy.last().at(2).value<qreal>(), 42.0);
|
|
QCOMPARE(axisStoppedSpy.count(), 0);
|
|
|
|
// lift the fingers off the device
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerAxis(Qt::Horizontal, 0, 0, PointerAxisSource::Finger);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 4);
|
|
QCOMPARE(axisSourceSpy.count(), 3);
|
|
QCOMPARE(axisSourceSpy.last().at(0).value<KWayland::Client::Pointer::AxisSource>(), KWayland::Client::Pointer::AxisSource::Finger);
|
|
QCOMPARE(axisDiscreteSpy.count(), 1);
|
|
QCOMPARE(axisSpy.count(), 2);
|
|
QCOMPARE(axisStoppedSpy.count(), 1);
|
|
QCOMPARE(axisStoppedSpy.last().at(0).value<quint32>(), 3);
|
|
QCOMPARE(axisStoppedSpy.last().at(1).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Horizontal);
|
|
|
|
// if the device is unknown, no axis_source event should be sent
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyPointerAxis(Qt::Horizontal, 42, 120, PointerAxisSource::Unknown);
|
|
m_seatInterface->notifyPointerFrame();
|
|
QVERIFY(frameSpy.wait());
|
|
QCOMPARE(frameSpy.count(), 5);
|
|
QCOMPARE(axisSourceSpy.count(), 3);
|
|
QCOMPARE(axisDiscreteSpy.count(), 2);
|
|
QCOMPARE(axisDiscreteSpy.last().at(0).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Horizontal);
|
|
QCOMPARE(axisDiscreteSpy.last().at(1).value<qint32>(), 1);
|
|
QCOMPARE(axisSpy.count(), 3);
|
|
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(4));
|
|
QCOMPARE(axisSpy.last().at(1).value<KWayland::Client::Pointer::Axis>(), KWayland::Client::Pointer::Axis::Horizontal);
|
|
QCOMPARE(axisSpy.last().at(2).value<qreal>(), 42.0);
|
|
QCOMPARE(axisStoppedSpy.count(), 1);
|
|
}
|
|
|
|
void TestWaylandSeat::testCursor()
|
|
{
|
|
using namespace KWin;
|
|
|
|
QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
|
|
m_seatInterface->setHasPointer(true);
|
|
QVERIFY(pointerSpy.wait());
|
|
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
KWayland::Client::Surface *surface = m_compositor->createSurface(m_compositor);
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
|
image.fill(Qt::black);
|
|
surface->attachBuffer(m_shm->createBuffer(image));
|
|
surface->damage(image.rect());
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
QSignalSpy committedSpy(serverSurface, &KWin::SurfaceInterface::committed);
|
|
QVERIFY(committedSpy.wait());
|
|
|
|
std::unique_ptr<KWayland::Client::Pointer> p(m_seat->createPointer());
|
|
QVERIFY(p->isValid());
|
|
wl_display_flush(m_connection->display());
|
|
QCoreApplication::processEvents();
|
|
|
|
QSignalSpy enteredSpy(p.get(), &KWayland::Client::Pointer::entered);
|
|
|
|
uint32_t serial = m_seatInterface->display()->serial();
|
|
m_seatInterface->notifyPointerEnter(serverSurface, QPointF(20, 18), QPointF(10, 15));
|
|
QVERIFY(enteredSpy.wait());
|
|
QCOMPARE_GT(enteredSpy.first().first().value<quint32>(), serial);
|
|
QVERIFY(m_seatInterface->focusedPointerSurface());
|
|
|
|
QSignalSpy cursorChangedSpy(m_seatInterface->pointer(), &KWin::PointerInterface::cursorChanged);
|
|
// just remove the pointer
|
|
p->setCursor(nullptr);
|
|
QVERIFY(cursorChangedSpy.wait());
|
|
QCOMPARE(cursorChangedSpy.count(), 1);
|
|
auto cursor = std::get<KWin::PointerSurfaceCursor *>(cursorChangedSpy.last().first().value<KWin::PointerCursor>());
|
|
QVERIFY(cursor);
|
|
QVERIFY(!cursor->surface());
|
|
QCOMPARE(cursor->hotspot(), QPoint());
|
|
|
|
// test changing hotspot
|
|
p->setCursor(nullptr, QPoint(1, 2));
|
|
QVERIFY(cursorChangedSpy.wait());
|
|
QCOMPARE(cursorChangedSpy.count(), 2);
|
|
QCOMPARE(cursor->surface(), nullptr);
|
|
QCOMPARE(cursor->hotspot(), QPoint(1, 2));
|
|
|
|
// set surface
|
|
QImage img(QSize(10, 20), QImage::Format_RGB32);
|
|
img.fill(Qt::red);
|
|
auto cursorSurface = m_compositor->createSurface(m_compositor);
|
|
cursorSurface->attachBuffer(m_shm->createBuffer(img));
|
|
cursorSurface->damage(QRect(0, 0, 10, 20));
|
|
cursorSurface->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
p->setCursor(cursorSurface, QPoint(1, 2));
|
|
QVERIFY(cursorChangedSpy.wait());
|
|
QCOMPARE(cursorChangedSpy.count(), 3);
|
|
QCOMPARE(cursor->hotspot(), QPoint(1, 2));
|
|
QVERIFY(cursor->surface());
|
|
|
|
p->hideCursor();
|
|
QVERIFY(cursorChangedSpy.wait());
|
|
QCOMPARE(cursorChangedSpy.count(), 4);
|
|
QVERIFY(!cursor->surface());
|
|
}
|
|
|
|
void TestWaylandSeat::testKeyboard()
|
|
{
|
|
using namespace KWin;
|
|
|
|
QSignalSpy keyboardSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
|
|
m_seatInterface->setHasKeyboard(true);
|
|
QVERIFY(keyboardSpy.wait());
|
|
|
|
// update modifiers before any surface focused
|
|
m_seatInterface->notifyKeyboardModifiers(4, 3, 2, 1);
|
|
|
|
// create the surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
KWayland::Client::Surface *s = m_compositor->createSurface(m_compositor);
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
KWayland::Client::Keyboard *keyboard = m_seat->createKeyboard(m_seat);
|
|
QSignalSpy repeatInfoSpy(keyboard, &KWayland::Client::Keyboard::keyRepeatChanged);
|
|
const KWayland::Client::Keyboard &ckeyboard = *keyboard;
|
|
QVERIFY(keyboard->isValid());
|
|
QCOMPARE(keyboard->isKeyRepeatEnabled(), false);
|
|
QCOMPARE(keyboard->keyRepeatDelay(), 0);
|
|
QCOMPARE(keyboard->keyRepeatRate(), 0);
|
|
QVERIFY(repeatInfoSpy.wait());
|
|
|
|
auto serverKeyboard = m_seatInterface->keyboard();
|
|
QVERIFY(serverKeyboard);
|
|
|
|
// we should get the repeat info announced
|
|
QCOMPARE(repeatInfoSpy.count(), 1);
|
|
QCOMPARE(keyboard->isKeyRepeatEnabled(), false);
|
|
QCOMPARE(keyboard->keyRepeatDelay(), 0);
|
|
QCOMPARE(keyboard->keyRepeatRate(), 0);
|
|
|
|
// let's change repeat in server
|
|
m_seatInterface->keyboard()->setRepeatInfo(25, 660);
|
|
QVERIFY(repeatInfoSpy.wait());
|
|
QCOMPARE(repeatInfoSpy.count(), 2);
|
|
QCOMPARE(keyboard->isKeyRepeatEnabled(), true);
|
|
QCOMPARE(keyboard->keyRepeatRate(), 25);
|
|
QCOMPARE(keyboard->keyRepeatDelay(), 660);
|
|
|
|
std::chrono::milliseconds time(1);
|
|
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_K, KeyboardKeyState::Pressed);
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_D, KeyboardKeyState::Pressed);
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_E, KeyboardKeyState::Pressed);
|
|
|
|
QSignalSpy modifierSpy(keyboard, &KWayland::Client::Keyboard::modifiersChanged);
|
|
|
|
QSignalSpy enteredSpy(keyboard, &KWayland::Client::Keyboard::entered);
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface);
|
|
QCOMPARE(m_seatInterface->keyboard()->focusedSurface(), serverSurface);
|
|
|
|
// we get the modifiers sent after the enter
|
|
QVERIFY(modifierSpy.wait());
|
|
QCOMPARE(modifierSpy.count(), 1);
|
|
QCOMPARE(modifierSpy.first().at(0).value<quint32>(), quint32(4));
|
|
QCOMPARE(modifierSpy.first().at(1).value<quint32>(), quint32(3));
|
|
QCOMPARE(modifierSpy.first().at(2).value<quint32>(), quint32(2));
|
|
QCOMPARE(modifierSpy.first().at(3).value<quint32>(), quint32(1));
|
|
QCOMPARE(enteredSpy.count(), 1);
|
|
// TODO: get through API
|
|
QCOMPARE(enteredSpy.first().first().value<quint32>(), m_display->serial() - 1);
|
|
|
|
QSignalSpy keyChangedSpy(keyboard, &KWayland::Client::Keyboard::keyChanged);
|
|
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_E, KeyboardKeyState::Released);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_D, KeyboardKeyState::Released);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_K, KeyboardKeyState::Released);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_F1, KeyboardKeyState::Pressed);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
m_seatInterface->setTimestamp(time++);
|
|
m_seatInterface->notifyKeyboardKey(KEY_F1, KeyboardKeyState::Released);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
|
|
QCOMPARE(keyChangedSpy.count(), 5);
|
|
QCOMPARE(keyChangedSpy.at(0).at(0).value<quint32>(), quint32(KEY_E));
|
|
QCOMPARE(keyChangedSpy.at(0).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
|
|
QCOMPARE(keyChangedSpy.at(0).at(2).value<quint32>(), quint32(4));
|
|
QCOMPARE(keyChangedSpy.at(1).at(0).value<quint32>(), quint32(KEY_D));
|
|
QCOMPARE(keyChangedSpy.at(1).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
|
|
QCOMPARE(keyChangedSpy.at(1).at(2).value<quint32>(), quint32(5));
|
|
QCOMPARE(keyChangedSpy.at(2).at(0).value<quint32>(), quint32(KEY_K));
|
|
QCOMPARE(keyChangedSpy.at(2).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
|
|
QCOMPARE(keyChangedSpy.at(2).at(2).value<quint32>(), quint32(6));
|
|
QCOMPARE(keyChangedSpy.at(3).at(0).value<quint32>(), quint32(KEY_F1));
|
|
QCOMPARE(keyChangedSpy.at(3).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
|
|
QCOMPARE(keyChangedSpy.at(3).at(2).value<quint32>(), quint32(7));
|
|
QCOMPARE(keyChangedSpy.at(4).at(0).value<quint32>(), quint32(KEY_F1));
|
|
QCOMPARE(keyChangedSpy.at(4).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Released);
|
|
QCOMPARE(keyChangedSpy.at(4).at(2).value<quint32>(), quint32(8));
|
|
|
|
// releasing a key which is already released should not set a key changed
|
|
m_seatInterface->notifyKeyboardKey(KEY_F1, KeyboardKeyState::Released);
|
|
QVERIFY(sync());
|
|
QCOMPARE(keyChangedSpy.count(), 5);
|
|
// let's press it again
|
|
m_seatInterface->notifyKeyboardKey(KEY_F1, KeyboardKeyState::Pressed);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
QCOMPARE(keyChangedSpy.count(), 6);
|
|
// press again should be ignored
|
|
m_seatInterface->notifyKeyboardKey(KEY_F1, KeyboardKeyState::Pressed);
|
|
QVERIFY(sync());
|
|
QCOMPARE(keyChangedSpy.count(), 6);
|
|
// and release
|
|
m_seatInterface->notifyKeyboardKey(KEY_F1, KeyboardKeyState::Released);
|
|
QVERIFY(keyChangedSpy.wait());
|
|
QCOMPARE(keyChangedSpy.count(), 7);
|
|
|
|
m_seatInterface->notifyKeyboardModifiers(1, 2, 3, 4);
|
|
QVERIFY(modifierSpy.wait());
|
|
QCOMPARE(modifierSpy.count(), 2);
|
|
QCOMPARE(modifierSpy.last().at(0).value<quint32>(), quint32(1));
|
|
QCOMPARE(modifierSpy.last().at(1).value<quint32>(), quint32(2));
|
|
QCOMPARE(modifierSpy.last().at(2).value<quint32>(), quint32(3));
|
|
QCOMPARE(modifierSpy.last().at(3).value<quint32>(), quint32(4));
|
|
|
|
QSignalSpy leftSpy(keyboard, &KWayland::Client::Keyboard::left);
|
|
m_seatInterface->setFocusedKeyboardSurface(nullptr);
|
|
QVERIFY(!m_seatInterface->focusedKeyboardSurface());
|
|
QVERIFY(leftSpy.wait());
|
|
QCOMPARE(leftSpy.count(), 1);
|
|
// TODO: get through API
|
|
QCOMPARE(leftSpy.first().first().value<quint32>(), m_display->serial() - 1);
|
|
|
|
QVERIFY(!keyboard->enteredSurface());
|
|
QVERIFY(!ckeyboard.enteredSurface());
|
|
|
|
// enter it again
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
QVERIFY(modifierSpy.wait());
|
|
QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface);
|
|
QCOMPARE(m_seatInterface->keyboard()->focusedSurface(), serverSurface);
|
|
QCOMPARE(enteredSpy.count(), 2);
|
|
|
|
QCOMPARE(keyboard->enteredSurface(), s);
|
|
QCOMPARE(ckeyboard.enteredSurface(), s);
|
|
|
|
QSignalSpy serverSurfaceDestroyedSpy(serverSurface, &QObject::destroyed);
|
|
QCOMPARE(keyboard->enteredSurface(), s);
|
|
delete s;
|
|
QVERIFY(!keyboard->enteredSurface());
|
|
QVERIFY(leftSpy.wait());
|
|
QCOMPARE(serverSurfaceDestroyedSpy.count(), 1);
|
|
QVERIFY(!m_seatInterface->focusedKeyboardSurface());
|
|
QVERIFY(!serverKeyboard->focusedSurface());
|
|
|
|
// let's create a Surface again
|
|
std::unique_ptr<KWayland::Client::Surface> s2(m_compositor->createSurface());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
QCOMPARE(surfaceCreatedSpy.count(), 2);
|
|
serverSurface = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface);
|
|
QCOMPARE(m_seatInterface->keyboard(), serverKeyboard);
|
|
}
|
|
|
|
void TestWaylandSeat::testSelection()
|
|
{
|
|
using namespace KWin;
|
|
std::unique_ptr<DataDeviceManagerInterface> ddmi(new DataDeviceManagerInterface(m_display));
|
|
KWayland::Client::Registry registry;
|
|
QSignalSpy dataDeviceManagerSpy(®istry, &KWayland::Client::Registry::dataDeviceManagerAnnounced);
|
|
m_seatInterface->setHasKeyboard(true);
|
|
registry.setEventQueue(m_queue);
|
|
registry.create(m_connection->display());
|
|
QVERIFY(registry.isValid());
|
|
registry.setup();
|
|
|
|
QVERIFY(dataDeviceManagerSpy.wait());
|
|
std::unique_ptr<KWayland::Client::DataDeviceManager> ddm(
|
|
registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(), dataDeviceManagerSpy.first().last().value<quint32>()));
|
|
QVERIFY(ddm->isValid());
|
|
|
|
std::unique_ptr<KWayland::Client::DataDevice> dd1(ddm->getDataDevice(m_seat));
|
|
QVERIFY(dd1->isValid());
|
|
QSignalSpy selectionSpy(dd1.get(), &KWayland::Client::DataDevice::selectionOffered);
|
|
QSignalSpy selectionClearedSpy(dd1.get(), &KWayland::Client::DataDevice::selectionCleared);
|
|
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surface->isValid());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(!m_seatInterface->selection());
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface);
|
|
QVERIFY(selectionClearedSpy.wait());
|
|
QVERIFY(selectionSpy.isEmpty());
|
|
QVERIFY(!selectionClearedSpy.isEmpty());
|
|
selectionClearedSpy.clear();
|
|
QVERIFY(!m_seatInterface->selection());
|
|
|
|
// now let's try to set a selection - we have keyboard focus, so it should be sent to us
|
|
std::unique_ptr<KWayland::Client::DataSource> ds(ddm->createDataSource());
|
|
QVERIFY(ds->isValid());
|
|
ds->offer(QStringLiteral("text/plain"));
|
|
dd1->setSelection(m_display->nextSerial(), ds.get());
|
|
QVERIFY(selectionSpy.wait());
|
|
QCOMPARE(selectionSpy.count(), 1);
|
|
auto ddi = m_seatInterface->selection();
|
|
QVERIFY(ddi);
|
|
auto df = selectionSpy.first().first().value<KWayland::Client::DataOffer *>();
|
|
QCOMPARE(df->offeredMimeTypes().count(), 1);
|
|
QCOMPARE(df->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
|
|
|
|
// try to clear
|
|
dd1->setSelection(m_display->nextSerial());
|
|
QVERIFY(selectionClearedSpy.wait());
|
|
QCOMPARE(selectionClearedSpy.count(), 1);
|
|
QCOMPARE(selectionSpy.count(), 1);
|
|
|
|
// unset the keyboard focus
|
|
m_seatInterface->setFocusedKeyboardSurface(nullptr);
|
|
QVERIFY(!m_seatInterface->focusedKeyboardSurface());
|
|
serverSurface->client()->flush();
|
|
QCoreApplication::processEvents();
|
|
QCoreApplication::processEvents();
|
|
|
|
// try to set Selection
|
|
dd1->setSelection(m_display->nextSerial(), ds.get());
|
|
wl_display_flush(m_connection->display());
|
|
QCoreApplication::processEvents();
|
|
QCoreApplication::processEvents();
|
|
QCOMPARE(selectionSpy.count(), 1);
|
|
|
|
// let's unset the selection on the seat
|
|
m_seatInterface->setSelection(nullptr, m_display->nextSerial());
|
|
// and pass focus back on our surface
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
// we don't have a selection, so it should not send a selection
|
|
QVERIFY(sync());
|
|
QCOMPARE(selectionSpy.count(), 1);
|
|
// now let's set it manually
|
|
m_seatInterface->setSelection(ddi, m_display->nextSerial());
|
|
QCOMPARE(m_seatInterface->selection(), ddi);
|
|
QVERIFY(selectionSpy.wait());
|
|
QCOMPARE(selectionSpy.count(), 2);
|
|
// setting the same again should not change
|
|
m_seatInterface->setSelection(ddi, m_display->nextSerial());
|
|
QVERIFY(sync());
|
|
QCOMPARE(selectionSpy.count(), 2);
|
|
// now clear it manually
|
|
m_seatInterface->setSelection(nullptr, m_display->nextSerial());
|
|
QVERIFY(selectionClearedSpy.wait());
|
|
QCOMPARE(selectionSpy.count(), 2);
|
|
|
|
// create a second ddi and a data source
|
|
std::unique_ptr<KWayland::Client::DataDevice> dd2(ddm->getDataDevice(m_seat));
|
|
QVERIFY(dd2->isValid());
|
|
std::unique_ptr<KWayland::Client::DataSource> ds2(ddm->createDataSource());
|
|
QVERIFY(ds2->isValid());
|
|
ds2->offer(QStringLiteral("text/plain"));
|
|
dd2->setSelection(m_display->nextSerial(), ds2.get());
|
|
QVERIFY(selectionSpy.wait());
|
|
QSignalSpy cancelledSpy(ds2.get(), &KWayland::Client::DataSource::cancelled);
|
|
m_seatInterface->setSelection(ddi, m_display->nextSerial());
|
|
QVERIFY(cancelledSpy.wait());
|
|
}
|
|
|
|
void TestWaylandSeat::testDataDeviceForKeyboardSurface()
|
|
{
|
|
// this test verifies that the server does not crash when creating a datadevice for the focused keyboard surface
|
|
// and the currentSelection does not have a DataSource.
|
|
// to properly test the functionality this test requires a second client
|
|
using namespace KWin;
|
|
// create the DataDeviceManager
|
|
std::unique_ptr<DataDeviceManagerInterface> ddmi(new DataDeviceManagerInterface(m_display));
|
|
QSignalSpy ddiCreatedSpy(ddmi.get(), &DataDeviceManagerInterface::dataDeviceCreated);
|
|
m_seatInterface->setHasKeyboard(true);
|
|
|
|
// create a second Wayland client connection to use it for setSelection
|
|
auto c = new KWayland::Client::ConnectionThread;
|
|
QSignalSpy connectedSpy(c, &KWayland::Client::ConnectionThread::connected);
|
|
c->setSocketName(s_socketName);
|
|
|
|
auto thread = new QThread(this);
|
|
c->moveToThread(thread);
|
|
thread->start();
|
|
|
|
c->initConnection();
|
|
QVERIFY(connectedSpy.wait());
|
|
|
|
std::unique_ptr<KWayland::Client::EventQueue> queue(new KWayland::Client::EventQueue);
|
|
queue->setup(c);
|
|
|
|
std::unique_ptr<KWayland::Client::Registry> registry(new KWayland::Client::Registry);
|
|
QSignalSpy interfacesAnnouncedSpy(registry.get(), &KWayland::Client::Registry::interfacesAnnounced);
|
|
registry->setEventQueue(queue.get());
|
|
registry->create(c);
|
|
QVERIFY(registry->isValid());
|
|
registry->setup();
|
|
|
|
QVERIFY(interfacesAnnouncedSpy.wait());
|
|
std::unique_ptr<KWayland::Client::Seat> seat(
|
|
registry->createSeat(registry->interface(KWayland::Client::Registry::Interface::Seat).name, registry->interface(KWayland::Client::Registry::Interface::Seat).version));
|
|
QVERIFY(seat->isValid());
|
|
std::unique_ptr<KWayland::Client::DataDeviceManager> ddm1(registry->createDataDeviceManager(registry->interface(KWayland::Client::Registry::Interface::DataDeviceManager).name,
|
|
registry->interface(KWayland::Client::Registry::Interface::DataDeviceManager).version));
|
|
QVERIFY(ddm1->isValid());
|
|
|
|
// now create our first datadevice
|
|
std::unique_ptr<KWayland::Client::DataDevice> dd1(ddm1->getDataDevice(seat.get()));
|
|
QVERIFY(ddiCreatedSpy.wait());
|
|
auto ddi = ddiCreatedSpy.first().first().value<DataDeviceInterface *>();
|
|
QVERIFY(ddi);
|
|
m_seatInterface->setSelection(ddi->selection(), m_display->nextSerial());
|
|
|
|
// switch to other client
|
|
// create a surface and pass it keyboard focus
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surface->isValid());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface);
|
|
|
|
// now create a DataDevice
|
|
KWayland::Client::Registry registry2;
|
|
QSignalSpy dataDeviceManagerSpy(®istry2, &KWayland::Client::Registry::dataDeviceManagerAnnounced);
|
|
registry2.setEventQueue(m_queue);
|
|
registry2.create(m_connection->display());
|
|
QVERIFY(registry2.isValid());
|
|
registry2.setup();
|
|
|
|
QVERIFY(dataDeviceManagerSpy.wait());
|
|
std::unique_ptr<KWayland::Client::DataDeviceManager> ddm(
|
|
registry2.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(), dataDeviceManagerSpy.first().last().value<quint32>()));
|
|
QVERIFY(ddm->isValid());
|
|
|
|
std::unique_ptr<KWayland::Client::DataDevice> dd(ddm->getDataDevice(m_seat));
|
|
QVERIFY(dd->isValid());
|
|
QVERIFY(ddiCreatedSpy.wait());
|
|
|
|
// unset surface and set again
|
|
m_seatInterface->setFocusedKeyboardSurface(nullptr);
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
|
|
// and delete the connection thread again
|
|
dd1.reset();
|
|
ddm1.reset();
|
|
seat.reset();
|
|
registry.reset();
|
|
queue.reset();
|
|
c->deleteLater();
|
|
thread->quit();
|
|
thread->wait();
|
|
delete thread;
|
|
}
|
|
|
|
void TestWaylandSeat::testTouch()
|
|
{
|
|
using namespace KWin;
|
|
|
|
QSignalSpy touchSpy(m_seat, &KWayland::Client::Seat::hasTouchChanged);
|
|
m_seatInterface->setHasTouch(true);
|
|
QVERIFY(touchSpy.wait());
|
|
|
|
// create the surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
KWayland::Client::Surface *s = m_compositor->createSurface(m_compositor);
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
|
|
QVERIFY(serverSurface);
|
|
|
|
m_seatInterface->setFocusedTouchSurface(serverSurface);
|
|
// no keyboard yet
|
|
QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface);
|
|
|
|
KWayland::Client::Touch *touch = m_seat->createTouch(m_seat);
|
|
QVERIFY(touch->isValid());
|
|
|
|
// Process wl_touch bind request.
|
|
wl_display_flush(m_connection->display());
|
|
QCoreApplication::processEvents();
|
|
|
|
QSignalSpy sequenceStartedSpy(touch, &KWayland::Client::Touch::sequenceStarted);
|
|
QSignalSpy sequenceEndedSpy(touch, &KWayland::Client::Touch::sequenceEnded);
|
|
QSignalSpy sequenceCanceledSpy(touch, &KWayland::Client::Touch::sequenceCanceled);
|
|
QSignalSpy frameEndedSpy(touch, &KWayland::Client::Touch::frameEnded);
|
|
QSignalSpy pointAddedSpy(touch, &KWayland::Client::Touch::pointAdded);
|
|
QSignalSpy pointMovedSpy(touch, &KWayland::Client::Touch::pointMoved);
|
|
QSignalSpy pointRemovedSpy(touch, &KWayland::Client::Touch::pointRemoved);
|
|
|
|
std::chrono::milliseconds timestamp(1);
|
|
|
|
// try a few things
|
|
m_seatInterface->setFocusedTouchSurfacePosition(QPointF(10, 20));
|
|
QCOMPARE(m_seatInterface->focusedTouchSurfacePosition(), QPointF(10, 20));
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchDown(0, QPointF(15, 26));
|
|
QVERIFY(sequenceStartedSpy.wait());
|
|
QCOMPARE(sequenceStartedSpy.count(), 1);
|
|
QCOMPARE(sequenceEndedSpy.count(), 0);
|
|
QCOMPARE(sequenceCanceledSpy.count(), 0);
|
|
QCOMPARE(frameEndedSpy.count(), 0);
|
|
QCOMPARE(pointAddedSpy.count(), 0);
|
|
QCOMPARE(pointMovedSpy.count(), 0);
|
|
QCOMPARE(pointRemovedSpy.count(), 0);
|
|
KWayland::Client::TouchPoint *tp = sequenceStartedSpy.first().first().value<KWayland::Client::TouchPoint *>();
|
|
QVERIFY(tp);
|
|
QCOMPARE(tp->downSerial(), m_seatInterface->display()->serial());
|
|
QCOMPARE(tp->id(), 0);
|
|
QVERIFY(tp->isDown());
|
|
QCOMPARE(tp->position(), QPointF(5, 6));
|
|
QCOMPARE(tp->positions().size(), 1);
|
|
QCOMPARE(tp->time(), 1u);
|
|
QCOMPARE(tp->timestamps().count(), 1);
|
|
QCOMPARE(tp->upSerial(), 0u);
|
|
QCOMPARE(tp->surface().data(), s);
|
|
QCOMPARE(touch->sequence().count(), 1);
|
|
QCOMPARE(touch->sequence().first(), tp);
|
|
|
|
// let's end the frame
|
|
m_seatInterface->notifyTouchFrame();
|
|
QVERIFY(frameEndedSpy.wait());
|
|
QCOMPARE(frameEndedSpy.count(), 1);
|
|
|
|
// move the one point
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchMotion(0, QPointF(10, 20));
|
|
m_seatInterface->notifyTouchFrame();
|
|
QVERIFY(frameEndedSpy.wait());
|
|
QCOMPARE(sequenceStartedSpy.count(), 1);
|
|
QCOMPARE(sequenceEndedSpy.count(), 0);
|
|
QCOMPARE(sequenceCanceledSpy.count(), 0);
|
|
QCOMPARE(frameEndedSpy.count(), 2);
|
|
QCOMPARE(pointAddedSpy.count(), 0);
|
|
QCOMPARE(pointMovedSpy.count(), 1);
|
|
QCOMPARE(pointRemovedSpy.count(), 0);
|
|
QCOMPARE(pointMovedSpy.first().first().value<KWayland::Client::TouchPoint *>(), tp);
|
|
|
|
QCOMPARE(tp->id(), 0);
|
|
QVERIFY(tp->isDown());
|
|
QCOMPARE(tp->position(), QPointF(0, 0));
|
|
QCOMPARE(tp->positions().size(), 2);
|
|
QCOMPARE(tp->time(), 2u);
|
|
QCOMPARE(tp->timestamps().count(), 2);
|
|
QCOMPARE(tp->upSerial(), 0u);
|
|
QCOMPARE(tp->surface().data(), s);
|
|
|
|
// add onther point
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchDown(1, QPointF(15, 26));
|
|
m_seatInterface->notifyTouchFrame();
|
|
QVERIFY(frameEndedSpy.wait());
|
|
QCOMPARE(sequenceStartedSpy.count(), 1);
|
|
QCOMPARE(sequenceEndedSpy.count(), 0);
|
|
QCOMPARE(sequenceCanceledSpy.count(), 0);
|
|
QCOMPARE(frameEndedSpy.count(), 3);
|
|
QCOMPARE(pointAddedSpy.count(), 1);
|
|
QCOMPARE(pointMovedSpy.count(), 1);
|
|
QCOMPARE(pointRemovedSpy.count(), 0);
|
|
QCOMPARE(touch->sequence().count(), 2);
|
|
QCOMPARE(touch->sequence().first(), tp);
|
|
KWayland::Client::TouchPoint *tp2 = pointAddedSpy.first().first().value<KWayland::Client::TouchPoint *>();
|
|
QVERIFY(tp2);
|
|
QCOMPARE(touch->sequence().last(), tp2);
|
|
QCOMPARE(tp2->id(), 1);
|
|
QVERIFY(tp2->isDown());
|
|
QCOMPARE(tp2->position(), QPointF(5, 6));
|
|
QCOMPARE(tp2->positions().size(), 1);
|
|
QCOMPARE(tp2->time(), 3u);
|
|
QCOMPARE(tp2->timestamps().count(), 1);
|
|
QCOMPARE(tp2->upSerial(), 0u);
|
|
QCOMPARE(tp2->surface().data(), s);
|
|
|
|
// send it an up
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchUp(1);
|
|
m_seatInterface->notifyTouchFrame();
|
|
QVERIFY(frameEndedSpy.wait());
|
|
QCOMPARE(sequenceStartedSpy.count(), 1);
|
|
QCOMPARE(sequenceEndedSpy.count(), 0);
|
|
QCOMPARE(sequenceCanceledSpy.count(), 0);
|
|
QCOMPARE(frameEndedSpy.count(), 4);
|
|
QCOMPARE(pointAddedSpy.count(), 1);
|
|
QCOMPARE(pointMovedSpy.count(), 1);
|
|
QCOMPARE(pointRemovedSpy.count(), 1);
|
|
QCOMPARE(pointRemovedSpy.first().first().value<KWayland::Client::TouchPoint *>(), tp2);
|
|
QCOMPARE(tp2->id(), 1);
|
|
QVERIFY(!tp2->isDown());
|
|
QCOMPARE(tp2->position(), QPointF(5, 6));
|
|
QCOMPARE(tp2->positions().size(), 1);
|
|
QCOMPARE(tp2->time(), 4u);
|
|
QCOMPARE(tp2->timestamps().count(), 2);
|
|
QCOMPARE(tp2->upSerial(), m_seatInterface->display()->serial());
|
|
QCOMPARE(tp2->surface().data(), s);
|
|
|
|
// send another down and up
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchDown(1, QPointF(15, 26));
|
|
m_seatInterface->notifyTouchFrame();
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchUp(1);
|
|
// and send an up for the first point
|
|
m_seatInterface->notifyTouchUp(0);
|
|
m_seatInterface->notifyTouchFrame();
|
|
QVERIFY(frameEndedSpy.wait());
|
|
QCOMPARE(sequenceStartedSpy.count(), 1);
|
|
QCOMPARE(sequenceEndedSpy.count(), 1);
|
|
QCOMPARE(sequenceCanceledSpy.count(), 0);
|
|
QCOMPARE(frameEndedSpy.count(), 6);
|
|
QCOMPARE(pointAddedSpy.count(), 2);
|
|
QCOMPARE(pointMovedSpy.count(), 1);
|
|
QCOMPARE(pointRemovedSpy.count(), 3);
|
|
QCOMPARE(touch->sequence().count(), 3);
|
|
QVERIFY(!touch->sequence().at(0)->isDown());
|
|
QVERIFY(!touch->sequence().at(1)->isDown());
|
|
QVERIFY(!touch->sequence().at(2)->isDown());
|
|
QVERIFY(!m_seatInterface->isTouchSequence());
|
|
|
|
// try cancel
|
|
m_seatInterface->setFocusedTouchSurface(serverSurface, QPointF(15, 26));
|
|
m_seatInterface->setTimestamp(timestamp++);
|
|
m_seatInterface->notifyTouchDown(0, QPointF(15, 26));
|
|
m_seatInterface->notifyTouchFrame();
|
|
m_seatInterface->notifyTouchCancel();
|
|
QVERIFY(sequenceCanceledSpy.wait());
|
|
QCOMPARE(sequenceStartedSpy.count(), 2);
|
|
QCOMPARE(sequenceEndedSpy.count(), 1);
|
|
QCOMPARE(sequenceCanceledSpy.count(), 1);
|
|
QCOMPARE(frameEndedSpy.count(), 7);
|
|
QCOMPARE(pointAddedSpy.count(), 2);
|
|
QCOMPARE(pointMovedSpy.count(), 1);
|
|
QCOMPARE(pointRemovedSpy.count(), 3);
|
|
QCOMPARE(touch->sequence().first()->position(), QPointF(0, 0));
|
|
}
|
|
|
|
void TestWaylandSeat::testKeymap()
|
|
{
|
|
using namespace KWin;
|
|
|
|
m_seatInterface->setHasKeyboard(true);
|
|
QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
|
|
QVERIFY(keyboardChangedSpy.wait());
|
|
|
|
std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
|
|
|
|
// create surface
|
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
|
|
std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
|
|
QVERIFY(surface->isValid());
|
|
QVERIFY(surfaceCreatedSpy.wait());
|
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
|
QVERIFY(!m_seatInterface->selection());
|
|
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
|
|
|
|
QSignalSpy keymapChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keymapChanged);
|
|
|
|
m_seatInterface->keyboard()->setKeymap(QByteArrayLiteral("foo"));
|
|
QVERIFY(keymapChangedSpy.wait());
|
|
int fd = keymapChangedSpy.first().first().toInt();
|
|
QVERIFY(fd != -1);
|
|
// Account for null terminator.
|
|
QCOMPARE(keymapChangedSpy.first().last().value<quint32>(), 4u);
|
|
QFile file;
|
|
QVERIFY(file.open(fd, QIODevice::ReadOnly));
|
|
const char *address = reinterpret_cast<char *>(file.map(0, keymapChangedSpy.first().last().value<quint32>()));
|
|
QVERIFY(address);
|
|
QCOMPARE(qstrcmp(address, "foo"), 0);
|
|
file.close();
|
|
|
|
// change the keymap
|
|
keymapChangedSpy.clear();
|
|
m_seatInterface->keyboard()->setKeymap(QByteArrayLiteral("bar"));
|
|
QVERIFY(keymapChangedSpy.wait());
|
|
fd = keymapChangedSpy.first().first().toInt();
|
|
QVERIFY(fd != -1);
|
|
// Account for null terminator.
|
|
QCOMPARE(keymapChangedSpy.first().last().value<quint32>(), 4u);
|
|
QVERIFY(file.open(fd, QIODevice::ReadWrite));
|
|
address = reinterpret_cast<char *>(file.map(0, keymapChangedSpy.first().last().value<quint32>()));
|
|
QVERIFY(address);
|
|
QCOMPARE(qstrcmp(address, "bar"), 0);
|
|
}
|
|
|
|
QTEST_GUILESS_MAIN(TestWaylandSeat)
|
|
#include "test_wayland_seat.moc"
|