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 | |
23 | using namespace KWayland::Client; |
24 | |
25 | class XdgTest : public QObject |
26 | { |
27 | Q_OBJECT |
28 | public: |
29 | explicit XdgTest(QObject *parent = nullptr); |
30 | ~XdgTest() override; |
31 | |
32 | void init(); |
33 | |
34 | private: |
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 * = nullptr; |
48 | XdgShellPopup * = nullptr; |
49 | }; |
50 | |
51 | XdgTest::XdgTest(QObject *parent) |
52 | : QObject(parent) |
53 | , m_connectionThread(new QThread(this)) |
54 | , m_connectionThreadObject(new ConnectionThread()) |
55 | { |
56 | } |
57 | |
58 | XdgTest::~XdgTest() |
59 | { |
60 | m_connectionThread->quit(); |
61 | m_connectionThread->wait(); |
62 | m_connectionThreadObject->deleteLater(); |
63 | } |
64 | |
65 | void 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 | |
85 | void 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 | |
147 | void XdgTest::() |
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 | |
163 | void 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 | |
182 | void XdgTest::() |
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 | |
196 | int 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 | |