1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qwaylandxdgshell.h"
5#include "qwaylandxdgshell_p.h"
6
7#if QT_CONFIG(wayland_compositor_quick)
8#include "qwaylandxdgshellintegration_p.h"
9#endif
10#include <QtWaylandCompositor/private/qwaylandutils_p.h>
11
12#include "qwaylandxdgdialogv1_p.h"
13
14#include <QtWaylandCompositor/QWaylandCompositor>
15#include <QtWaylandCompositor/QWaylandSeat>
16#include <QtWaylandCompositor/QWaylandSurface>
17#include <QtWaylandCompositor/QWaylandSurfaceRole>
18#include <QtWaylandCompositor/QWaylandResource>
19
20#include <QtCore/QObject>
21
22#include <algorithm>
23
24QT_BEGIN_NAMESPACE
25
26QWaylandXdgShellPrivate::QWaylandXdgShellPrivate()
27{
28}
29
30void QWaylandXdgShellPrivate::ping(QtWaylandServer::xdg_wm_base::Resource *resource, uint32_t serial)
31{
32 m_pings.insert(value: serial);
33 send_ping(resource->handle, serial);
34}
35
36void QWaylandXdgShellPrivate::registerXdgSurface(QWaylandXdgSurface *xdgSurface)
37{
38 m_xdgSurfaces.insert(key: xdgSurface->surface()->client()->client(), value: xdgSurface);
39}
40
41void QWaylandXdgShellPrivate::unregisterXdgSurface(QWaylandXdgSurface *xdgSurface)
42{
43 auto xdgSurfacePrivate = QWaylandXdgSurfacePrivate::get(xdgSurface);
44 if (!m_xdgSurfaces.remove(xdgSurfacePrivate->resource()->client(), xdgSurface))
45 qWarning(msg: "%s Unexpected state. Can't find registered xdg surface\n", Q_FUNC_INFO);
46}
47
48QWaylandXdgSurface *QWaylandXdgShellPrivate::xdgSurfaceFromSurface(QWaylandSurface *surface)
49{
50 for (QWaylandXdgSurface *xdgSurface : std::as_const(t&: m_xdgSurfaces)) {
51 if (surface == xdgSurface->surface())
52 return xdgSurface;
53 }
54 return nullptr;
55}
56
57void QWaylandXdgShellPrivate::xdg_wm_base_destroy(Resource *resource)
58{
59 if (!m_xdgSurfaces.values(resource->client()).empty())
60 wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES,
61 "xdg_shell was destroyed before children");
62
63 wl_resource_destroy(resource->handle);
64}
65
66void QWaylandXdgShellPrivate::xdg_wm_base_create_positioner(QtWaylandServer::xdg_wm_base::Resource *resource, uint32_t id)
67{
68 QWaylandResource positionerResource(wl_resource_create(resource->client(), &xdg_positioner_interface,
69 wl_resource_get_version(resource->handle), id));
70
71 new QWaylandXdgPositioner(positionerResource);
72}
73
74void QWaylandXdgShellPrivate::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surfaceResource)
75{
76 Q_Q(QWaylandXdgShell);
77 QWaylandSurface *surface = QWaylandSurface::fromResource(resource: surfaceResource);
78
79 if (surface->role() != nullptr) {
80 wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_ROLE,
81 "wl_surface@%d, already has role %s\n",
82 wl_resource_get_id(surface->resource()),
83 surface->role()->name().constData());
84 return;
85 }
86
87 if (surface->hasContent()) {
88 //TODO: According to the spec, this is a client error, but there's no appropriate error code
89 qWarning() << "get_xdg_surface requested on a xdg_surface with content";
90 }
91
92 QWaylandResource xdgSurfaceResource(wl_resource_create(resource->client(), &xdg_surface_interface,
93 wl_resource_get_version(resource->handle), id));
94
95 QWaylandXdgSurface *xdgSurface = new QWaylandXdgSurface(q, surface, xdgSurfaceResource);
96
97 registerXdgSurface(xdgSurface);
98 emit q->xdgSurfaceCreated(xdgSurface);
99}
100
101void QWaylandXdgShellPrivate::xdg_wm_base_pong(Resource *resource, uint32_t serial)
102{
103 Q_UNUSED(resource);
104 Q_Q(QWaylandXdgShell);
105 if (m_pings.remove(value: serial))
106 emit q->pong(serial);
107 else
108 qWarning(msg: "Received an unexpected pong!");
109}
110
111/*!
112 * \qmltype XdgShell
113 * \nativetype QWaylandXdgShell
114 * \inqmlmodule QtWayland.Compositor.XdgShell
115 * \since 5.12
116 * \brief Provides an extension for desktop-style user interfaces.
117 *
118 * The XdgShell extension provides a way to associate a XdgToplevel or XdgPopup
119 * with a regular Wayland surface. Using the XdgToplevel interface, the client
120 * can request that the surface is resized, moved, and so on.
121 *
122 * XdgShell corresponds to the Wayland interface, \c xdg_shell.
123 *
124 * To provide the functionality of the shell extension in a compositor, create
125 * an instance of the XdgShell component and add it to the list of extensions
126 * supported by the compositor:
127 *
128 * \qml
129 * import QtWayland.Compositor.XdgShell
130 *
131 * WaylandCompositor {
132 * XdgShell {
133 * // ...
134 * }
135 * }
136 * \endqml
137 */
138
139/*!
140 * \class QWaylandXdgShell
141 * \inmodule QtWaylandCompositor
142 * \since 5.12
143 * \brief The QWaylandXdgShell class is an extension for desktop-style user interfaces.
144 *
145 * The QWaylandXdgShell extension provides a way to associate a QWaylandXdgToplevel or
146 * QWaylandXdgPopup with a regular Wayland surface. Using the QWaylandXdgToplevel interface,
147 * the client can request that the surface is resized, moved, and so on.
148 *
149 * QWaylandXdgShell corresponds to the Wayland interface, \c xdg_shell.
150 */
151
152/*!
153 * Constructs a QWaylandXdgShell object.
154 */
155QWaylandXdgShell::QWaylandXdgShell()
156 : QWaylandShellTemplate<QWaylandXdgShell>(*new QWaylandXdgShellPrivate())
157{
158}
159
160/*!
161 * Constructs a QWaylandXdgShell object for the provided \a compositor.
162 */
163QWaylandXdgShell::QWaylandXdgShell(QWaylandCompositor *compositor)
164 : QWaylandShellTemplate<QWaylandXdgShell>(compositor, *new QWaylandXdgShellPrivate())
165{
166}
167
168/*!
169 * Initializes the shell extension.
170 */
171void QWaylandXdgShell::initialize()
172{
173 Q_D(QWaylandXdgShell);
174 QWaylandShellTemplate::initialize();
175 QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
176 if (!compositor) {
177 qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandXdgShell";
178 return;
179 }
180 d->init(compositor->display(), 1);
181
182 handleSeatChanged(newSeat: compositor->defaultSeat(), oldSeat: nullptr);
183
184 connect(sender: compositor, signal: &QWaylandCompositor::defaultSeatChanged,
185 context: this, slot: &QWaylandXdgShell::handleSeatChanged);
186
187 // Support the dialog extension unconditionally.
188 QObject *dialogExtension = new QWaylandXdgDialogV1Global(compositor);
189 dialogExtension->setParent(this);
190}
191
192/*!
193 * Returns the Wayland interface for the QWaylandXdgShell.
194 */
195const struct wl_interface *QWaylandXdgShell::interface()
196{
197 return QWaylandXdgShellPrivate::interface();
198}
199
200QByteArray QWaylandXdgShell::interfaceName()
201{
202 return QWaylandXdgShellPrivate::interfaceName();
203}
204
205/*!
206 * \qmlmethod void XdgShell::ping(WaylandClient client)
207 *
208 * Sends a ping event to \a client. If the client replies to the event the
209 * \l pong signal will be emitted.
210 */
211
212/*!
213 * Sends a ping event to \a client. If the client replies to the event the
214 * \l pong signal will be emitted.
215 */
216uint QWaylandXdgShell::ping(QWaylandClient *client)
217{
218 Q_D(QWaylandXdgShell);
219
220 QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
221 Q_ASSERT(compositor);
222
223 uint32_t serial = compositor->nextSerial();
224
225 QWaylandXdgShellPrivate::Resource *clientResource = d->resourceMap().value(client->client(), nullptr);
226 Q_ASSERT(clientResource);
227
228 d->ping(clientResource, serial);
229 return serial;
230}
231
232void QWaylandXdgShell::handleSeatChanged(QWaylandSeat *newSeat, QWaylandSeat *oldSeat)
233{
234 if (oldSeat != nullptr) {
235 disconnect(sender: oldSeat, signal: &QWaylandSeat::keyboardFocusChanged,
236 receiver: this, slot: &QWaylandXdgShell::handleFocusChanged);
237 }
238
239 if (newSeat != nullptr) {
240 connect(sender: newSeat, signal: &QWaylandSeat::keyboardFocusChanged,
241 context: this, slot: &QWaylandXdgShell::handleFocusChanged);
242 }
243}
244
245void QWaylandXdgShell::handleFocusChanged(QWaylandSurface *newSurface, QWaylandSurface *oldSurface)
246{
247 Q_D(QWaylandXdgShell);
248
249 QWaylandXdgSurface *newXdgSurface = d->xdgSurfaceFromSurface(surface: newSurface);
250 QWaylandXdgSurface *oldXdgSurface = d->xdgSurfaceFromSurface(surface: oldSurface);
251
252 if (newXdgSurface)
253 QWaylandXdgSurfacePrivate::get(xdgSurface: newXdgSurface)->handleFocusReceived();
254
255 if (oldXdgSurface)
256 QWaylandXdgSurfacePrivate::get(xdgSurface: oldXdgSurface)->handleFocusLost();
257}
258
259QWaylandXdgSurfacePrivate::QWaylandXdgSurfacePrivate()
260{
261}
262
263void QWaylandXdgSurfacePrivate::setWindowType(Qt::WindowType windowType)
264{
265 if (m_windowType == windowType)
266 return;
267
268 m_windowType = windowType;
269
270 Q_Q(QWaylandXdgSurface);
271 emit q->windowTypeChanged();
272}
273
274void QWaylandXdgSurfacePrivate::handleFocusLost()
275{
276 if (m_toplevel)
277 QWaylandXdgToplevelPrivate::get(toplevel: m_toplevel)->handleFocusLost();
278}
279
280void QWaylandXdgSurfacePrivate::handleFocusReceived()
281{
282 if (m_toplevel)
283 QWaylandXdgToplevelPrivate::get(toplevel: m_toplevel)->handleFocusReceived();
284}
285
286QRect QWaylandXdgSurfacePrivate::calculateFallbackWindowGeometry() const
287{
288 // TODO: The unset window geometry should include subsurfaces as well, so this solution
289 // won't work too well on those kinds of clients.
290 return QRect(QPoint(), m_surface->destinationSize());
291}
292
293void QWaylandXdgSurfacePrivate::updateFallbackWindowGeometry()
294{
295 Q_Q(QWaylandXdgSurface);
296 if (!m_unsetWindowGeometry)
297 return;
298
299 const QRect unsetGeometry = calculateFallbackWindowGeometry();
300 if (unsetGeometry == m_windowGeometry)
301 return;
302
303 m_windowGeometry = unsetGeometry;
304 emit q->windowGeometryChanged();
305}
306
307void QWaylandXdgSurfacePrivate::xdg_surface_destroy_resource(QtWaylandServer::xdg_surface::Resource *resource)
308{
309 Q_UNUSED(resource);
310 Q_Q(QWaylandXdgSurface);
311 QWaylandXdgShellPrivate::get(xdgShell: m_xdgShell)->unregisterXdgSurface(xdgSurface: q);
312 delete q;
313}
314
315void QWaylandXdgSurfacePrivate::xdg_surface_destroy(QtWaylandServer::xdg_surface::Resource *resource)
316{
317 wl_resource_destroy(resource->handle);
318}
319
320void QWaylandXdgSurfacePrivate::xdg_surface_get_toplevel(QtWaylandServer::xdg_surface::Resource *resource, uint32_t id)
321{
322 Q_Q(QWaylandXdgSurface);
323
324 if (m_toplevel || m_popup) {
325 wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED,
326 "xdg_surface already has a role object");
327 return;
328 }
329
330 if (!m_surface->setRole(QWaylandXdgToplevel::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE))
331 return;
332
333 QWaylandResource topLevelResource(wl_resource_create(resource->client(), &xdg_toplevel_interface,
334 wl_resource_get_version(resource->handle), id));
335
336 m_toplevel = new QWaylandXdgToplevel(q, topLevelResource);
337 emit q->toplevelCreated();
338 emit m_xdgShell->toplevelCreated(toplevel: m_toplevel, xdgSurface: q);
339 q->connect(sender: m_toplevel, signal: &QWaylandXdgToplevel::modalChanged, context: q, slot: [q, this](){
340 q->setModal(m_toplevel->isModal());
341 });
342}
343
344void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surface::Resource *resource, uint32_t id, wl_resource *parentResource, wl_resource *positionerResource)
345{
346 Q_Q(QWaylandXdgSurface);
347
348 if (m_toplevel || m_popup) {
349 wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED,
350 "xdg_surface already has a role object");
351 return;
352 }
353
354 QWaylandXdgSurface *parent = QWaylandXdgSurface::fromResource(resource: parentResource);
355 if (!parent) {
356 wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
357 "xdg_surface.get_popup with invalid popup parent");
358 return;
359 }
360
361 QWaylandXdgPositioner *positioner = QWaylandXdgPositioner::fromResource(resource: positionerResource);
362 if (!positioner) {
363 wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
364 "xdg_surface.get_popup without positioner");
365 return;
366 }
367
368 if (!positioner->m_data.isComplete()) {
369 QWaylandXdgPositionerData p = positioner->m_data;
370 wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
371 "xdg_surface.get_popup with invalid positioner (size: %dx%d, anchorRect: %dx%d)",
372 p.size.width(), p.size.height(), p.anchorRect.width(), p.anchorRect.height());
373 return;
374 }
375
376 QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size());
377 if (!anchorBounds.contains(r: positioner->m_data.anchorRect)) {
378 // TODO: this is a protocol error and should ideally be handled like this:
379 //wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
380 // "xdg_positioner anchor rect extends beyound its parent's window geometry");
381 //return;
382 // However, our own clients currently do this, so we'll settle for a gentle warning instead.
383 qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: xdg_positioner anchor"
384 << "rect extends beyond its parent's window geometry";
385 }
386
387 if (!m_surface->setRole(QWaylandXdgPopup::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE))
388 return;
389
390 QWaylandResource popupResource(wl_resource_create(resource->client(), &xdg_popup_interface,
391 wl_resource_get_version(resource->handle), id));
392
393 m_popup = new QWaylandXdgPopup(q, parent, positioner, popupResource);
394 emit q->popupCreated();
395 emit m_xdgShell->popupCreated(popup: m_popup, xdgSurface: q);
396}
397
398void QWaylandXdgSurfacePrivate::xdg_surface_ack_configure(QtWaylandServer::xdg_surface::Resource *resource, uint32_t serial)
399{
400 if (m_toplevel) {
401 QWaylandXdgToplevelPrivate::get(toplevel: m_toplevel)->handleAckConfigure(serial);
402 } else if (m_popup) {
403 QWaylandXdgPopupPrivate::get(popup: m_popup)->handleAckConfigure(serial);
404 } else {
405 wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
406 "ack_configure requested on an unconstructed xdg_surface");
407 }
408}
409
410void QWaylandXdgSurfacePrivate::xdg_surface_set_window_geometry(QtWaylandServer::xdg_surface::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
411{
412 Q_Q(QWaylandXdgSurface);
413
414 if (!q->surface()->role()) {
415 wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
416 "set_window_geometry requested on an unconstructed xdg_surface");
417 return;
418 }
419
420 if (width <= 0 || height <= 0) {
421 // The protocol spec says "setting an invalid size will raise an error". But doesn't tell
422 // which error to raise, and there's no fitting error in the xdg_surface_error enum.
423 // So until this is fixed, just output a warning and return.
424 qWarning() << "Invalid (non-positive) dimensions received in set_window_geometry";
425 return;
426 }
427
428 m_unsetWindowGeometry = false;
429
430 QRect geometry(x, y, width, height);
431
432 if (m_windowGeometry == geometry)
433 return;
434
435 m_windowGeometry = geometry;
436 emit q->windowGeometryChanged();
437}
438
439/*!
440 * \qmltype XdgSurface
441 * \nativetype QWaylandXdgSurface
442 * \inqmlmodule QtWayland.Compositor.XdgShell
443 * \since 5.12
444 * \brief XdgSurface provides desktop-style compositor-specific features to an xdg surface.
445 *
446 * This type is part of the \l{XdgShell} extension and provides a way to
447 * extend the functionality of an existing \l{WaylandSurface} with features
448 * specific to desktop-style compositors, such as resizing and moving the
449 * surface.
450 *
451 * It corresponds to the Wayland interface \c xdg_surface.
452 */
453
454/*!
455 * \class QWaylandXdgSurface
456 * \inmodule QtWaylandCompositor
457 * \since 5.12
458 * \brief The QWaylandXdgSurface class provides desktop-style compositor-specific features to an xdg surface.
459 *
460 * This class is part of the QWaylandXdgShell extension and provides a way to
461 * extend the functionality of an existing QWaylandSurface with features
462 * specific to desktop-style compositors, such as resizing and moving the
463 * surface.
464 *
465 * It corresponds to the Wayland interface \c xdg_surface.
466 */
467
468/*!
469 * Constructs a QWaylandXdgSurface.
470 */
471QWaylandXdgSurface::QWaylandXdgSurface()
472 : QWaylandShellSurfaceTemplate<QWaylandXdgSurface>(*new QWaylandXdgSurfacePrivate)
473{
474}
475
476/*!
477 * Constructs a QWaylandXdgSurface for \a surface and initializes it with the
478 * given \a xdgShell, \a surface, and resource \a res.
479 */
480QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *xdgShell, QWaylandSurface *surface, const QWaylandResource &res)
481 : QWaylandShellSurfaceTemplate<QWaylandXdgSurface>(*new QWaylandXdgSurfacePrivate)
482{
483 initialize(xdgShell, surface, resource: res);
484}
485
486/*!
487 * \qmlmethod void XdgSurface::initialize(object xdgShell, object surface, object client, int id)
488 *
489 * Initializes the XdgSurface, associating it with the given \a xdgShell, \a surface,
490 * \a client, and \a id.
491 */
492
493/*!
494 * Initializes the QWaylandXdgSurface, associating it with the given \a xdgShell, \a surface
495 * and \a resource.
496 */
497void QWaylandXdgSurface::initialize(QWaylandXdgShell *xdgShell, QWaylandSurface *surface, const QWaylandResource &resource)
498{
499 Q_D(QWaylandXdgSurface);
500 d->m_xdgShell = xdgShell;
501 d->m_surface = surface;
502 d->init(resource.resource());
503 setExtensionContainer(surface);
504 d->m_windowGeometry = d->calculateFallbackWindowGeometry();
505 connect(sender: surface, signal: &QWaylandSurface::destinationSizeChanged, context: this, slot: &QWaylandXdgSurface::handleSurfaceSizeChanged);
506 connect(sender: surface, signal: &QWaylandSurface::bufferScaleChanged, context: this, slot: &QWaylandXdgSurface::handleBufferScaleChanged);
507 emit shellChanged();
508 emit surfaceChanged();
509 QWaylandCompositorExtension::initialize();
510}
511
512/*!
513 * \qmlproperty enum XdgSurface::windowType
514 *
515 * This property holds the window type of the XdgSurface.
516 */
517Qt::WindowType QWaylandXdgSurface::windowType() const
518{
519 Q_D(const QWaylandXdgSurface);
520 return d->m_windowType;
521}
522
523/*!
524 * \qmlproperty rect XdgSurface::windowGeometry
525 *
526 * This property holds the window geometry of the QWaylandXdgSurface. The window
527 * geometry describes the window's visible bounds from the user's perspective.
528 * The geometry includes title bars and borders if drawn by the client, but
529 * excludes drop shadows. It is meant to be used for aligning and tiling
530 * windows.
531 */
532
533/*!
534 * \property QWaylandXdgSurface::windowGeometry
535 *
536 * This property holds the window geometry of the QWaylandXdgSurface. The window
537 * geometry describes the window's visible bounds from the user's perspective.
538 * The geometry includes title bars and borders if drawn by the client, but
539 * excludes drop shadows. It is meant to be used for aligning and tiling
540 * windows.
541 */
542QRect QWaylandXdgSurface::windowGeometry() const
543{
544 Q_D(const QWaylandXdgSurface);
545 return d->m_windowGeometry;
546}
547
548/*!
549 * \internal
550 */
551void QWaylandXdgSurface::initialize()
552{
553 QWaylandCompositorExtension::initialize();
554}
555
556void QWaylandXdgSurface::handleSurfaceSizeChanged()
557{
558 Q_D(QWaylandXdgSurface);
559 d->updateFallbackWindowGeometry();
560}
561
562void QWaylandXdgSurface::handleBufferScaleChanged()
563{
564 Q_D(QWaylandXdgSurface);
565 d->updateFallbackWindowGeometry();
566}
567
568/*!
569 * \qmlproperty XdgShell XdgSurface::shell
570 *
571 * This property holds the shell associated with this XdgSurface.
572 */
573
574/*!
575 * \property QWaylandXdgSurface::shell
576 *
577 * This property holds the shell associated with this QWaylandXdgSurface.
578 */
579QWaylandXdgShell *QWaylandXdgSurface::shell() const
580{
581 Q_D(const QWaylandXdgSurface);
582 return d->m_xdgShell;
583}
584
585/*!
586 * \qmlproperty WaylandSurface XdgSurface::surface
587 *
588 * This property holds the surface associated with this XdgSurface.
589 */
590
591/*!
592 * \property QWaylandXdgSurface::surface
593 *
594 * This property holds the surface associated with this QWaylandXdgSurface.
595 */
596QWaylandSurface *QWaylandXdgSurface::surface() const
597{
598 Q_D(const QWaylandXdgSurface);
599 return d->m_surface;
600}
601
602/*!
603 * \qmlproperty XdgToplevel XdgSurface::toplevel
604 *
605 * This property holds the properties and methods that are specific to the
606 * toplevel XdgSurface.
607 *
608 * \sa popup, XdgShell::toplevelCreated
609 */
610
611/*!
612 * \property QWaylandXdgSurface::toplevel
613 *
614 * This property holds the properties and methods that are specific to the
615 * toplevel QWaylandXdgSurface.
616 *
617 * \sa QWaylandXdgSurface::popup, QWaylandXdgShell::toplevelCreated
618 */
619QWaylandXdgToplevel *QWaylandXdgSurface::toplevel() const
620{
621 Q_D(const QWaylandXdgSurface);
622 return d->m_toplevel;
623}
624
625/*!
626 * \qmlproperty XdgPopup XdgSurface::popup
627 *
628 * This property holds the properties and methods that are specific to the
629 * popup XdgSurface.
630 *
631 * \sa toplevel, XdgShell::popupCreated
632 */
633
634/*!
635 * \property QWaylandXdgSurface::popup
636 *
637 * This property holds the properties and methods that are specific to the
638 * popup QWaylandXdgSurface.
639 *
640 * \sa QWaylandXdgSurface::toplevel, QWaylandXdgShell::popupCreated
641 */
642QWaylandXdgPopup *QWaylandXdgSurface::popup() const
643{
644 Q_D(const QWaylandXdgSurface);
645 return d->m_popup;
646}
647
648/*!
649 * Returns the Wayland interface for the QWaylandXdgSurface.
650 */
651const wl_interface *QWaylandXdgSurface::interface()
652{
653 return QWaylandXdgSurfacePrivate::interface();
654}
655
656/*!
657 * \internal
658 */
659QByteArray QWaylandXdgSurface::interfaceName()
660{
661 return QWaylandXdgSurfacePrivate::interfaceName();
662}
663
664/*!
665 * Returns the QWaylandXdgSurface corresponding to the \a resource.
666 */
667QWaylandXdgSurface *QWaylandXdgSurface::fromResource(wl_resource *resource)
668{
669 if (auto p = QtWayland::fromResource<QWaylandXdgSurfacePrivate *>(resource))
670 return p->q_func();
671 return nullptr;
672}
673
674#if QT_CONFIG(wayland_compositor_quick)
675QWaylandQuickShellIntegration *QWaylandXdgSurface::createIntegration(QWaylandQuickShellSurfaceItem *item)
676{
677 Q_D(const QWaylandXdgSurface);
678
679 if (d->m_toplevel)
680 return new QtWayland::XdgToplevelIntegration(item);
681
682 if (d->m_popup)
683 return new QtWayland::XdgPopupIntegration(item);
684
685 return nullptr;
686}
687#endif
688
689/*!
690 * \qmltype XdgToplevel
691 * \nativetype QWaylandXdgToplevel
692 * \inqmlmodule QtWayland.Compositor.XdgShell
693 * \since 5.12
694 * \brief XdgToplevel represents the toplevel window specific parts of an xdg surface.
695 *
696 * This type is part of the \l{XdgShell} extension and provides a way to
697 * extend the functionality of an XdgSurface with features
698 * specific to desktop-style windows.
699 *
700 * It corresponds to the Wayland interface \c xdg_toplevel.
701 */
702
703/*!
704 * \class QWaylandXdgToplevel
705 * \inmodule QtWaylandCompositor
706 * \since 5.12
707 * \brief The QWaylandXdgToplevel class represents the toplevel window specific parts of an xdg surface.
708 *
709 * This class is part of the QWaylandXdgShell extension and provides a way to
710 * extend the functionality of an QWaylandXdgSurface with features
711 * specific to desktop-style windows.
712 *
713 * It corresponds to the Wayland interface \c xdg_toplevel.
714 */
715
716/*!
717 * Constructs a QWaylandXdgToplevel for the given \a xdgSurface and \a resource.
718 */
719QWaylandXdgToplevel::QWaylandXdgToplevel(QWaylandXdgSurface *xdgSurface, QWaylandResource &resource)
720 : QObject(*new QWaylandXdgToplevelPrivate(xdgSurface, resource))
721{
722 QList<QWaylandXdgToplevel::State> states;
723 sendConfigure(size: {0, 0}, states);
724}
725
726QWaylandXdgToplevel::~QWaylandXdgToplevel()
727{
728 Q_D(QWaylandXdgToplevel);
729 // Usually, the decoration is destroyed by the client (according to the protocol),
730 // but if the client misbehaves, or is shut down, we need to clean up here.
731 if (Q_UNLIKELY(d->m_decoration))
732 wl_resource_destroy(d->m_decoration->resource()->handle);
733 Q_ASSERT(!d->m_decoration);
734}
735
736/*!
737 * \qmlproperty XdgSurface XdgToplevel::xdgSurface
738 *
739 * This property holds the XdgSurface for this XdgToplevel.
740 */
741
742/*!
743 * \property QWaylandXdgToplevel::xdgSurface
744 *
745 * This property holds the QWaylandXdgSurface for this QWaylandXdgToplevel.
746 */
747QWaylandXdgSurface *QWaylandXdgToplevel::xdgSurface() const
748{
749 Q_D(const QWaylandXdgToplevel);
750 return d->m_xdgSurface;
751}
752
753/*!
754 * \qmlproperty XdgToplevel XdgToplevel::parentToplevel
755 *
756 * This property holds the XdgToplevel parent of this XdgToplevel.
757 */
758
759/*!
760 * \property QWaylandXdgToplevel::parentToplevel
761 *
762 * This property holds the XdgToplevel parent of this XdgToplevel.
763 *
764 */
765QWaylandXdgToplevel *QWaylandXdgToplevel::parentToplevel() const
766{
767 Q_D(const QWaylandXdgToplevel);
768 return d->m_parentToplevel;
769}
770
771/*!
772 * \qmlproperty string XdgToplevel::title
773 *
774 * This property holds the title of the XdgToplevel.
775 */
776
777/*!
778 * \property QWaylandXdgToplevel::title
779 *
780 * This property holds the title of the QWaylandXdgToplevel.
781 */
782QString QWaylandXdgToplevel::title() const
783{
784 Q_D(const QWaylandXdgToplevel);
785 return d->m_title;
786}
787
788/*!
789 * \qmlproperty string XdgToplevel::appId
790 *
791 * This property holds the app id of the XdgToplevel.
792 */
793
794/*!
795 * \property QWaylandXdgToplevel::appId
796 *
797 * This property holds the app id of the QWaylandXdgToplevel.
798 */
799QString QWaylandXdgToplevel::appId() const
800{
801 Q_D(const QWaylandXdgToplevel);
802 return d->m_appId;
803}
804
805/*!
806 * \qmlproperty size XdgToplevel::maxSize
807 *
808 * This property holds the maximum size of the XdgToplevel as requested by the client.
809 *
810 * The compositor is free to ignore this value and request a larger size.
811 */
812
813/*!
814 * \property QWaylandXdgToplevel::maxSize
815 *
816 * This property holds the maximum size of the QWaylandXdgToplevel.
817 *
818 * The compositor is free to ignore this value and request a larger size.
819 */
820QSize QWaylandXdgToplevel::maxSize() const
821{
822 Q_D(const QWaylandXdgToplevel);
823 return d->m_maxSize;
824}
825
826/*!
827 * \qmlproperty size XdgToplevel::minSize
828 *
829 * This property holds the minimum size of the XdgToplevel as requested by the client.
830 *
831 * The compositor is free to ignore this value and request a smaller size.
832 */
833
834/*!
835 * \property QWaylandXdgToplevel::minSize
836 *
837 * This property holds the minimum size of the QWaylandXdgToplevel.
838 *
839 * The compositor is free to ignore this value and request a smaller size.
840 */
841QSize QWaylandXdgToplevel::minSize() const
842{
843 Q_D(const QWaylandXdgToplevel);
844 return d->m_minSize;
845}
846
847/*!
848 * \property QWaylandXdgToplevel::states
849 *
850 * This property holds the last states the client acknowledged for this QWaylandToplevel.
851 */
852QList<QWaylandXdgToplevel::State> QWaylandXdgToplevel::states() const
853{
854 Q_D(const QWaylandXdgToplevel);
855 return d->m_lastAckedConfigure.states;
856}
857
858/*!
859 * \qmlproperty bool XdgToplevel::maximized
860 *
861 * This property holds whether the client has acknowledged that it should be maximized.
862 */
863
864/*!
865 * \property QWaylandXdgToplevel::maximized
866 *
867 * This property holds whether the client has acknowledged that it should be maximized.
868 */
869bool QWaylandXdgToplevel::maximized() const
870{
871 Q_D(const QWaylandXdgToplevel);
872 return d->m_lastAckedConfigure.states.contains(t: QWaylandXdgToplevel::State::MaximizedState);
873}
874
875/*!
876 * \qmlproperty bool XdgToplevel::fullscreen
877 *
878 * This property holds whether the client has acknowledged that it should be fullscreen.
879 */
880
881/*!
882 * \property QWaylandXdgToplevel::fullscreen
883 *
884 * This property holds whether the client has acknowledged that it should be fullscreen.
885 */
886bool QWaylandXdgToplevel::fullscreen() const
887{
888 Q_D(const QWaylandXdgToplevel);
889 return d->m_lastAckedConfigure.states.contains(t: QWaylandXdgToplevel::State::FullscreenState);
890}
891
892/*!
893 * \qmlproperty bool XdgToplevel::resizing
894 *
895 * This property holds whether the client has acknowledged that it is being resized.
896 */
897
898/*!
899 * \property QWaylandXdgToplevel::resizing
900 *
901 * This property holds whether the client has acknowledged that it is being resized.
902 */
903bool QWaylandXdgToplevel::resizing() const
904{
905 Q_D(const QWaylandXdgToplevel);
906 return d->m_lastAckedConfigure.states.contains(t: QWaylandXdgToplevel::State::ResizingState);
907}
908
909/*!
910 * \qmlproperty bool XdgToplevel::activated
911 *
912 * This property holds whether toplevel is drawing itself as having input focus.
913 */
914
915/*!
916 * \property QWaylandXdgToplevel::activated
917 *
918 * This property holds whether toplevel is drawing itself as having input focus.
919 */
920bool QWaylandXdgToplevel::activated() const
921{
922 Q_D(const QWaylandXdgToplevel);
923 return d->m_lastAckedConfigure.states.contains(t: QWaylandXdgToplevel::State::ActivatedState);
924}
925
926/*!
927 * \qmlproperty bool XdgToplevel::modal
928 *
929 * This property holds whether toplevel blocks other windows from receiving input.
930 * \since 6.8
931 */
932
933/*!
934 * \property QWaylandXdgToplevel::modal
935 *
936 * This property holds whether toplevel blocks other windows from receiving input.
937 * \since 6.8
938 */
939bool QWaylandXdgToplevel::isModal() const
940{
941 Q_D(const QWaylandXdgToplevel);
942 return d->m_modal;
943}
944
945void QWaylandXdgToplevel::setModal(bool newModal)
946{
947 Q_D(QWaylandXdgToplevel);
948 if (d->m_modal == newModal)
949 return;
950 d->m_modal = newModal;
951 emit modalChanged();
952}
953
954/*!
955 * \enum QWaylandXdgToplevel::DecorationMode
956 *
957 * This enum type is used to specify the window decoration mode for toplevel windows.
958 *
959 * \value ServerSideDecoration The compositor should draw window decorations.
960 * \value ClientSideDecoration The client should draw window decorations.
961 */
962
963/*!
964 * \qmlproperty enumeration XdgToplevel::decorationMode
965 *
966 * This property holds the current window decoration mode for this toplevel.
967 *
968 * The possible values are:
969 * \value XdgToplevel.ServerSideDecoration The compositor should draw window decorations.
970 * \value XdgToplevel.ClientSideDecoration The client should draw window decorations.
971 *
972 * \sa XdgDecorationManagerV1
973 */
974
975/*!
976 * \property QWaylandXdgToplevel::decorationMode
977 *
978 * This property holds the current window decoration mode for this toplevel.
979 *
980 * \sa QWaylandXdgDecorationManagerV1
981 */
982QWaylandXdgToplevel::DecorationMode QWaylandXdgToplevel::decorationMode() const
983{
984 Q_D(const QWaylandXdgToplevel);
985 return d->m_decoration ? d->m_decoration->configuredMode() : DecorationMode::ClientSideDecoration;
986}
987
988/*!
989 * \qmlmethod size XdgToplevel::sizeForResize(size size, point delta, uint edges)
990 *
991 * Convenience for computing the new size given the current \a size, a \a delta, and
992 * the \a edges active in the drag.
993 */
994
995/*!
996 * Convenience for computing the new size given the current \a size, a \a delta, and
997 * the \a edges active in the drag.
998 */
999QSize QWaylandXdgToplevel::sizeForResize(const QSizeF &size, const QPointF &delta, Qt::Edges edges) const
1000{
1001 qreal width = size.width();
1002 qreal height = size.height();
1003 if (edges & Qt::LeftEdge)
1004 width -= delta.x();
1005 else if (edges & Qt::RightEdge)
1006 width += delta.x();
1007
1008 if (edges & Qt::TopEdge)
1009 height -= delta.y();
1010 else if (edges & Qt::BottomEdge)
1011 height += delta.y();
1012
1013 QSize newSize = QSize(width, height)
1014 .expandedTo(otherSize: minSize())
1015 .expandedTo(otherSize: {1, 1}); // We don't want to send a size of (0,0) as that means that the client decides
1016
1017 if (maxSize().isValid())
1018 newSize = newSize.boundedTo(otherSize: maxSize());
1019
1020 return newSize;
1021}
1022
1023/*!
1024 * Sends a configure event to the client. Parameter \a size contains the pixel size
1025 * of the surface. A size of zero means the client is free to decide the size.
1026 * Known \a states are enumerated in QWaylandXdgToplevel::State.
1027 */
1028uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QList<QWaylandXdgToplevel::State> &states)
1029{
1030 if (!size.isValid()) {
1031 qWarning() << "Can't configure xdg_toplevel with an invalid size" << size;
1032 return 0;
1033 }
1034 Q_D(QWaylandXdgToplevel);
1035 auto statesBytes = QByteArray::fromRawData(data: reinterpret_cast<const char *>(states.data()),
1036 size: states.size() * static_cast<int>(sizeof(State)));
1037 uint32_t serial = d->m_xdgSurface->surface()->compositor()->nextSerial();
1038 d->m_pendingConfigures.append(t: QWaylandXdgToplevelPrivate::ConfigureEvent{states, size, serial});
1039 d->send_configure(size.width(), size.height(), statesBytes);
1040 QWaylandXdgSurfacePrivate::get(xdgSurface: d->m_xdgSurface)->send_configure(serial);
1041 return serial;
1042}
1043
1044/*!
1045 * \qmlmethod int XdgToplevel::sendConfigure(size size, list<int> states)
1046 *
1047 * Sends a configure event to the client. \a size contains the pixel size of the surface.
1048 * A size of zero means the client is free to decide the size.
1049 * Known \a states are enumerated in XdgToplevel::State.
1050 */
1051uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QList<int> &states)
1052{
1053 QList<State> s;
1054 for (auto state : states)
1055 s << State(state);
1056 return sendConfigure(size, states: s);
1057}
1058
1059/*!
1060 * \qmlmethod void XdgToplevel::sendClose()
1061 *
1062 * Sends a close event to the client. The client may choose to ignore the event.
1063 */
1064
1065/*!
1066 * Sends a close event to the client. The client may choose to ignore the event.
1067 */
1068void QWaylandXdgToplevel::sendClose()
1069{
1070 Q_D(QWaylandXdgToplevel);
1071 d->send_close();
1072}
1073
1074/*!
1075 * \qmlmethod void XdgToplevel::sendMaximized(size size)
1076 *
1077 * Convenience for sending a configure event with the maximized state set, and
1078 * fullscreen and resizing removed. The activated state is left in its current state.
1079 *
1080 * \a size is the new size of the window.
1081 */
1082
1083/*!
1084 * Convenience for sending a configure event with the maximized state set, and
1085 * fullscreen and resizing removed. The activated state is left in its current state.
1086 *
1087 * \a size is the new size of the window.
1088 */
1089uint QWaylandXdgToplevel::sendMaximized(const QSize &size)
1090{
1091 Q_D(QWaylandXdgToplevel);
1092 QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
1093
1094 if (!conf.states.contains(t: QWaylandXdgToplevel::State::MaximizedState))
1095 conf.states.append(t: QWaylandXdgToplevel::State::MaximizedState);
1096 conf.states.removeOne(t: QWaylandXdgToplevel::State::FullscreenState);
1097 conf.states.removeOne(t: QWaylandXdgToplevel::State::ResizingState);
1098
1099 return sendConfigure(size, states: conf.states);
1100}
1101
1102/*!
1103 * \qmlmethod void XdgToplevel::sendUnmaximized(size size)
1104 *
1105 * Convenience for sending a configure event with the maximized, fullscreen and
1106 * resizing states removed, and fullscreen and resizing removed. The activated
1107 * state is left in its current state.
1108 *
1109 * \a size is the new size of the window. If \a size is zero, the client decides the size.
1110 */
1111
1112/*!
1113 * Convenience for sending a configure event with the maximized, fullscreen and
1114 * resizing states removed, and fullscreen and resizing removed. The activated
1115 * state is left in its current state.
1116 *
1117 * \a size is the new size of the window. If \a size is zero, the client decides the size.
1118 */
1119uint QWaylandXdgToplevel::sendUnmaximized(const QSize &size)
1120{
1121 Q_D(QWaylandXdgToplevel);
1122 QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
1123
1124 conf.states.removeOne(t: QWaylandXdgToplevel::State::MaximizedState);
1125 conf.states.removeOne(t: QWaylandXdgToplevel::State::FullscreenState);
1126 conf.states.removeOne(t: QWaylandXdgToplevel::State::ResizingState);
1127
1128 return sendConfigure(size, states: conf.states);
1129
1130}
1131
1132/*!
1133 * \qmlmethod void XdgToplevel::sendFullscreen(size size)
1134 *
1135 * Convenience for sending a configure event with the fullscreen state set, and
1136 * maximized and resizing removed. The activated state is left in its current state.
1137 *
1138 * \sa sendUnmaximized
1139 *
1140 * \a size is the new size of the window.
1141 */
1142
1143/*!
1144 * Convenience for sending a configure event with the fullscreen state set, and
1145 * maximized and resizing removed. The activated state is left in its current state.
1146 *
1147 * \sa sendUnmaximized
1148 *
1149 * \a size is the new size of the window.
1150 */
1151uint QWaylandXdgToplevel::sendFullscreen(const QSize &size)
1152{
1153 Q_D(QWaylandXdgToplevel);
1154 QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
1155
1156 if (!conf.states.contains(t: QWaylandXdgToplevel::State::FullscreenState))
1157 conf.states.append(t: QWaylandXdgToplevel::State::FullscreenState);
1158 conf.states.removeOne(t: QWaylandXdgToplevel::State::MaximizedState);
1159 conf.states.removeOne(t: QWaylandXdgToplevel::State::ResizingState);
1160
1161 return sendConfigure(size, states: conf.states);
1162}
1163
1164/*!
1165 * \qmlmethod void XdgToplevel::sendResizing(size maxSize)
1166 *
1167 * Convenience for sending a configure event with the resizing state set, and
1168 * maximized and fullscreen removed. The activated state is left in its current state.
1169 *
1170 * \a maxSize is the new size of the window.
1171 */
1172
1173/*!
1174 * Convenience for sending a configure event with the resizing state set, and
1175 * maximized and fullscreen removed. The activated state is left in its current state.
1176 *
1177 * \a maxSize is the new size of the window.
1178 */
1179uint QWaylandXdgToplevel::sendResizing(const QSize &maxSize)
1180{
1181 Q_D(QWaylandXdgToplevel);
1182 QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
1183
1184 if (!conf.states.contains(t: QWaylandXdgToplevel::State::ResizingState))
1185 conf.states.append(t: QWaylandXdgToplevel::State::ResizingState);
1186 conf.states.removeOne(t: QWaylandXdgToplevel::State::MaximizedState);
1187 conf.states.removeOne(t: QWaylandXdgToplevel::State::FullscreenState);
1188
1189 return sendConfigure(size: maxSize, states: conf.states);
1190}
1191
1192/*!
1193 * Returns the surface role for the QWaylandToplevel.
1194 */
1195QWaylandSurfaceRole *QWaylandXdgToplevel::role()
1196{
1197 return &QWaylandXdgToplevelPrivate::s_role;
1198}
1199
1200/*!
1201 * Returns the QWaylandXdgToplevel corresponding to the \a resource.
1202 */
1203QWaylandXdgToplevel *QWaylandXdgToplevel::fromResource(wl_resource *resource)
1204{
1205 if (auto p = QtWayland::fromResource<QWaylandXdgToplevelPrivate *>(resource))
1206 return p->q_func();
1207 return nullptr;
1208}
1209
1210/*!
1211 * \qmlsignal XdgShell::xdgSurfaceCreated(XdgSurface xdgSurface)
1212 *
1213 * This signal is emitted when the client has created a \c xdg_surface.
1214 * Note that \a xdgSurface is not mapped, i.e. according to the \c xdg-shell
1215 * protocol it should not be displayed, until it has received a role object.
1216 *
1217 * \sa toplevelCreated(), popupCreated()
1218 */
1219
1220/*!
1221 * \fn void QWaylandXdgShell::xdgSurfaceCreated(QWaylandXdgSurface *xdgSurface)
1222 *
1223 * This signal is emitted when the client has created a \c xdg_surface.
1224 * Note that \a xdgSurface is not mapped, i.e. according to the \c xdg-shell
1225 * protocol it should not be displayed, until it has received a role object.
1226 *
1227 * \sa toplevelCreated(), popupCreated()
1228 */
1229
1230/*!
1231 * \qmlsignal XdgShell::toplevelCreated(XdgToplevel toplevel, XdgSurface xdgSurface)
1232 *
1233 * This signal is emitted when the client has created a \c xdg_toplevel.
1234 * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or
1235 * WaylandQuickItem for displaying \a toplevel in a QtQuick scene.
1236 *
1237 * \a xdgSurface is the XdgSurface \a toplevel is the role object for.
1238 */
1239
1240/*!
1241 * \fn void QWaylandXdgShell::toplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface)
1242 *
1243 * This signal is emitted when the client has created a \c xdg_toplevel.
1244 * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or
1245 * QWaylandQuickItem for displaying \a toplevel in a QtQuick scene.
1246 *
1247 * \a xdgSurface is the XdgSurface \a toplevel is the role object for.
1248 */
1249
1250/*!
1251 * \qmlsignal XdgShell::popupCreated(XdgPopup popup, XdgSurface xdgSurface)
1252 *
1253 * This signal is emitted when the client has created a \c xdg_popup.
1254 * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or
1255 * WaylandQuickItem for displaying \a popup in a QtQuick scene.
1256 *
1257 * \a xdgSurface is the XdgSurface \a popup is the role object for.
1258 */
1259
1260/*!
1261 * \fn void QWaylandXdgShell::popupCreated(QWaylandXdgPopup *popup, QWaylandXdgSurface *xdgSurface)
1262 *
1263 * This signal is emitted when the client has created a \c xdg_popup.
1264 * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or
1265 * QWaylandQuickItem for displaying \a popup in a QtQuick scene.
1266 *
1267 * \a xdgSurface is the XdgSurface \a popup is the role object for.
1268 */
1269
1270/*!
1271 * \qmlsignal XdgShell::pong(int serial)
1272 *
1273 * This signal is emitted when the client has responded to a ping event with serial, \a serial.
1274 *
1275 * \sa ping()
1276 */
1277
1278/*!
1279 * \fn void QWaylandXdgShell::pong(uint serial)
1280 *
1281 * This signal is emitted when the client has responded to a ping event with serial, \a serial.
1282 *
1283 * \sa QWaylandXdgShell::ping()
1284 */
1285
1286QList<int> QWaylandXdgToplevel::statesAsInts() const
1287{
1288 QList<int> list;
1289 const auto s = states();
1290 list.reserve(size: s.size());
1291 for (auto state : s) {
1292 list << static_cast<int>(state);
1293 }
1294 return list;
1295}
1296
1297QWaylandSurfaceRole QWaylandXdgToplevelPrivate::s_role("xdg_toplevel");
1298
1299QWaylandXdgToplevelPrivate::QWaylandXdgToplevelPrivate(QWaylandXdgSurface *xdgSurface, const QWaylandResource &resource)
1300 : m_xdgSurface(xdgSurface)
1301{
1302 init(resource.resource());
1303}
1304
1305void QWaylandXdgToplevelPrivate::handleAckConfigure(uint serial)
1306{
1307 Q_Q(QWaylandXdgToplevel);
1308 ConfigureEvent config;
1309 Q_FOREVER {
1310 if (m_pendingConfigures.empty()) {
1311 qWarning(msg: "Toplevel received an unexpected ack_configure!");
1312 return;
1313 }
1314
1315 // This won't work unless there always is a toplevel.configure for each xdgsurface.configure
1316 config = m_pendingConfigures.takeFirst();
1317
1318 if (config.serial == serial)
1319 break;
1320 }
1321
1322 QList<uint> changedStates;
1323 std::set_symmetric_difference(
1324 first1: m_lastAckedConfigure.states.begin(), last1: m_lastAckedConfigure.states.end(),
1325 first2: config.states.begin(), last2: config.states.end(),
1326 result: std::back_inserter(x&: changedStates));
1327
1328 m_lastAckedConfigure = config;
1329
1330 for (uint state : changedStates) {
1331 switch (state) {
1332 case state_maximized:
1333 emit q->maximizedChanged();
1334 break;
1335 case state_fullscreen:
1336 emit q->fullscreenChanged();
1337 break;
1338 case state_resizing:
1339 emit q->resizingChanged();
1340 break;
1341 case state_activated:
1342 emit q->activatedChanged();
1343 break;
1344 }
1345 }
1346
1347 if (!changedStates.empty())
1348 emit q->statesChanged();
1349}
1350
1351void QWaylandXdgToplevelPrivate::handleFocusLost()
1352{
1353 Q_Q(QWaylandXdgToplevel);
1354 QWaylandXdgToplevelPrivate::ConfigureEvent current = lastSentConfigure();
1355 current.states.removeOne(t: QWaylandXdgToplevel::State::ActivatedState);
1356 q->sendConfigure(size: current.size, states: current.states);
1357}
1358
1359void QWaylandXdgToplevelPrivate::handleFocusReceived()
1360{
1361 Q_Q(QWaylandXdgToplevel);
1362 QWaylandXdgToplevelPrivate::ConfigureEvent current = lastSentConfigure();
1363 if (!current.states.contains(t: QWaylandXdgToplevel::State::ActivatedState)) {
1364 current.states.push_back(t: QWaylandXdgToplevel::State::ActivatedState);
1365 q->sendConfigure(size: current.size, states: current.states);
1366 }
1367}
1368
1369Qt::Edges QWaylandXdgToplevelPrivate::convertToEdges(resize_edge edge)
1370{
1371 return Qt::Edges(((edge & 0b1100) >> 1) | ((edge & 0b0010) << 2) | (edge & 0b0001));
1372}
1373
1374void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy_resource(QtWaylandServer::xdg_toplevel::Resource *resource)
1375{
1376 Q_UNUSED(resource);
1377 Q_Q(QWaylandXdgToplevel);
1378 delete q;
1379}
1380
1381void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy(QtWaylandServer::xdg_toplevel::Resource *resource)
1382{
1383 if (Q_UNLIKELY(m_decoration))
1384 qWarning() << "Client error: xdg_toplevel destroyed before its decoration object";
1385
1386 wl_resource_destroy(resource->handle);
1387 //TODO: Should the xdg surface be desroyed as well? Or is it allowed to recreate a new toplevel for it?
1388}
1389
1390void QWaylandXdgToplevelPrivate::xdg_toplevel_set_parent(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *parent)
1391{
1392 Q_UNUSED(resource);
1393 QWaylandXdgToplevel *parentToplevel = QWaylandXdgToplevel::fromResource(resource: parent);
1394
1395 Q_Q(QWaylandXdgToplevel);
1396
1397 if (m_parentToplevel != parentToplevel) {
1398 m_parentToplevel = parentToplevel;
1399 emit q->parentToplevelChanged();
1400 }
1401
1402 if (m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::SubWindow) {
1403 // There's a parent now, which means the surface is transient
1404 QWaylandXdgSurfacePrivate::get(xdgSurface: m_xdgSurface)->setWindowType(Qt::WindowType::SubWindow);
1405 } else if (!m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::Window) {
1406 // When the surface has no parent it is toplevel
1407 QWaylandXdgSurfacePrivate::get(xdgSurface: m_xdgSurface)->setWindowType(Qt::WindowType::Window);
1408 }
1409}
1410
1411void QWaylandXdgToplevelPrivate::xdg_toplevel_set_title(QtWaylandServer::xdg_toplevel::Resource *resource, const QString &title)
1412{
1413 Q_UNUSED(resource);
1414 if (title == m_title)
1415 return;
1416 Q_Q(QWaylandXdgToplevel);
1417 m_title = title;
1418 emit q->titleChanged();
1419}
1420
1421void QWaylandXdgToplevelPrivate::xdg_toplevel_set_app_id(QtWaylandServer::xdg_toplevel::Resource *resource, const QString &app_id)
1422{
1423 Q_UNUSED(resource);
1424 if (app_id == m_appId)
1425 return;
1426 Q_Q(QWaylandXdgToplevel);
1427 m_appId = app_id;
1428 emit q->appIdChanged();
1429}
1430
1431void QWaylandXdgToplevelPrivate::xdg_toplevel_show_window_menu(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y)
1432{
1433 Q_UNUSED(resource);
1434 Q_UNUSED(serial);
1435 QPoint position(x, y);
1436 auto seat = QWaylandSeat::fromSeatResource(resource: seatResource);
1437 Q_Q(QWaylandXdgToplevel);
1438 emit q->showWindowMenu(seat, localSurfacePosition: position);
1439}
1440
1441void QWaylandXdgToplevelPrivate::xdg_toplevel_move(Resource *resource, wl_resource *seatResource, uint32_t serial)
1442{
1443 Q_UNUSED(resource);
1444 Q_UNUSED(serial);
1445 Q_Q(QWaylandXdgToplevel);
1446 QWaylandSeat *seat = QWaylandSeat::fromSeatResource(resource: seatResource);
1447 emit q->startMove(seat);
1448}
1449
1450void QWaylandXdgToplevelPrivate::xdg_toplevel_resize(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *seatResource, uint32_t serial, uint32_t edges)
1451{
1452 Q_UNUSED(resource);
1453 Q_UNUSED(serial);
1454 Q_Q(QWaylandXdgToplevel);
1455 QWaylandSeat *seat = QWaylandSeat::fromSeatResource(resource: seatResource);
1456 emit q->startResize(seat, convertToEdges(resize_edge(edges)));
1457}
1458
1459void QWaylandXdgToplevelPrivate::xdg_toplevel_set_max_size(QtWaylandServer::xdg_toplevel::Resource *resource, int32_t width, int32_t height)
1460{
1461 Q_UNUSED(resource);
1462
1463 QSize maxSize(width, height);
1464 if (width == 0 && height == 0)
1465 maxSize = QSize(); // Wayland size of zero means unspecified which best translates to invalid
1466
1467 if (m_maxSize == maxSize)
1468 return;
1469
1470 if (width < 0 || height < 0) {
1471 // The spec says raise a protocol error, but there's no matching error defined
1472 qWarning() << "Received a xdg_toplevel.set_max_size request with a negative size";
1473 return;
1474 }
1475
1476 if (m_minSize.isValid() && maxSize.isValid() &&
1477 (maxSize.width() < m_minSize.width() || maxSize.height() < m_minSize.height())) {
1478 // The spec says raise a protocol error, but there's no matching error defined
1479 qWarning() << "Received a xdg_toplevel.set_max_size request with a size smaller than the minimium size";
1480 return;
1481 }
1482
1483 m_maxSize = maxSize;
1484
1485 Q_Q(QWaylandXdgToplevel);
1486 emit q->maxSizeChanged();
1487}
1488
1489void QWaylandXdgToplevelPrivate::xdg_toplevel_set_min_size(QtWaylandServer::xdg_toplevel::Resource *resource, int32_t width, int32_t height)
1490{
1491 Q_UNUSED(resource);
1492
1493 QSize minSize(width, height);
1494 if (width == 0 && height == 0)
1495 minSize = QSize(); // Wayland size of zero means unspecified
1496
1497 if (m_minSize == minSize)
1498 return;
1499
1500 if (width < 0 || height < 0) {
1501 // The spec says raise a protocol error, but there's no matching error defined
1502 qWarning() << "Received a xdg_toplevel.set_min_size request with a negative size";
1503 return;
1504 }
1505
1506 if (m_maxSize.isValid() && minSize.isValid() &&
1507 (minSize.width() > m_maxSize.width() || minSize.height() > m_maxSize.height())) {
1508 // The spec says raise a protocol error, but there's no matching error defined
1509 qWarning() << "Received a xdg_toplevel.set_min_size request with a size larger than the maximum size";
1510 return;
1511 }
1512
1513 m_minSize = minSize;
1514
1515 Q_Q(QWaylandXdgToplevel);
1516 emit q->minSizeChanged();
1517}
1518
1519void QWaylandXdgToplevelPrivate::xdg_toplevel_set_maximized(QtWaylandServer::xdg_toplevel::Resource *resource)
1520{
1521 Q_UNUSED(resource);
1522 Q_Q(QWaylandXdgToplevel);
1523 emit q->setMaximized();
1524}
1525
1526void QWaylandXdgToplevelPrivate::xdg_toplevel_unset_maximized(QtWaylandServer::xdg_toplevel::Resource *resource)
1527{
1528 Q_UNUSED(resource);
1529 Q_Q(QWaylandXdgToplevel);
1530 emit q->unsetMaximized();
1531}
1532
1533void QWaylandXdgToplevelPrivate::xdg_toplevel_set_fullscreen(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *output_res)
1534{
1535 Q_UNUSED(resource);
1536 Q_Q(QWaylandXdgToplevel);
1537 QWaylandOutput *output = output_res ? QWaylandOutput::fromResource(resource: output_res) : nullptr;
1538 emit q->setFullscreen(output);
1539}
1540
1541void QWaylandXdgToplevelPrivate::xdg_toplevel_unset_fullscreen(QtWaylandServer::xdg_toplevel::Resource *resource)
1542{
1543 Q_UNUSED(resource);
1544 Q_Q(QWaylandXdgToplevel);
1545 emit q->unsetFullscreen();
1546}
1547
1548void QWaylandXdgToplevelPrivate::xdg_toplevel_set_minimized(QtWaylandServer::xdg_toplevel::Resource *resource)
1549{
1550 Q_UNUSED(resource);
1551 Q_Q(QWaylandXdgToplevel);
1552 emit q->setMinimized();
1553}
1554
1555/*!
1556 * \qmltype XdgPopup
1557 * \nativetype QWaylandXdgPopup
1558 * \inqmlmodule QtWayland.Compositor.XdgShell
1559 * \since 5.12
1560 * \brief XdgPopup represents the popup specific parts of and xdg surface.
1561 *
1562 * This type is part of the \l{XdgShell} extension and provides a way to extend
1563 * extend the functionality of an \l{XdgSurface} with features
1564 * specific to desktop-style menus for an xdg surface.
1565 *
1566 * It corresponds to the Wayland interface \c xdg_popup.
1567 */
1568
1569/*!
1570 * \class QWaylandXdgPopup
1571 * \inmodule QtWaylandCompositor
1572 * \since 5.12
1573 * \brief The QWaylandXdgPopup class represents the popup specific parts of an xdg surface.
1574 *
1575 * This class is part of the QWaylandXdgShell extension and provides a way to
1576 * extend the functionality of a QWaylandXdgSurface with features
1577 * specific to desktop-style menus for an xdg surface.
1578 *
1579 * It corresponds to the Wayland interface \c xdg_popup.
1580 */
1581
1582/*!
1583 * Constructs a QWaylandXdgPopup.
1584 */
1585QWaylandXdgPopup::QWaylandXdgPopup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface,
1586 QWaylandXdgPositioner *positioner, QWaylandResource &resource)
1587 : QObject(*new QWaylandXdgPopupPrivate(xdgSurface, parentXdgSurface, positioner, resource))
1588{
1589}
1590
1591/*!
1592 * \qmlproperty XdgSurface XdgPopup::xdgSurface
1593 *
1594 * This property holds the XdgSurface associated with this XdgPopup.
1595 */
1596
1597/*!
1598 * \property QWaylandXdgPopup::xdgSurface
1599 *
1600 * This property holds the QWaylandXdgSurface associated with this QWaylandXdgPopup.
1601 */
1602QWaylandXdgSurface *QWaylandXdgPopup::xdgSurface() const
1603{
1604 Q_D(const QWaylandXdgPopup);
1605 return d->m_xdgSurface;
1606}
1607
1608/*!
1609 * \qmlproperty XdgSurface XdgPopup::parentXdgSurface
1610 *
1611 * This property holds the XdgSurface associated with the parent of this XdgPopup.
1612 */
1613
1614/*!
1615 * \property QWaylandXdgPopup::parentXdgSurface
1616 *
1617 * This property holds the QWaylandXdgSurface associated with the parent of this
1618 * QWaylandXdgPopup.
1619 */
1620QWaylandXdgSurface *QWaylandXdgPopup::parentXdgSurface() const
1621{
1622 Q_D(const QWaylandXdgPopup);
1623 return d->m_parentXdgSurface;
1624}
1625
1626/*!
1627 * \qmlproperty rect XdgPopup::configuredGeometry
1628 *
1629 * The window geometry the popup received in the configure event. Relative to the
1630 * upper left corner of the parent surface.
1631 */
1632
1633/*!
1634 * \property QWaylandXdgPopup::configuredGeometry
1635 *
1636 * The window geometry the popup received in the configure event. Relative to the
1637 * upper left corner of the parent surface.
1638 */
1639QRect QWaylandXdgPopup::configuredGeometry() const
1640{
1641 Q_D(const QWaylandXdgPopup);
1642 return d->m_geometry;
1643}
1644
1645/*!
1646 * \qmlproperty rect XdgPopup::anchorRect
1647 *
1648 * The anchor rectangle relative to the parent window geometry that the child
1649 * surface should be placed relative to.
1650 */
1651
1652/*!
1653 * \property QWaylandXdgPopup::anchorRect
1654 *
1655 * Returns the anchor rectangle relative to the parent window geometry that the child
1656 * surface should be placed relative to.
1657 */
1658QRect QWaylandXdgPopup::anchorRect() const
1659{
1660 Q_D(const QWaylandXdgPopup);
1661 return d->m_positionerData.anchorRect;
1662}
1663
1664/*!
1665 * \qmlproperty enumeration XdgPopup::anchorEdges
1666 *
1667 * This property holds the set of edges on the anchor rect that the child surface should be placed
1668 * relative to. If no edges are specified in a direction, the anchor point should be
1669 * centered between the edges.
1670 *
1671 * The possible values are:
1672 * \value Qt.TopEdge The top edge of the rectangle.
1673 * \value Qt.LeftEdge The left edge of the rectangle.
1674 * \value Qt.RightEdge The right edge of the rectangle.
1675 * \value Qt.BottomEdge The bottom edge of the rectangle.
1676 */
1677
1678/*!
1679 * \property QWaylandXdgPopup::anchorEdges
1680 *
1681 * Returns the set of edges on the anchor rect that the child surface should be placed
1682 * relative to. If no edges are specified in a direction, the anchor point should be
1683 * centered between the edges.
1684 */
1685Qt::Edges QWaylandXdgPopup::anchorEdges() const
1686{
1687 Q_D(const QWaylandXdgPopup);
1688 return d->m_positionerData.anchorEdges;
1689}
1690
1691/*!
1692 * \qmlproperty rect XdgPopup::gravityEdges
1693 *
1694 * Specifies in what direction the surface should be positioned, relative to the anchor
1695 * point.
1696 *
1697 * The possible values are:
1698 * \value Qt.TopEdge The surface should slide towards the top of the screen.
1699 * \value Qt.LeftEdge The surface should slide towards the left of the screen.
1700 * \value Qt.RightEdge The surface should slide towards the right of the screen.
1701 * \value Qt.BottomEdge The surface should slide towards the bottom of the screen.
1702 */
1703
1704/*!
1705 * \property QWaylandXdgPopup::gravityEdges
1706 *
1707 * Specifies in what direction the surface should be positioned, relative to the anchor
1708 * point.
1709 */
1710Qt::Edges QWaylandXdgPopup::gravityEdges() const
1711{
1712 Q_D(const QWaylandXdgPopup);
1713 return d->m_positionerData.gravityEdges;
1714}
1715
1716/*!
1717 * \qmlproperty enumeration XdgPopup::slideConstraints
1718 *
1719 * This property holds the orientations in which the child should slide to fit within the screen.
1720 *
1721 * Possible values:
1722 * \value Qt.Horizontal Horizontal
1723 * \value Qt.Vertical Vertical
1724 */
1725
1726/*!
1727 * \property QWaylandXdgPopup::slideConstraints
1728 *
1729 * This property holds the orientations in which the child should slide to fit within the screen.
1730 */
1731Qt::Orientations QWaylandXdgPopup::slideConstraints() const
1732{
1733 Q_D(const QWaylandXdgPopup);
1734 const uint flags = d->m_positionerData.constraintAdjustments;
1735
1736 Qt::Orientations constraints = {};
1737
1738 if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X)
1739 constraints |= Qt::Horizontal;
1740 if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)
1741 constraints |= Qt::Vertical;
1742
1743 return constraints;
1744}
1745
1746/*!
1747 * \qmlproperty enumeration XdgPopup::flipConstraints
1748 *
1749 * This property holds the orientations in which the child should flip to fit within the screen.
1750 *
1751 * Possible values:
1752 * \value Qt.Horizontal Horizontal
1753 * \value Qt.Vertical Vertical
1754 */
1755
1756/*!
1757 * \property QWaylandXdgPopup::flipConstraints
1758 *
1759 * This property holds the orientations in which the child should flip to fit within the screen.
1760 */
1761Qt::Orientations QWaylandXdgPopup::flipConstraints() const
1762{
1763 Q_D(const QWaylandXdgPopup);
1764 const uint flags = d->m_positionerData.constraintAdjustments;
1765
1766 Qt::Orientations constraints = {};
1767
1768 if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X)
1769 constraints |= Qt::Horizontal;
1770 if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)
1771 constraints |= Qt::Vertical;
1772
1773 return constraints;
1774}
1775
1776/*!
1777 * \qmlproperty enumeration XdgPopup::resizeConstraints
1778 *
1779 * This property holds the orientations in which the child should resize to fit within the screen.
1780 *
1781 * Possible values:
1782 * \value Qt.Horizontal Horizontal
1783 * \value Qt.Vertical Vertical
1784 */
1785
1786/*!
1787 * \property QWaylandXdgPopup::resizeConstraints
1788 *
1789 * This property holds the orientations in which the child should resize to fit within the screen.
1790 */
1791Qt::Orientations QWaylandXdgPopup::resizeConstraints() const
1792{
1793 Q_D(const QWaylandXdgPopup);
1794 const uint flags = d->m_positionerData.constraintAdjustments;
1795
1796 Qt::Orientations constraints = {};
1797
1798 if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X)
1799 constraints |= Qt::Horizontal;
1800 if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)
1801 constraints |= Qt::Vertical;
1802
1803 return constraints;
1804}
1805
1806/*!
1807 * \qmlproperty point XdgPopup::offset
1808 *
1809 * The position relative to the position of the anchor on the anchor rectangle and
1810 * the anchor on the surface.
1811 */
1812
1813/*!
1814 * \property QWaylandXdgPopup::offset
1815 *
1816 * Returns the surface position relative to the position of the anchor on the anchor
1817 * rectangle and the anchor on the surface.
1818 */
1819QPoint QWaylandXdgPopup::offset() const
1820{
1821 Q_D(const QWaylandXdgPopup);
1822 return d->m_positionerData.offset;
1823}
1824
1825/*!
1826 * \qmlproperty size XdgPopup::positionerSize
1827 *
1828 * The size requested for the window geometry by the positioner object.
1829 */
1830
1831/*!
1832 * \property QWaylandXdgPopup::positionerSize
1833 *
1834 * Returns the size requested for the window geometry by the positioner object.
1835 */
1836QSize QWaylandXdgPopup::positionerSize() const
1837{
1838 Q_D(const QWaylandXdgPopup);
1839 return d->m_positionerData.size;
1840}
1841
1842/*!
1843 * \qmlproperty point XdgPopup::unconstrainedPosition
1844 *
1845 * The position of the surface relative to the parent window geometry if the surface
1846 * is not constrained. I.e. when not moved to fit inside the screen or similar.
1847 */
1848
1849/*!
1850 * \property QWaylandXdgPopup::unconstrainedPosition
1851 *
1852 * The position of the surface relative to the parent window geometry if the surface
1853 * is not constrained. I.e. when not moved to fit inside the screen or similar.
1854 */
1855QPoint QWaylandXdgPopup::unconstrainedPosition() const
1856{
1857 Q_D(const QWaylandXdgPopup);
1858 return d->m_positionerData.unconstrainedPosition();
1859}
1860
1861/*!
1862 * \qmlmethod int XdgPopup::sendConfigure(rect geometry)
1863 *
1864 * Sends a configure event to the client. \a geometry contains the window geometry
1865 * relative to the upper left corner of the window geometry of the parent surface.
1866 *
1867 * This implicitly sends a configure event to the corresponding XdgSurface as well.
1868 */
1869
1870/*!
1871 * Sends a configure event to the client. \a geometry contains the window geometry
1872 * relative to the upper left corner of the window geometry of the parent surface.
1873 *
1874 * This implicitly sends a configure event to the corresponding QWaylandXdgSurface
1875 * as well.
1876 */
1877uint QWaylandXdgPopup::sendConfigure(const QRect &geometry)
1878{
1879 Q_D(QWaylandXdgPopup);
1880 return d->sendConfigure(geometry);
1881}
1882
1883/*!
1884 * \qmlmethod void XdgPopup::sendPopupDone()
1885 * \since 5.14
1886 *
1887 * Dismiss the popup. According to the \c xdg-shell protocol this should make the
1888 * client destroy the popup.
1889 */
1890
1891/*!
1892 * \since 5.14
1893 *
1894 * Dismiss the popup. According to the \c xdg-shell protocol this should make the
1895 * client destroy the popup.
1896 */
1897void QWaylandXdgPopup::sendPopupDone()
1898{
1899 Q_D(QWaylandXdgPopup);
1900 d->send_popup_done();
1901}
1902
1903/*!
1904 * Returns the surface role for the QWaylandPopup.
1905 */
1906QWaylandSurfaceRole *QWaylandXdgPopup::role()
1907{
1908 return &QWaylandXdgPopupPrivate::s_role;
1909}
1910
1911QWaylandXdgPopupPrivate::QWaylandXdgPopupPrivate(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface,
1912 QWaylandXdgPositioner *positioner, const QWaylandResource &resource)
1913 : m_xdgSurface(xdgSurface)
1914 , m_parentXdgSurface(parentXdgSurface)
1915 , m_positionerData(positioner->m_data)
1916{
1917 Q_ASSERT(m_positionerData.isComplete());
1918 init(resource.resource());
1919
1920 QWaylandXdgSurfacePrivate::get(xdgSurface: m_xdgSurface)->setWindowType(Qt::WindowType::Popup);
1921
1922 //TODO: Need an API for sending a different initial configure
1923 sendConfigure(geometry: QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size));
1924}
1925
1926void QWaylandXdgPopupPrivate::handleAckConfigure(uint serial)
1927{
1928 Q_Q(QWaylandXdgPopup);
1929 ConfigureEvent config;
1930 Q_FOREVER {
1931 if (m_pendingConfigures.empty()) {
1932 qWarning(msg: "Popup received an unexpected ack_configure!");
1933 return;
1934 }
1935
1936 // This won't work unless there always is a popup.configure for each xdgsurface.configure
1937 config = m_pendingConfigures.takeFirst();
1938
1939 if (config.serial == serial)
1940 break;
1941 }
1942
1943 if (m_geometry == config.geometry)
1944 return;
1945
1946 m_geometry = config.geometry;
1947 emit q->configuredGeometryChanged();
1948}
1949
1950uint QWaylandXdgPopupPrivate::sendConfigure(const QRect &geometry)
1951{
1952 uint32_t serial = m_xdgSurface->surface()->compositor()->nextSerial();
1953 m_pendingConfigures.append(t: QWaylandXdgPopupPrivate::ConfigureEvent{.geometry: geometry, .serial: serial});
1954 send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height());
1955 QWaylandXdgSurfacePrivate::get(xdgSurface: m_xdgSurface)->send_configure(serial);
1956 return serial;
1957}
1958
1959void QWaylandXdgPopupPrivate::xdg_popup_destroy(QtWaylandServer::xdg_popup::Resource *resource)
1960{
1961 Q_UNUSED(resource);
1962 qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO
1963}
1964
1965void QWaylandXdgPopupPrivate::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial)
1966{
1967 Q_UNUSED(resource);
1968 Q_UNUSED(serial);
1969 Q_UNUSED(seat);
1970 qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO
1971 //switch keyboard focus
1972 //eventually send configure with activated.
1973}
1974
1975QWaylandSurfaceRole QWaylandXdgPopupPrivate::s_role("xdg_popup");
1976
1977QWaylandXdgPositionerData::QWaylandXdgPositionerData()
1978 : offset(0, 0)
1979{}
1980
1981bool QWaylandXdgPositionerData::isComplete() const
1982{
1983 return size.width() > 0 && size.height() > 0 && anchorRect.size().width() > 0 && anchorRect.size().height() > 0;
1984}
1985
1986QPoint QWaylandXdgPositionerData::anchorPoint() const
1987{
1988 int yPosition = 0;
1989 if (anchorEdges & Qt::TopEdge)
1990 yPosition = anchorRect.top();
1991 else if (anchorEdges & Qt::BottomEdge)
1992 yPosition = anchorRect.bottom() + 1;
1993 else
1994 yPosition = anchorRect.top() + anchorRect.height() / 2;
1995
1996 int xPosition = 0;
1997 if (anchorEdges & Qt::LeftEdge)
1998 xPosition = anchorRect.left();
1999 else if (anchorEdges & Qt::RightEdge)
2000 xPosition = anchorRect.right() + 1;
2001 else
2002 xPosition = anchorRect.left() + anchorRect.width() / 2;
2003
2004 return QPoint(xPosition, yPosition);
2005}
2006
2007QPoint QWaylandXdgPositionerData::unconstrainedPosition() const
2008{
2009 int gravityOffsetY = 0;
2010 if (gravityEdges & Qt::TopEdge)
2011 gravityOffsetY = -size.height();
2012 else if (!(gravityEdges & Qt::BottomEdge))
2013 gravityOffsetY = -size.height() / 2;
2014
2015 int gravityOffsetX = 0;
2016 if (gravityEdges & Qt::LeftEdge)
2017 gravityOffsetX = -size.width();
2018 else if (!(gravityEdges & Qt::RightEdge))
2019 gravityOffsetX = -size.width() / 2;
2020
2021 QPoint gravityOffset(gravityOffsetX, gravityOffsetY);
2022 return anchorPoint() + gravityOffset + offset;
2023}
2024
2025QWaylandXdgPositioner::QWaylandXdgPositioner(const QWaylandResource &resource)
2026{
2027 init(resource.resource());
2028}
2029
2030void QWaylandXdgPositioner::xdg_positioner_destroy_resource(QtWaylandServer::xdg_positioner::Resource *resource)
2031{
2032 Q_UNUSED(resource);
2033 delete this;
2034}
2035
2036void QWaylandXdgPositioner::xdg_positioner_destroy(QtWaylandServer::xdg_positioner::Resource *resource)
2037{
2038 wl_resource_destroy(resource->handle);
2039}
2040
2041void QWaylandXdgPositioner::xdg_positioner_set_size(QtWaylandServer::xdg_positioner::Resource *resource, int32_t width, int32_t height)
2042{
2043 if (width <= 0 || height <= 0) {
2044 wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
2045 "xdg_positioner.set_size requested with non-positive dimensions");
2046 return;
2047 }
2048
2049 QSize size(width, height);
2050 m_data.size = size;
2051}
2052
2053void QWaylandXdgPositioner::xdg_positioner_set_anchor_rect(QtWaylandServer::xdg_positioner::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
2054{
2055 if (width <= 0 || height <= 0) {
2056 wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
2057 "xdg_positioner.set_anchor_rect requested with non-positive dimensions");
2058 return;
2059 }
2060
2061 QRect anchorRect(x, y, width, height);
2062 m_data.anchorRect = anchorRect;
2063}
2064
2065void QWaylandXdgPositioner::xdg_positioner_set_anchor(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t anchor)
2066{
2067 Qt::Edges anchorEdges = convertToEdges(xdg_positioner::anchor(anchor));
2068
2069 if ((anchorEdges & Qt::BottomEdge && anchorEdges & Qt::TopEdge) ||
2070 (anchorEdges & Qt::LeftEdge && anchorEdges & Qt::RightEdge)) {
2071 wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
2072 "xdg_positioner.set_anchor requested with parallel edges");
2073 return;
2074 }
2075
2076 m_data.anchorEdges = anchorEdges;
2077}
2078
2079void QWaylandXdgPositioner::xdg_positioner_set_gravity(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t gravity)
2080{
2081 Qt::Edges gravityEdges = convertToEdges(xdg_positioner::gravity(gravity));
2082
2083 if ((gravityEdges & Qt::BottomEdge && gravityEdges & Qt::TopEdge) ||
2084 (gravityEdges & Qt::LeftEdge && gravityEdges & Qt::RightEdge)) {
2085 wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
2086 "xdg_positioner.set_gravity requested with parallel edges");
2087 return;
2088 }
2089
2090 m_data.gravityEdges = gravityEdges;
2091}
2092
2093void QWaylandXdgPositioner::xdg_positioner_set_constraint_adjustment(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t constraint_adjustment)
2094{
2095 Q_UNUSED(resource);
2096 m_data.constraintAdjustments = constraint_adjustment;
2097}
2098
2099void QWaylandXdgPositioner::xdg_positioner_set_offset(QtWaylandServer::xdg_positioner::Resource *resource, int32_t x, int32_t y)
2100{
2101 Q_UNUSED(resource);
2102 m_data.offset = QPoint(x, y);
2103}
2104
2105QWaylandXdgPositioner *QWaylandXdgPositioner::fromResource(wl_resource *resource)
2106{
2107 return QtWayland::fromResource<QWaylandXdgPositioner *>(resource);
2108}
2109
2110Qt::Edges QWaylandXdgPositioner::convertToEdges(anchor anchor)
2111{
2112 switch (anchor) {
2113 case anchor_none:
2114 return Qt::Edges();
2115 case anchor_top:
2116 return Qt::TopEdge;
2117 case anchor_bottom:
2118 return Qt::BottomEdge;
2119 case anchor_left:
2120 return Qt::LeftEdge;
2121 case anchor_right:
2122 return Qt::RightEdge;
2123 case anchor_top_left:
2124 return Qt::TopEdge | Qt::LeftEdge;
2125 case anchor_bottom_left:
2126 return Qt::BottomEdge | Qt::LeftEdge;
2127 case anchor_top_right:
2128 return Qt::TopEdge | Qt::RightEdge;
2129 case anchor_bottom_right:
2130 return Qt::BottomEdge | Qt::RightEdge;
2131 default:
2132 qWarning() << "Unknown Wayland xdg edge" << anchor;
2133 return Qt::Edges();
2134 }
2135}
2136
2137Qt::Edges QWaylandXdgPositioner::convertToEdges(QWaylandXdgPositioner::gravity gravity)
2138{
2139 return convertToEdges(anchor(gravity));
2140}
2141
2142
2143QT_END_NAMESPACE
2144
2145#include "moc_qwaylandxdgshell.cpp"
2146

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtwayland/src/compositor/extensions/qwaylandxdgshell.cpp