1/*
2 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "../src/client/compositor.h"
7#include "../src/client/connection_thread.h"
8#include "../src/client/event_queue.h"
9#include "../src/client/pointer.h"
10#include "../src/client/registry.h"
11#include "../src/client/seat.h"
12#include "../src/client/shadow.h"
13#include "../src/client/shell.h"
14#include "../src/client/shm_pool.h"
15#include "../src/client/surface.h"
16#include "../src/client/xdgshell.h"
17// Qt
18#include <QGuiApplication>
19#include <QImage>
20#include <QPainter>
21#include <QThread>
22
23using namespace KWayland::Client;
24
25class XdgTest : public QObject
26{
27 Q_OBJECT
28public:
29 explicit XdgTest(QObject *parent = nullptr);
30 ~XdgTest() override;
31
32 void init();
33
34private:
35 void setupRegistry(Registry *registry);
36 void createPopup();
37 void render();
38 void renderPopup();
39 QThread *m_connectionThread;
40 ConnectionThread *m_connectionThreadObject;
41 EventQueue *m_eventQueue = nullptr;
42 Compositor *m_compositor = nullptr;
43 ShmPool *m_shm = nullptr;
44 Surface *m_surface = nullptr;
45 XdgShell *m_xdgShell = nullptr;
46 XdgShellSurface *m_xdgShellSurface = nullptr;
47 Surface *m_popupSurface = nullptr;
48 XdgShellPopup *m_xdgShellPopup = nullptr;
49};
50
51XdgTest::XdgTest(QObject *parent)
52 : QObject(parent)
53 , m_connectionThread(new QThread(this))
54 , m_connectionThreadObject(new ConnectionThread())
55{
56}
57
58XdgTest::~XdgTest()
59{
60 m_connectionThread->quit();
61 m_connectionThread->wait();
62 m_connectionThreadObject->deleteLater();
63}
64
65void XdgTest::init()
66{
67 connect(
68 sender: m_connectionThreadObject,
69 signal: &ConnectionThread::connected,
70 context: this,
71 slot: [this] {
72 m_eventQueue = new EventQueue(this);
73 m_eventQueue->setup(m_connectionThreadObject);
74
75 Registry *registry = new Registry(this);
76 setupRegistry(registry);
77 },
78 type: Qt::QueuedConnection);
79 m_connectionThreadObject->moveToThread(thread: m_connectionThread);
80 m_connectionThread->start();
81
82 m_connectionThreadObject->initConnection();
83}
84
85void XdgTest::setupRegistry(Registry *registry)
86{
87 connect(sender: registry, signal: &Registry::compositorAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
88 m_compositor = registry->createCompositor(name, version, parent: this);
89 });
90 connect(sender: registry, signal: &Registry::shmAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
91 m_shm = registry->createShmPool(name, version, parent: this);
92 });
93 connect(sender: registry, signal: &Registry::xdgShellStableAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
94 m_xdgShell = registry->createXdgShell(name, version, parent: this);
95 m_xdgShell->setEventQueue(m_eventQueue);
96 });
97 connect(sender: registry, signal: &Registry::interfacesAnnounced, context: this, slot: [this] {
98 Q_ASSERT(m_compositor);
99 Q_ASSERT(m_xdgShell);
100 Q_ASSERT(m_shm);
101 m_surface = m_compositor->createSurface(parent: this);
102 Q_ASSERT(m_surface);
103 m_xdgShellSurface = m_xdgShell->createSurface(surface: m_surface, parent: this);
104 Q_ASSERT(m_xdgShellSurface);
105 connect(sender: m_xdgShellSurface,
106 signal: &XdgShellSurface::configureRequested,
107 context: this,
108 slot: [this](const QSize &size, KWayland::Client::XdgShellSurface::States states, int serial) {
109 Q_UNUSED(size);
110 Q_UNUSED(states);
111 m_xdgShellSurface->ackConfigure(serial);
112 render();
113 });
114
115 m_xdgShellSurface->setTitle(QStringLiteral("Test Window"));
116
117 m_surface->commit();
118 });
119 connect(sender: registry, signal: &Registry::seatAnnounced, context: this, slot: [this, registry](quint32 name) {
120 Seat *s = registry->createSeat(name, version: 2, parent: this);
121 connect(sender: s, signal: &Seat::hasPointerChanged, context: this, slot: [this, s](bool has) {
122 if (!has) {
123 return;
124 }
125 Pointer *p = s->createPointer(parent: this);
126 connect(sender: p, signal: &Pointer::buttonStateChanged, context: this, slot: [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) {
127 Q_UNUSED(button)
128 Q_UNUSED(serial)
129 Q_UNUSED(time)
130 if (state == Pointer::ButtonState::Released) {
131 if (m_popupSurface) {
132 m_popupSurface->deleteLater();
133 m_popupSurface = nullptr;
134 } else {
135 createPopup();
136 }
137 }
138 });
139 });
140 });
141
142 registry->setEventQueue(m_eventQueue);
143 registry->create(connection: m_connectionThreadObject);
144 registry->setup();
145}
146
147void XdgTest::createPopup()
148{
149 if (m_popupSurface) {
150 m_popupSurface->deleteLater();
151 }
152
153 m_popupSurface = m_compositor->createSurface(parent: this);
154
155 XdgPositioner positioner(QSize(200, 200), QRect(50, 50, 400, 400));
156 positioner.setAnchorEdge(Qt::BottomEdge | Qt::RightEdge);
157 positioner.setGravity(Qt::BottomEdge);
158 positioner.setConstraints(XdgPositioner::Constraint::FlipX | XdgPositioner::Constraint::SlideY);
159 m_xdgShellPopup = m_xdgShell->createPopup(surface: m_popupSurface, parentSurface: m_xdgShellSurface, positioner, parent: m_popupSurface);
160 renderPopup();
161}
162
163void XdgTest::render()
164{
165 const QSize &size = m_xdgShellSurface->size().isValid() ? m_xdgShellSurface->size() : QSize(500, 500);
166 auto buffer = m_shm->getBuffer(size, stride: size.width() * 4).toStrongRef();
167 buffer->setUsed(true);
168 QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
169 image.fill(color: QColor(255, 255, 255, 255));
170 // draw a red rectangle indicating the anchor of the top level
171 QPainter painter(&image);
172 painter.setBrush(Qt::red);
173 painter.setPen(Qt::black);
174 painter.drawRect(x: 50, y: 50, w: 400, h: 400);
175
176 m_surface->attachBuffer(buffer: *buffer);
177 m_surface->damage(rect: QRect(QPoint(0, 0), size));
178 m_surface->commit(flag: Surface::CommitFlag::None);
179 buffer->setUsed(false);
180}
181
182void XdgTest::renderPopup()
183{
184 QSize size(200, 200);
185 auto buffer = m_shm->getBuffer(size, stride: size.width() * 4).toStrongRef();
186 buffer->setUsed(true);
187 QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
188 image.fill(color: QColor(0, 0, 255, 255));
189
190 m_popupSurface->attachBuffer(buffer: *buffer);
191 m_popupSurface->damage(rect: QRect(QPoint(0, 0), size));
192 m_popupSurface->commit(flag: Surface::CommitFlag::None);
193 buffer->setUsed(false);
194}
195
196int main(int argc, char **argv)
197{
198 QCoreApplication app(argc, argv);
199 XdgTest client;
200 client.init();
201
202 return app.exec();
203}
204
205#include "xdgtest.moc"
206

source code of kwayland/tests/xdgtest.cpp