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 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | QWaylandXdgShellPrivate::QWaylandXdgShellPrivate() |
25 | { |
26 | } |
27 | |
28 | void 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 | |
34 | void QWaylandXdgShellPrivate::registerXdgSurface(QWaylandXdgSurface *xdgSurface) |
35 | { |
36 | m_xdgSurfaces.insert(key: xdgSurface->surface()->client()->client(), value: xdgSurface); |
37 | } |
38 | |
39 | void 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 | |
46 | QWaylandXdgSurface *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 | |
55 | void 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 | |
64 | void 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 | |
72 | void 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 | |
99 | void 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 | */ |
153 | QWaylandXdgShell::QWaylandXdgShell() |
154 | : QWaylandShellTemplate<QWaylandXdgShell>(*new QWaylandXdgShellPrivate()) |
155 | { |
156 | } |
157 | |
158 | /*! |
159 | * Constructs a QWaylandXdgShell object for the provided \a compositor. |
160 | */ |
161 | QWaylandXdgShell::QWaylandXdgShell(QWaylandCompositor *compositor) |
162 | : QWaylandShellTemplate<QWaylandXdgShell>(compositor, *new QWaylandXdgShellPrivate()) |
163 | { |
164 | } |
165 | |
166 | /*! |
167 | * Initializes the shell extension. |
168 | */ |
169 | void 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 | */ |
189 | const struct wl_interface *QWaylandXdgShell::interface() |
190 | { |
191 | return QWaylandXdgShellPrivate::interface(); |
192 | } |
193 | |
194 | QByteArray 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 | */ |
210 | uint 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 | |
226 | void 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 | |
239 | void 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 | |
253 | QWaylandXdgSurfacePrivate::QWaylandXdgSurfacePrivate() |
254 | { |
255 | } |
256 | |
257 | void 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 | |
268 | void QWaylandXdgSurfacePrivate::handleFocusLost() |
269 | { |
270 | if (m_toplevel) |
271 | QWaylandXdgToplevelPrivate::get(toplevel: m_toplevel)->handleFocusLost(); |
272 | } |
273 | |
274 | void QWaylandXdgSurfacePrivate::handleFocusReceived() |
275 | { |
276 | if (m_toplevel) |
277 | QWaylandXdgToplevelPrivate::get(toplevel: m_toplevel)->handleFocusReceived(); |
278 | } |
279 | |
280 | QRect 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 | |
287 | void 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 | |
301 | void 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 | |
309 | void QWaylandXdgSurfacePrivate::xdg_surface_destroy(QtWaylandServer::xdg_surface::Resource *resource) |
310 | { |
311 | wl_resource_destroy(resource->handle); |
312 | } |
313 | |
314 | void 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 | |
335 | void 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 (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 | |
389 | void 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 | |
401 | void 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 | */ |
462 | QWaylandXdgSurface::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 | */ |
471 | QWaylandXdgSurface::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 | */ |
488 | void 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 | */ |
508 | Qt::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 | */ |
533 | QRect QWaylandXdgSurface::windowGeometry() const |
534 | { |
535 | Q_D(const QWaylandXdgSurface); |
536 | return d->m_windowGeometry; |
537 | } |
538 | |
539 | /*! |
540 | * \internal |
541 | */ |
542 | void QWaylandXdgSurface::initialize() |
543 | { |
544 | QWaylandCompositorExtension::initialize(); |
545 | } |
546 | |
547 | void QWaylandXdgSurface::handleSurfaceSizeChanged() |
548 | { |
549 | Q_D(QWaylandXdgSurface); |
550 | d->updateFallbackWindowGeometry(); |
551 | } |
552 | |
553 | void 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 | */ |
570 | QWaylandXdgShell *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 | */ |
587 | QWaylandSurface *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 | */ |
610 | QWaylandXdgToplevel *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 | */ |
633 | QWaylandXdgPopup *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 | */ |
642 | const wl_interface *QWaylandXdgSurface::interface() |
643 | { |
644 | return QWaylandXdgSurfacePrivate::interface(); |
645 | } |
646 | |
647 | /*! |
648 | * \internal |
649 | */ |
650 | QByteArray QWaylandXdgSurface::interfaceName() |
651 | { |
652 | return QWaylandXdgSurfacePrivate::interfaceName(); |
653 | } |
654 | |
655 | /*! |
656 | * Returns the QWaylandXdgSurface corresponding to the \a resource. |
657 | */ |
658 | QWaylandXdgSurface *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) |
666 | QWaylandQuickShellIntegration *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 | */ |
710 | QWaylandXdgToplevel::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 | |
717 | QWaylandXdgToplevel::~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 | */ |
738 | QWaylandXdgSurface *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 | */ |
756 | QWaylandXdgToplevel *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 | */ |
773 | QString 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 | */ |
790 | QString 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 | */ |
811 | QSize 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 | */ |
832 | QSize 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 | */ |
843 | QList<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 | */ |
860 | bool 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 | */ |
877 | bool 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 | */ |
894 | bool 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 | */ |
911 | bool 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 | */ |
945 | QWaylandXdgToplevel::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 | */ |
962 | QSize 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 | */ |
991 | uint 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 | */ |
1014 | uint 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 | */ |
1031 | void 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 | */ |
1052 | uint 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 | */ |
1082 | uint 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 | */ |
1114 | uint 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 | */ |
1142 | uint 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 | */ |
1158 | QWaylandSurfaceRole *QWaylandXdgToplevel::role() |
1159 | { |
1160 | return &QWaylandXdgToplevelPrivate::s_role; |
1161 | } |
1162 | |
1163 | /*! |
1164 | * Returns the QWaylandXdgToplevel corresponding to the \a resource. |
1165 | */ |
1166 | QWaylandXdgToplevel *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 | |
1249 | QList<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 | |
1260 | QWaylandSurfaceRole QWaylandXdgToplevelPrivate::s_role("xdg_toplevel" ); |
1261 | |
1262 | QWaylandXdgToplevelPrivate::QWaylandXdgToplevelPrivate(QWaylandXdgSurface *xdgSurface, const QWaylandResource &resource) |
1263 | : m_xdgSurface(xdgSurface) |
1264 | { |
1265 | init(resource.resource()); |
1266 | } |
1267 | |
1268 | void 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 | |
1314 | void 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 | |
1322 | void 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 | |
1332 | Qt::Edges QWaylandXdgToplevelPrivate::convertToEdges(resize_edge edge) |
1333 | { |
1334 | return Qt::Edges(((edge & 0b1100) >> 1) | ((edge & 0b0010) << 2) | (edge & 0b0001)); |
1335 | } |
1336 | |
1337 | void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy_resource(QtWaylandServer::xdg_toplevel::Resource *resource) |
1338 | { |
1339 | Q_UNUSED(resource); |
1340 | Q_Q(QWaylandXdgToplevel); |
1341 | delete q; |
1342 | } |
1343 | |
1344 | void 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 | |
1353 | void 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 | |
1374 | void 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 | |
1384 | void 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 | |
1394 | void 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 | |
1404 | void 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 | |
1413 | void 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 | |
1422 | void 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 | |
1452 | void 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 | |
1482 | void 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 | |
1489 | void 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 | |
1496 | void 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 | |
1504 | void 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 | |
1511 | void 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 | */ |
1548 | QWaylandXdgPopup::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 | */ |
1565 | QWaylandXdgSurface *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 | */ |
1583 | QWaylandXdgSurface *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 | */ |
1602 | QRect 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 | */ |
1621 | QRect 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 | */ |
1648 | Qt::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 | */ |
1673 | Qt::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 | */ |
1694 | Qt::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 | */ |
1724 | Qt::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 | */ |
1754 | Qt::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 | */ |
1782 | QPoint 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 | */ |
1799 | QSize 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 | */ |
1818 | QPoint 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 | */ |
1840 | uint 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 | */ |
1860 | void QWaylandXdgPopup::sendPopupDone() |
1861 | { |
1862 | Q_D(QWaylandXdgPopup); |
1863 | d->send_popup_done(); |
1864 | } |
1865 | |
1866 | /*! |
1867 | * Returns the surface role for the QWaylandPopup. |
1868 | */ |
1869 | QWaylandSurfaceRole *QWaylandXdgPopup::role() |
1870 | { |
1871 | return &QWaylandXdgPopupPrivate::s_role; |
1872 | } |
1873 | |
1874 | QWaylandXdgPopupPrivate::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 | |
1889 | void 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 | |
1913 | uint 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 | |
1922 | void QWaylandXdgPopupPrivate::xdg_popup_destroy(QtWaylandServer::xdg_popup::Resource *resource) |
1923 | { |
1924 | Q_UNUSED(resource); |
1925 | qWarning() << Q_FUNC_INFO << "Not implemented" ; //TODO |
1926 | } |
1927 | |
1928 | void 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 | |
1938 | QWaylandSurfaceRole QWaylandXdgPopupPrivate::s_role("xdg_popup" ); |
1939 | |
1940 | QWaylandXdgPositionerData::QWaylandXdgPositionerData() |
1941 | : offset(0, 0) |
1942 | {} |
1943 | |
1944 | bool QWaylandXdgPositionerData::isComplete() const |
1945 | { |
1946 | return size.width() > 0 && size.height() > 0 && anchorRect.size().width() > 0 && anchorRect.size().height() > 0; |
1947 | } |
1948 | |
1949 | QPoint 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 | |
1970 | QPoint 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 | |
1988 | QWaylandXdgPositioner::QWaylandXdgPositioner(const QWaylandResource &resource) |
1989 | { |
1990 | init(resource.resource()); |
1991 | } |
1992 | |
1993 | void QWaylandXdgPositioner::xdg_positioner_destroy_resource(QtWaylandServer::xdg_positioner::Resource *resource) |
1994 | { |
1995 | Q_UNUSED(resource); |
1996 | delete this; |
1997 | } |
1998 | |
1999 | void QWaylandXdgPositioner::xdg_positioner_destroy(QtWaylandServer::xdg_positioner::Resource *resource) |
2000 | { |
2001 | wl_resource_destroy(resource->handle); |
2002 | } |
2003 | |
2004 | void 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 | |
2016 | void 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 | |
2028 | void 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 | |
2042 | void 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 | |
2056 | void 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 | |
2062 | void 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 | |
2068 | QWaylandXdgPositioner *QWaylandXdgPositioner::fromResource(wl_resource *resource) |
2069 | { |
2070 | return QtWayland::fromResource<QWaylandXdgPositioner *>(resource); |
2071 | } |
2072 | |
2073 | Qt::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 | |
2100 | Qt::Edges QWaylandXdgPositioner::convertToEdges(QWaylandXdgPositioner::gravity gravity) |
2101 | { |
2102 | return convertToEdges(anchor(gravity)); |
2103 | } |
2104 | |
2105 | |
2106 | QT_END_NAMESPACE |
2107 | |
2108 | #include "moc_qwaylandxdgshell.cpp" |
2109 | |