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

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