Commit 4a1888fd authored by Martin Flöser's avatar Martin Flöser
Browse files

Add support for setting cursor on the Pointer

Methods on client side added and proper handling on server side.
parent ff33fcdb
......@@ -60,6 +60,7 @@ private Q_SLOTS:
void testPointer();
void testPointerButton_data();
void testPointerButton();
void testCursor();
void testKeyboard();
void testCast();
void testDestroy();
......@@ -73,6 +74,7 @@ private:
KWayland::Client::ConnectionThread *m_connection;
KWayland::Client::Compositor *m_compositor;
KWayland::Client::Seat *m_seat;
KWayland::Client::ShmPool *m_shm;
KWayland::Client::EventQueue *m_queue;
QThread *m_thread;
};
......@@ -87,6 +89,7 @@ TestWaylandSeat::TestWaylandSeat(QObject *parent)
, m_connection(nullptr)
, m_compositor(nullptr)
, m_seat(nullptr)
, m_shm(nullptr)
, m_queue(nullptr)
, m_thread(nullptr)
{
......@@ -100,6 +103,7 @@ void TestWaylandSeat::init()
m_display->setSocketName(s_socketName);
m_display->start();
QVERIFY(m_display->isRunning());
m_display->createShm();
m_compositorInterface = m_display->createCompositor(m_display);
QVERIFY(m_compositorInterface);
......@@ -132,6 +136,7 @@ void TestWaylandSeat::init()
KWayland::Client::Registry registry;
QSignalSpy compositorSpy(&registry, SIGNAL(compositorAnnounced(quint32,quint32)));
QSignalSpy seatSpy(&registry, SIGNAL(seatAnnounced(quint32,quint32)));
QSignalSpy shmSpy(&registry, SIGNAL(shmAnnounced(quint32,quint32)));
registry.setEventQueue(m_queue);
registry.create(m_connection->display());
QVERIFY(registry.isValid());
......@@ -152,10 +157,18 @@ void TestWaylandSeat::init()
m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this);
QSignalSpy nameSpy(m_seat, SIGNAL(nameChanged(QString)));
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());
}
void TestWaylandSeat::cleanup()
{
if (m_shm) {
delete m_shm;
m_shm = nullptr;
}
if (m_seat) {
delete m_seat;
m_seat = nullptr;
......@@ -514,6 +527,103 @@ void TestWaylandSeat::testPointerButton()
QCOMPARE(buttonChangedSpy.last().at(3).value<KWayland::Client::Pointer::ButtonState>(), Pointer::ButtonState::Released);
}
void TestWaylandSeat::testCursor()
{
using namespace KWayland::Client;
using namespace KWayland::Server;
QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool)));
QVERIFY(pointerSpy.isValid());
m_seatInterface->setHasPointer(true);
QVERIFY(pointerSpy.wait());
QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*)));
QVERIFY(surfaceCreatedSpy.isValid());
m_compositor->createSurface(m_compositor);
QVERIFY(surfaceCreatedSpy.wait());
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWayland::Server::SurfaceInterface*>();
QVERIFY(serverSurface);
QScopedPointer<Pointer> p(m_seat->createPointer());
QVERIFY(p->isValid());
wl_display_flush(m_connection->display());
QCoreApplication::processEvents();
QSignalSpy enteredSpy(p.data(), SIGNAL(entered(quint32,QPointF)));
QVERIFY(enteredSpy.isValid());
m_seatInterface->setPointerPos(QPoint(20, 18));
m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15));
quint32 serial = m_seatInterface->display()->serial();
QVERIFY(enteredSpy.wait());
QCOMPARE(enteredSpy.first().first().value<quint32>(), serial);
QVERIFY(m_seatInterface->focusedPointerSurface());
QVERIFY(m_seatInterface->focusedPointer());
QVERIFY(!m_seatInterface->focusedPointer()->cursor());
QSignalSpy cursorChangedSpy(m_seatInterface->focusedPointer(), SIGNAL(cursorChanged()));
QVERIFY(cursorChangedSpy.isValid());
// just remove the pointer
p->setCursor(nullptr);
QVERIFY(cursorChangedSpy.wait());
QCOMPARE(cursorChangedSpy.count(), 1);
auto cursor = m_seatInterface->focusedPointer()->cursor();
QVERIFY(cursor);
QVERIFY(!cursor->surface());
QCOMPARE(cursor->hotspot(), QPoint());
QCOMPARE(cursor->enteredSerial(), serial);
QSignalSpy hotspotChangedSpy(cursor, SIGNAL(hotspotChanged()));
QVERIFY(hotspotChangedSpy.isValid());
QSignalSpy surfaceChangedSpy(cursor, SIGNAL(surfaceChanged()));
QVERIFY(surfaceChangedSpy.isValid());
QSignalSpy enteredSerialChangedSpy(cursor, SIGNAL(enteredSerialChanged()));
QVERIFY(enteredSerialChangedSpy.isValid());
QSignalSpy changedSpy(cursor, SIGNAL(changed()));
QVERIFY(changedSpy.isValid());
// test changing hotspot
p->setCursor(nullptr, QPoint(1, 2));
QVERIFY(hotspotChangedSpy.wait());
QCOMPARE(hotspotChangedSpy.count(), 1);
QCOMPARE(changedSpy.count(), 1);
QCOMPARE(cursorChangedSpy.count(), 2);
QCOMPARE(cursor->hotspot(), QPoint(1, 2));
QVERIFY(enteredSerialChangedSpy.isEmpty());
QVERIFY(surfaceChangedSpy.isEmpty());
// set surface
auto cursorSurface = m_compositor->createSurface(m_compositor);
QVERIFY(cursorSurface->isValid());
p->setCursor(cursorSurface, QPoint(1, 2));
QVERIFY(surfaceChangedSpy.wait());
QCOMPARE(surfaceChangedSpy.count(), 1);
QCOMPARE(changedSpy.count(), 2);
QCOMPARE(cursorChangedSpy.count(), 3);
QVERIFY(enteredSerialChangedSpy.isEmpty());
QCOMPARE(cursor->hotspot(), QPoint(1, 2));
QVERIFY(cursor->surface());
// and add an image to the surface
QImage img(QSize(10, 20), QImage::Format_RGB32);
img.fill(Qt::red);
cursorSurface->attachBuffer(m_shm->createBuffer(img));
cursorSurface->damage(QRect(0, 0, 10, 20));
cursorSurface->commit(Surface::CommitFlag::None);
QVERIFY(changedSpy.wait());
QCOMPARE(changedSpy.count(), 3);
QCOMPARE(cursorChangedSpy.count(), 4);
QCOMPARE(surfaceChangedSpy.count(), 1);
QCOMPARE(cursor->surface()->buffer()->data(), img);
p->hideCursor();
QVERIFY(surfaceChangedSpy.wait());
QCOMPARE(changedSpy.count(), 4);
QCOMPARE(cursorChangedSpy.count(), 5);
QCOMPARE(surfaceChangedSpy.count(), 2);
QVERIFY(!cursor->surface());
}
void TestWaylandSeat::testKeyboard()
{
using namespace KWayland::Client;
......@@ -685,6 +795,7 @@ void TestWaylandSeat::testDestroy()
delete m_compositor;
m_compositor = nullptr;
connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy);
connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy);
connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy);
QVERIFY(m_seat->isValid());
......
......@@ -39,8 +39,13 @@ public:
SeatInterface *seat;
SurfaceInterface *focusedSurface = nullptr;
QMetaObject::Connection destroyConnection;
Cursor *cursor = nullptr;
private:
PointerInterface *q_func() {
return reinterpret_cast<PointerInterface *>(q);
}
void setCursor(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot);
// interface
static void setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial,
wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y);
......@@ -50,12 +55,41 @@ private:
static const struct wl_pointer_interface s_interface;
};
class Cursor::Private
{
public:
Private(Cursor *q, PointerInterface *pointer);
PointerInterface *pointer;
quint32 enteredSerial = 0;
QPoint hotspot;
QPointer<SurfaceInterface> surface;
void update(const QPointer<SurfaceInterface> &surface, quint32 serial, const QPoint &hotspot);
private:
Cursor *q;
};
PointerInterface::Private::Private(SeatInterface *parent, wl_resource *parentResource, PointerInterface *q)
: Resource::Private(q, parent, parentResource, &wl_pointer_interface, &s_interface)
, seat(parent)
{
}
void PointerInterface::Private::setCursor(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot)
{
if (!cursor) {
Q_Q(PointerInterface);
cursor = new Cursor(q);
cursor->d->enteredSerial = serial;
cursor->d->hotspot = hotspot;
cursor->d->surface = QPointer<SurfaceInterface>(surface);
QObject::connect(cursor, &Cursor::changed, q, &PointerInterface::cursorChanged);
emit q->cursorChanged();
} else {
cursor->d->update(QPointer<SurfaceInterface>(surface), serial, hotspot);
}
}
const struct wl_pointer_interface PointerInterface::Private::s_interface = {
setCursorCallback,
......@@ -129,13 +163,9 @@ void PointerInterface::axis(Qt::Orientation orientation, quint32 delta)
void PointerInterface::Private::setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial,
wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
{
Q_UNUSED(client)
Q_UNUSED(resource)
Q_UNUSED(serial)
Q_UNUSED(surface)
Q_UNUSED(hotspot_x)
Q_UNUSED(hotspot_y)
// TODO: implement
auto p = cast<Private>(resource);
Q_ASSERT(p->client->client() == client);
p->setCursor(serial, SurfaceInterface::get(surface), QPoint(hotspot_x, hotspot_y));
}
void PointerInterface::Private::releaseCallback(wl_client *client, wl_resource *resource)
......@@ -144,10 +174,79 @@ void PointerInterface::Private::releaseCallback(wl_client *client, wl_resource *
unbind(resource);
}
Cursor *PointerInterface::cursor() const
{
Q_D();
return d->cursor;
}
PointerInterface::Private *PointerInterface::d_func() const
{
return reinterpret_cast<Private*>(d.data());
}
Cursor::Private::Private(Cursor *q, PointerInterface *pointer)
: pointer(pointer)
, q(q)
{
}
void Cursor::Private::update(const QPointer< SurfaceInterface > &s, quint32 serial, const QPoint &p)
{
bool emitChanged = false;
if (enteredSerial != serial) {
enteredSerial = serial;
emitChanged = true;
emit q->enteredSerialChanged();
}
if (hotspot != p) {
hotspot = p;
emitChanged = true;
emit q->hotspotChanged();
}
if (surface != s) {
if (!surface.isNull()) {
QObject::disconnect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed);
}
surface = s;
if (!surface.isNull()) {
QObject::connect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed);
}
emitChanged = true;
emit q->surfaceChanged();
}
if (emitChanged) {
emit q->changed();
}
}
Cursor::Cursor(PointerInterface *parent)
: QObject(parent)
, d(new Private(this, parent))
{
}
Cursor::~Cursor() = default;
quint32 Cursor::enteredSerial() const
{
return d->enteredSerial;
}
QPoint Cursor::hotspot() const
{
return d->hotspot;
}
PointerInterface *Cursor::pointer() const
{
return d->pointer;
}
QPointer< SurfaceInterface > Cursor::surface() const
{
return d->surface;
}
}
}
......@@ -29,6 +29,7 @@ namespace KWayland
namespace Server
{
class Cursor;
class SeatInterface;
class SurfaceInterface;
......@@ -39,6 +40,18 @@ public:
virtual ~PointerInterface();
SurfaceInterface *focusedSurface() const;
/**
* The Cursor set on this PointerInterface. Might be @c null.
* @since 5.3
**/
Cursor *cursor() const;
Q_SIGNALS:
/**
* Signal emitted whenever the Cursor changes.
**/
void cursorChanged();
private:
void setFocusedSurface(SurfaceInterface *surface, quint32 serial);
void buttonPressed(quint32 button, quint32 serial);
......@@ -50,6 +63,46 @@ private:
Private *d_func() const;
};
/**
* @brief Class encapsulating a Cursor image.
*
* @since 5.3
**/
class KWAYLANDSERVER_EXPORT Cursor : public QObject
{
Q_OBJECT
public:
virtual ~Cursor();
/**
* The hotspot of the cursor image in surface-relative coordinates.
**/
QPoint hotspot() const;
/**
* The entered serial when the Cursor got set.
**/
quint32 enteredSerial() const;
/**
* The PointerInterface this Cursor belongs to.
**/
PointerInterface *pointer() const;
/**
* The SurfaceInterface for the image content of the Cursor.
**/
QPointer<SurfaceInterface> surface() const;
Q_SIGNALS:
void hotspotChanged();
void enteredSerialChanged();
void surfaceChanged();
void changed();
private:
friend class PointerInterface;
Cursor(PointerInterface *parent);
class Private;
const QScopedPointer<Private> d;
};
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment