1 /****************************************************************************
2 Copyright 2017 Marco Martin <notmart@gmail.com>
3 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 ****************************************************************************/
21 #include "xdg_foreign.h"
22
23 #include "xdg_foreign_v2_p.h"
24
25 #include "display.h"
26 #include "surface_p.h"
27
28 #include "wayland/global.h"
29
30 #include "wayland-xdg-foreign-unstable-v2-server-protocol.h"
31
32 #include <QUuid>
33
34 namespace Wrapland::Server
35 {
36
37 constexpr uint32_t XdgExporterV2Version = 1;
38 using XdgExporterV2Global = Wayland::Global<XdgExporterV2, XdgExporterV2Version>;
39 using XdgExporterV2Bind = Wayland::Bind<XdgExporterV2Global>;
40
41 class Q_DECL_HIDDEN XdgExporterV2::Private
42 : public Wayland::Global<XdgExporterV2, XdgExporterV2Version>
43 {
44 public:
45 Private(XdgExporterV2* q, Display* display);
46
47 QHash<QString, XdgExportedV2*> exportedSurfaces;
48
49 private:
50 static void
51 exportToplevelCallback(XdgExporterV2Bind* bind, uint32_t id, wl_resource* wlSurface);
52
53 static const struct zxdg_exporter_v2_interface s_interface;
54 };
55
Private(XdgExporterV2 * q,Display * display)56 XdgExporterV2::Private::Private(XdgExporterV2* q, Display* display)
57 : Wayland::Global<XdgExporterV2, XdgExporterV2Version>(q,
58 display,
59 &zxdg_exporter_v2_interface,
60 &s_interface)
61 {
62 }
63
64 const struct zxdg_exporter_v2_interface XdgExporterV2::Private::s_interface = {
65 resourceDestroyCallback,
66 cb<exportToplevelCallback>,
67 };
68
exportedSurface(const QString & handle)69 XdgExportedV2* XdgExporterV2::exportedSurface(const QString& handle)
70 {
71 auto it = d_ptr->exportedSurfaces.constFind(handle);
72 if (it != d_ptr->exportedSurfaces.constEnd()) {
73 return it.value();
74 }
75 return nullptr;
76 }
exportToplevelCallback(XdgExporterV2Bind * bind,uint32_t id,wl_resource * wlSurface)77 void XdgExporterV2::Private::exportToplevelCallback(XdgExporterV2Bind* bind,
78 uint32_t id,
79 wl_resource* wlSurface)
80 {
81 auto priv = bind->global()->handle()->d_ptr;
82
83 auto surface = Wayland::Resource<Surface>::handle(wlSurface);
84
85 const QString protocolHandle = QUuid::createUuid().toString();
86
87 auto exported = new XdgExportedV2(bind->client()->handle(),
88 wl_resource_get_version(bind->resource()),
89 id,
90 surface,
91 protocolHandle);
92 // TODO(romangg): error handling
93
94 // Surface not exported anymore.
95 connect(exported, &XdgExportedV2::destroyed, priv->handle(), [priv, protocolHandle] {
96 priv->exportedSurfaces.remove(protocolHandle);
97 });
98
99 priv->exportedSurfaces[protocolHandle] = exported;
100 }
101
XdgExporterV2(Display * display,QObject * parent)102 XdgExporterV2::XdgExporterV2(Display* display, QObject* parent)
103 : QObject(parent)
104 , d_ptr(new Private(this, display))
105 {
106 d_ptr->create();
107 }
108
~XdgExporterV2()109 XdgExporterV2::~XdgExporterV2()
110 {
111 delete d_ptr;
112 }
113
114 constexpr uint32_t XdgImporterV2Version = 1;
115
116 class Q_DECL_HIDDEN XdgImporterV2::Private
117 : public Wayland::Global<XdgImporterV2, XdgImporterV2Version>
118 {
119 public:
120 Private(XdgImporterV2* q, Display* display);
121
122 void parentChange(XdgImportedV2* imported, XdgExportedV2* exported);
123
124 XdgExporterV2* exporter = nullptr;
125
126 // Child -> parent hash
127 QHash<Surface*, Surface*> parents;
128
129 private:
130 void childChange(Surface* parent, Surface* prevChild, Surface* nextChild);
131
132 static void importToplevelCallback(wl_client* wlClient,
133 wl_resource* wlResource,
134 uint32_t id,
135 const char* handle);
136
137 static const struct zxdg_importer_v2_interface s_interface;
138 };
139
140 const struct zxdg_importer_v2_interface XdgImporterV2::Private::s_interface = {
141 resourceDestroyCallback,
142 importToplevelCallback,
143 };
144
importToplevelCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t id,const char * handle)145 void XdgImporterV2::Private::importToplevelCallback([[maybe_unused]] wl_client* wlClient,
146 wl_resource* wlResource,
147 uint32_t id,
148 const char* handle)
149 {
150 auto importerHandle = XdgImporterV2::Private::handle(wlResource);
151 auto bind = importerHandle->d_ptr->getBind(wlResource);
152 auto client = bind->client()->handle();
153
154 if (!importerHandle->d_ptr->exporter) {
155 importerHandle->d_ptr->send<zxdg_imported_v2_send_destroyed>();
156 return;
157 }
158
159 auto* exported = importerHandle->d_ptr->exporter->exportedSurface(QString::fromUtf8(handle));
160 if (!exported) {
161 importerHandle->d_ptr->send<zxdg_imported_v2_send_destroyed>();
162 return;
163 }
164
165 if (!exported->surface()) {
166 importerHandle->d_ptr->send<zxdg_imported_v2_send_destroyed>();
167 return;
168 }
169
170 auto imported = new XdgImportedV2(client, bind->version(), id, exported);
171 // TODO(romangg): error handling
172
173 connect(imported,
174 &XdgImportedV2::childChanged,
175 importerHandle,
176 [importerHandle](Surface* parent, Surface* prevChild, Surface* nextChild) {
177 importerHandle->d_ptr->childChange(parent, prevChild, nextChild);
178 });
179 }
180
Private(XdgImporterV2 * q,Display * display)181 XdgImporterV2::Private::Private(XdgImporterV2* q, Display* display)
182 : Wayland::Global<XdgImporterV2, XdgImporterV2Version>(q,
183 display,
184 &zxdg_importer_v2_interface,
185 &s_interface)
186 {
187 }
188
childChange(Surface * parent,Surface * prevChild,Surface * nextChild)189 void XdgImporterV2::Private::childChange(Surface* parent, Surface* prevChild, Surface* nextChild)
190 {
191 if (prevChild) {
192 int removed = parents.remove(prevChild);
193 Q_ASSERT(removed);
194 }
195 if (nextChild && parent) {
196 parents[nextChild] = parent;
197 }
198
199 Q_EMIT handle()->parentChanged(parent, nextChild);
200 }
201
XdgImporterV2(Display * display,QObject * parent)202 XdgImporterV2::XdgImporterV2(Display* display, QObject* parent)
203 : QObject(parent)
204 , d_ptr(new Private(this, display))
205 {
206 d_ptr->create();
207 }
208
~XdgImporterV2()209 XdgImporterV2::~XdgImporterV2()
210 {
211 delete d_ptr;
212 }
213
setExporter(XdgExporterV2 * exporter)214 void XdgImporterV2::setExporter(XdgExporterV2* exporter)
215 {
216 d_ptr->exporter = exporter;
217 }
218
parentOf(Surface * surface)219 Surface* XdgImporterV2::parentOf(Surface* surface)
220 {
221 if (!d_ptr->parents.contains(surface)) {
222 return nullptr;
223 }
224 return d_ptr->parents[surface];
225 }
226
227 class Q_DECL_HIDDEN XdgExportedV2::Private : public Wayland::Resource<XdgExportedV2>
228 {
229 public:
230 Private(Client* client, uint32_t version, uint32_t id, Surface* surface, XdgExportedV2* q);
231
232 Surface* exportedSurface;
233
234 private:
235 static const struct zxdg_exported_v2_interface s_interface;
236 };
237
238 const struct zxdg_exported_v2_interface XdgExportedV2::Private::s_interface = {destroyCallback};
239
XdgExportedV2(Client * client,uint32_t version,uint32_t id,Surface * surface,const QString & protocolHandle)240 XdgExportedV2::XdgExportedV2(Client* client,
241 uint32_t version,
242 uint32_t id,
243 Surface* surface,
244 const QString& protocolHandle)
245 : QObject(nullptr)
246 , d_ptr(new Private(client, version, id, surface, this))
247 {
248 d_ptr->send<zxdg_exported_v2_send_handle>(protocolHandle.toUtf8().constData());
249 }
250
surface() const251 Surface* XdgExportedV2::surface() const
252 {
253 return d_ptr->exportedSurface;
254 }
255
Private(Client * client,uint32_t version,uint32_t id,Surface * surface,XdgExportedV2 * q)256 XdgExportedV2::Private::Private(Client* client,
257 uint32_t version,
258 uint32_t id,
259 Surface* surface,
260 XdgExportedV2* q)
261 : Wayland::Resource<XdgExportedV2>(client,
262 version,
263 id,
264 &zxdg_exported_v2_interface,
265 &s_interface,
266 q)
267 , exportedSurface(surface)
268 {
269 }
270
271 class Q_DECL_HIDDEN XdgImportedV2::Private : public Wayland::Resource<XdgImportedV2>
272 {
273 public:
274 Private(Client* client,
275 uint32_t version,
276 uint32_t id,
277 XdgExportedV2* exported,
278 XdgImportedV2* q);
279
280 void setChild(Surface* surface);
281
282 XdgExportedV2* source = nullptr;
283 Surface* child = nullptr;
284
285 private:
286 static void
287 setParentOfCallback(wl_client* wlClient, wl_resource* wlResource, wl_resource* wlSurface);
288
289 static const struct zxdg_imported_v2_interface s_interface;
290 };
291
292 const struct zxdg_imported_v2_interface XdgImportedV2::Private::s_interface = {
293 destroyCallback,
294 setParentOfCallback,
295 };
296
XdgImportedV2(Client * client,uint32_t version,uint32_t id,XdgExportedV2 * exported)297 XdgImportedV2::XdgImportedV2(Client* client, uint32_t version, uint32_t id, XdgExportedV2* exported)
298 : QObject(nullptr)
299 , d_ptr(new Private(client, version, id, exported, this))
300 {
301 connect(
302 exported->surface(), &Surface::resourceDestroyed, this, &XdgImportedV2::onSourceDestroy);
303 connect(exported, &XdgExportedV2::resourceDestroyed, this, &XdgImportedV2::onSourceDestroy);
304 }
305
~XdgImportedV2()306 XdgImportedV2::~XdgImportedV2()
307 {
308 if (source()) {
309 d_ptr->setChild(nullptr);
310 }
311 }
312
child() const313 Surface* XdgImportedV2::child() const
314 {
315 return d_ptr->child;
316 }
317
source() const318 XdgExportedV2* XdgImportedV2::source() const
319 {
320 return d_ptr->source;
321 }
322
onSourceDestroy()323 void XdgImportedV2::onSourceDestroy()
324 {
325 // Export no longer available.
326 d_ptr->source = nullptr;
327 d_ptr->setChild(child());
328 d_ptr->send<zxdg_imported_v2_send_destroyed>();
329 }
330
setParentOfCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlSurface)331 void XdgImportedV2::Private::setParentOfCallback([[maybe_unused]] wl_client* wlClient,
332 wl_resource* wlResource,
333 wl_resource* wlSurface)
334 {
335 auto priv = handle(wlResource)->d_ptr;
336 auto surface = Wayland::Resource<Surface>::handle(wlSurface);
337
338 // Guaranteed by libwayland (?).
339 Q_ASSERT(surface);
340
341 if (priv->child != surface) {
342 // Only set on change. We do the check here so the setChild function can be reused
343 // for setting the same child when the exporting source goes away.
344 priv->setChild(surface);
345 }
346 }
347
setChild(Surface * surface)348 void XdgImportedV2::Private::setChild(Surface* surface)
349 {
350 auto* prevChild = child;
351 if (prevChild) {
352 disconnect(prevChild, &Surface::resourceDestroyed, handle(), nullptr);
353 }
354
355 child = surface;
356
357 connect(surface, &Surface::resourceDestroyed, handle(), [this] {
358 // Child surface is destroyed, this means relation is cancelled.
359 Q_EMIT handle()->childChanged(source->surface(), this->child, nullptr);
360 this->child = nullptr;
361 });
362
363 Q_EMIT handle()->childChanged(source ? source->surface() : nullptr, prevChild, surface);
364 }
365
Private(Client * client,uint32_t version,uint32_t id,XdgExportedV2 * exported,XdgImportedV2 * q)366 XdgImportedV2::Private::Private(Client* client,
367 uint32_t version,
368 uint32_t id,
369 XdgExportedV2* exported,
370 XdgImportedV2* q)
371 : Wayland::Resource<XdgImportedV2>(client,
372 version,
373 id,
374 &zxdg_imported_v2_interface,
375 &s_interface,
376 q)
377 , source(exported)
378 {
379 }
380
381 }
382