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