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