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/datadevice.h"
9#include "../src/client/datadevicemanager.h"
10#include "../src/client/dataoffer.h"
11#include "../src/client/event_queue.h"
12#include "../src/client/keyboard.h"
13#include "../src/client/plasmashell.h"
14#include "../src/client/plasmawindowmanagement.h"
15#include "../src/client/pointer.h"
16#include "../src/client/registry.h"
17#include "../src/client/seat.h"
18#include "../src/client/shell.h"
19#include "../src/client/shm_pool.h"
20#include "../src/client/surface.h"
21// Qt
22#include <QDebug>
23#include <QFile>
24#include <QGuiApplication>
25#include <QImage>
26#include <QMimeType>
27#include <QThread>
28// system
29#include <unistd.h>
30
31#include <linux/input.h>
32
33using namespace KWayland::Client;
34
35class PanelTest : public QObject
36{
37 Q_OBJECT
38public:
39 explicit PanelTest(QObject *parent = nullptr);
40 ~PanelTest() override;
41
42 void init();
43
44private:
45 void setupRegistry(Registry *registry);
46 void render();
47 void showTooltip(const QPointF &pos);
48 void hideTooltip();
49 void moveTooltip(const QPointF &pos);
50 QThread *m_connectionThread;
51 ConnectionThread *m_connectionThreadObject;
52 EventQueue *m_eventQueue = nullptr;
53 Compositor *m_compositor = nullptr;
54 Seat *m_seat = nullptr;
55 Shell *m_shell = nullptr;
56 ShellSurface *m_shellSurface = nullptr;
57 ShmPool *m_shm = nullptr;
58 Surface *m_surface = nullptr;
59 PlasmaShell *m_plasmaShell = nullptr;
60 PlasmaShellSurface *m_plasmaShellSurface = nullptr;
61 PlasmaWindowManagement *m_windowManagement = nullptr;
62 struct {
63 Surface *surface = nullptr;
64 ShellSurface *shellSurface = nullptr;
65 PlasmaShellSurface *plasmaSurface = nullptr;
66 bool visible = false;
67 } m_tooltip;
68};
69
70PanelTest::PanelTest(QObject *parent)
71 : QObject(parent)
72 , m_connectionThread(new QThread(this))
73 , m_connectionThreadObject(new ConnectionThread())
74{
75}
76
77PanelTest::~PanelTest()
78{
79 m_connectionThread->quit();
80 m_connectionThread->wait();
81 m_connectionThreadObject->deleteLater();
82}
83
84void PanelTest::init()
85{
86 connect(
87 sender: m_connectionThreadObject,
88 signal: &ConnectionThread::connected,
89 context: this,
90 slot: [this] {
91 m_eventQueue = new EventQueue(this);
92 m_eventQueue->setup(m_connectionThreadObject);
93
94 Registry *registry = new Registry(this);
95 setupRegistry(registry);
96 },
97 type: Qt::QueuedConnection);
98 m_connectionThreadObject->moveToThread(thread: m_connectionThread);
99 m_connectionThread->start();
100
101 m_connectionThreadObject->initConnection();
102}
103
104void PanelTest::showTooltip(const QPointF &pos)
105{
106 if (!m_tooltip.surface) {
107 m_tooltip.surface = m_compositor->createSurface(parent: this);
108 m_tooltip.shellSurface = m_shell->createSurface(surface: m_tooltip.surface, parent: this);
109 if (m_plasmaShell) {
110 m_tooltip.plasmaSurface = m_plasmaShell->createSurface(surface: m_tooltip.surface, parent: this);
111 }
112 }
113 m_tooltip.shellSurface->setTransient(parent: m_surface, offset: pos.toPoint());
114
115 if (!m_tooltip.visible) {
116 const QSize size(100, 50);
117 auto buffer = m_shm->getBuffer(size, stride: size.width() * 4).toStrongRef();
118 buffer->setUsed(true);
119 QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
120 image.fill(color: Qt::red);
121
122 m_tooltip.surface->attachBuffer(buffer: *buffer);
123 m_tooltip.surface->damage(rect: QRect(QPoint(0, 0), size));
124 m_tooltip.surface->commit(flag: Surface::CommitFlag::None);
125 m_tooltip.visible = true;
126 }
127}
128
129void PanelTest::hideTooltip()
130{
131 if (!m_tooltip.visible) {
132 return;
133 }
134 m_tooltip.surface->attachBuffer(buffer: Buffer::Ptr());
135 m_tooltip.surface->commit(flag: Surface::CommitFlag::None);
136 m_tooltip.visible = false;
137}
138
139void PanelTest::moveTooltip(const QPointF &pos)
140{
141 if (m_tooltip.plasmaSurface) {
142 m_tooltip.plasmaSurface->setPosition(QPoint(10, 0) + pos.toPoint());
143 }
144}
145
146void PanelTest::setupRegistry(Registry *registry)
147{
148 connect(sender: registry, signal: &Registry::compositorAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
149 m_compositor = registry->createCompositor(name, version, parent: this);
150 });
151 connect(sender: registry, signal: &Registry::shellAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
152 m_shell = registry->createShell(name, version, parent: this);
153 });
154 connect(sender: registry, signal: &Registry::shmAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
155 m_shm = registry->createShmPool(name, version, parent: this);
156 });
157 connect(sender: registry, signal: &Registry::seatAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
158 m_seat = registry->createSeat(name, version, parent: this);
159 connect(sender: m_seat, signal: &Seat::hasPointerChanged, context: this, slot: [this](bool has) {
160 if (!has) {
161 return;
162 }
163 auto p = m_seat->createPointer(parent: this);
164 connect(sender: p, signal: &Pointer::buttonStateChanged, context: this, slot: [this](quint32 serial, quint32 time, quint32 button, KWayland::Client::Pointer::ButtonState state) {
165 Q_UNUSED(time)
166 Q_UNUSED(serial)
167 if (!m_windowManagement) {
168 return;
169 }
170 if (state == Pointer::ButtonState::Released) {
171 return;
172 }
173 if (button == BTN_LEFT) {
174 m_windowManagement->showDesktop();
175 } else if (button == BTN_RIGHT) {
176 m_windowManagement->hideDesktop();
177 }
178 });
179 connect(sender: p, signal: &Pointer::entered, context: this, slot: [this, p](quint32 serial, const QPointF &relativeToSurface) {
180 Q_UNUSED(serial)
181 if (p->enteredSurface() == m_surface) {
182 showTooltip(pos: relativeToSurface);
183 }
184 });
185 connect(sender: p, signal: &Pointer::motion, context: this, slot: [this, p](const QPointF &relativeToSurface) {
186 if (p->enteredSurface() == m_surface) {
187 moveTooltip(pos: relativeToSurface);
188 }
189 });
190 connect(sender: p, signal: &Pointer::left, context: this, slot: [this] {
191 hideTooltip();
192 });
193 });
194 });
195 connect(sender: registry, signal: &Registry::plasmaShellAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
196 m_plasmaShell = registry->createPlasmaShell(name, version, parent: this);
197 });
198 connect(sender: registry, signal: &Registry::plasmaWindowManagementAnnounced, context: this, slot: [this, registry](quint32 name, quint32 version) {
199 m_windowManagement = registry->createPlasmaWindowManagement(name, version, parent: this);
200 connect(sender: m_windowManagement, signal: &PlasmaWindowManagement::showingDesktopChanged, context: this, slot: [](bool set) {
201 qDebug() << "Showing desktop changed, new state: " << set;
202 });
203 connect(sender: m_windowManagement, signal: &PlasmaWindowManagement::windowCreated, context: this, slot: [this](PlasmaWindow *w) {
204 connect(sender: w, signal: &PlasmaWindow::titleChanged, context: this, slot: [w] {
205 qDebug() << "Window title changed to: " << w->title();
206 });
207 connect(sender: w, signal: &PlasmaWindow::activeChanged, context: this, slot: [w] {
208 qDebug() << "Window active changed: " << w->isActive();
209 });
210 connect(sender: w, signal: &PlasmaWindow::maximizedChanged, context: this, slot: [w] {
211 qDebug() << "Window maximized changed: " << w->isMaximized();
212 });
213 connect(sender: w, signal: &PlasmaWindow::maximizedChanged, context: this, slot: [w] {
214 qDebug() << "Window minimized changed: " << w->isMinimized();
215 });
216 connect(sender: w, signal: &PlasmaWindow::keepAboveChanged, context: this, slot: [w] {
217 qDebug() << "Window keep above changed: " << w->isKeepAbove();
218 });
219 connect(sender: w, signal: &PlasmaWindow::keepBelowChanged, context: this, slot: [w] {
220 qDebug() << "Window keep below changed: " << w->isKeepBelow();
221 });
222 connect(sender: w, signal: &PlasmaWindow::onAllDesktopsChanged, context: this, slot: [w] {
223 qDebug() << "Window on all desktops changed: " << w->isOnAllDesktops();
224 });
225 connect(sender: w, signal: &PlasmaWindow::fullscreenChanged, context: this, slot: [w] {
226 qDebug() << "Window full screen changed: " << w->isFullscreen();
227 });
228 connect(sender: w, signal: &PlasmaWindow::demandsAttentionChanged, context: this, slot: [w] {
229 qDebug() << "Window demands attention changed: " << w->isDemandingAttention();
230 });
231 connect(sender: w, signal: &PlasmaWindow::closeableChanged, context: this, slot: [w] {
232 qDebug() << "Window is closeable changed: " << w->isCloseable();
233 });
234 connect(sender: w, signal: &PlasmaWindow::minimizeableChanged, context: this, slot: [w] {
235 qDebug() << "Window is minimizeable changed: " << w->isMinimizeable();
236 });
237 connect(sender: w, signal: &PlasmaWindow::maximizeableChanged, context: this, slot: [w] {
238 qDebug() << "Window is maximizeable changed: " << w->isMaximizeable();
239 });
240 connect(sender: w, signal: &PlasmaWindow::fullscreenableChanged, context: this, slot: [w] {
241 qDebug() << "Window is fullscreenable changed: " << w->isFullscreenable();
242 });
243 connect(sender: w, signal: &PlasmaWindow::iconChanged, context: this, slot: [w] {
244 qDebug() << "Window icon changed: " << w->icon().name();
245 });
246 });
247 });
248 connect(sender: registry, signal: &Registry::interfacesAnnounced, context: this, slot: [this] {
249 Q_ASSERT(m_compositor);
250 Q_ASSERT(m_seat);
251 Q_ASSERT(m_shell);
252 Q_ASSERT(m_shm);
253 m_surface = m_compositor->createSurface(parent: this);
254 Q_ASSERT(m_surface);
255 m_shellSurface = m_shell->createSurface(surface: m_surface, parent: this);
256 Q_ASSERT(m_shellSurface);
257 m_shellSurface->setToplevel();
258 connect(sender: m_shellSurface, signal: &ShellSurface::sizeChanged, context: this, slot: &PanelTest::render);
259 if (m_plasmaShell) {
260 m_plasmaShellSurface = m_plasmaShell->createSurface(surface: m_surface, parent: this);
261 m_plasmaShellSurface->setPosition(QPoint(10, 0));
262 m_plasmaShellSurface->setRole(PlasmaShellSurface::Role::Panel);
263 }
264 render();
265 });
266 registry->setEventQueue(m_eventQueue);
267 registry->create(connection: m_connectionThreadObject);
268 registry->setup();
269}
270
271void PanelTest::render()
272{
273 const QSize &size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(300, 20);
274 auto buffer = m_shm->getBuffer(size, stride: size.width() * 4).toStrongRef();
275 buffer->setUsed(true);
276 QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
277 image.fill(color: Qt::blue);
278
279 m_surface->attachBuffer(buffer: *buffer);
280 m_surface->damage(rect: QRect(QPoint(0, 0), size));
281 m_surface->commit(flag: Surface::CommitFlag::None);
282 buffer->setUsed(false);
283}
284
285int main(int argc, char **argv)
286{
287 QGuiApplication app(argc, argv);
288 PanelTest client;
289 client.init();
290
291 return app.exec();
292}
293
294#include "paneltest.moc"
295

source code of kwayland/tests/paneltest.cpp