1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qwaylandqtshell.h" |
5 | #include "qwaylandqtshell_p.h" |
6 | #include "qwaylandqtshellchrome.h" |
7 | |
8 | #include <QtWaylandCompositor/QWaylandCompositor> |
9 | #include <QtWaylandCompositor/QWaylandSurface> |
10 | #include "qwaylandqtshell.h" |
11 | #include <QtWaylandCompositor/QWaylandResource> |
12 | |
13 | #if QT_CONFIG(wayland_compositor_quick) |
14 | # include "qwaylandqtshellintegration_p.h" |
15 | #endif |
16 | |
17 | #include <QtWaylandCompositor/QWaylandResource> |
18 | #include <QDebug> |
19 | #include <compositor/compositor_api/qwaylandseat.h> |
20 | |
21 | #include <QtWaylandCompositor/private/qwaylandutils_p.h> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | /*! |
26 | * \qmltype QtShell |
27 | * \instantiates QWaylandQtShell |
28 | * \inqmlmodule QtWayland.Compositor.QtShell |
29 | * \since 6.3 |
30 | * \brief Provides a shell extension for Qt applications running on a Qt Wayland Compositor. |
31 | * |
32 | * The QtShell extension provides a way to associate an QtShellSurface with a regular Wayland |
33 | * surface. The QtShell extension is written to support the window management features which are |
34 | * supported by Qt. It may be suitable on a platform where both the compositor and client |
35 | * applications are written with Qt, and where applications are trusted not to abuse features such |
36 | * as manual window positioning and "bring-to-front". |
37 | * |
38 | * For other use cases, consider using IviApplication or XdgShell instead. |
39 | * |
40 | * \qml |
41 | * import QtWayland.Compositor.QtShell |
42 | * |
43 | * WaylandCompositor { |
44 | * property ListModel shellSurfaces: ListModel {} |
45 | * QtShell { |
46 | * onQtShellSurfaceCreated: { |
47 | * shellSurfaces.append({shellSurface: qtShellSurface}) |
48 | * } |
49 | * } |
50 | * } |
51 | * \endqml |
52 | */ |
53 | QWaylandQtShell::QWaylandQtShell() |
54 | : QWaylandCompositorExtensionTemplate<QWaylandQtShell>(*new QWaylandQtShellPrivate()) |
55 | { |
56 | } |
57 | |
58 | QWaylandQtShell::QWaylandQtShell(QWaylandCompositor *compositor) |
59 | : QWaylandCompositorExtensionTemplate<QWaylandQtShell>(compositor, *new QWaylandQtShellPrivate()) |
60 | { |
61 | } |
62 | |
63 | bool QWaylandQtShell::moveChromeToFront(QWaylandQtShellChrome *chrome) |
64 | { |
65 | Q_D(QWaylandQtShell); |
66 | for (int i = 0; i < d->m_chromes.size(); ++i) { |
67 | if (d->m_chromes.at(i) == chrome) { |
68 | if (i > 0) { |
69 | QWaylandQtShellChrome *currentActive = d->m_chromes.first(); |
70 | d->m_chromes.move(i, 0); |
71 | chrome->activate(); |
72 | currentActive->deactivate(); |
73 | } |
74 | return true; |
75 | } |
76 | } |
77 | |
78 | return false; |
79 | } |
80 | |
81 | void QWaylandQtShell::registerChrome(QWaylandQtShellChrome *chrome) |
82 | { |
83 | Q_D(QWaylandQtShell); |
84 | if (moveChromeToFront(chrome)) |
85 | return; |
86 | |
87 | QWaylandQtShellChrome *currentActive = d->m_chromes.isEmpty() ? nullptr : d->m_chromes.first(); |
88 | |
89 | d->m_chromes.prepend(chrome); |
90 | chrome->activate(); |
91 | |
92 | if (currentActive != nullptr) |
93 | currentActive->deactivate(); |
94 | |
95 | connect(sender: chrome, signal: &QWaylandQtShellChrome::activated, context: this, slot: &QWaylandQtShell::chromeActivated); |
96 | connect(sender: chrome, signal: &QWaylandQtShellChrome::deactivated, context: this, slot: &QWaylandQtShell::chromeDeactivated); |
97 | } |
98 | |
99 | void QWaylandQtShell::unregisterChrome(QWaylandQtShellChrome *chrome) |
100 | { |
101 | Q_D(QWaylandQtShell); |
102 | |
103 | chrome->disconnect(receiver: this); |
104 | int index = d->m_chromes.indexOf(chrome); |
105 | if (index >= 0) { |
106 | d->m_chromes.removeAt(index); |
107 | if (index == 0 && d->m_chromes.size() > 0) |
108 | d->m_chromes.at(0)->activate(); |
109 | } |
110 | } |
111 | |
112 | void QWaylandQtShell::chromeActivated() |
113 | { |
114 | QWaylandQtShellChrome *c = qobject_cast<QWaylandQtShellChrome *>(object: sender()); |
115 | if (c != nullptr) { |
116 | moveChromeToFront(chrome: c); |
117 | } |
118 | } |
119 | |
120 | void QWaylandQtShell::chromeDeactivated() |
121 | { |
122 | Q_D(QWaylandQtShell); |
123 | QWaylandQtShellChrome *c = qobject_cast<QWaylandQtShellChrome *>(object: sender()); |
124 | if (d->m_chromes.size() > 1 && d->m_chromes.at(0) == c) { |
125 | d->m_chromes.move(0, 1); |
126 | d->m_chromes.at(0)->activate(); |
127 | } else if (d->m_chromes.size() == 1) { // One window must be active |
128 | d->m_chromes.at(0)->activate(); |
129 | } |
130 | } |
131 | |
132 | void QWaylandQtShell::initialize() |
133 | { |
134 | Q_D(QWaylandQtShell); |
135 | QWaylandCompositorExtensionTemplate::initialize(); |
136 | |
137 | QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); |
138 | if (!compositor) { |
139 | qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandQtShell" ; |
140 | return; |
141 | } |
142 | |
143 | d->init(compositor->display(), 1); |
144 | } |
145 | |
146 | const struct wl_interface *QWaylandQtShell::interface() |
147 | { |
148 | return QWaylandQtShellPrivate::interface(); |
149 | } |
150 | |
151 | /*! |
152 | * \internal |
153 | */ |
154 | QByteArray QWaylandQtShell::interfaceName() |
155 | { |
156 | return QWaylandQtShellPrivate::interfaceName(); |
157 | } |
158 | |
159 | /*! |
160 | * \qmlsignal void QtShell::qtShellSurfaceRequested(WaylandSurface surface, WaylandResource resource) |
161 | * |
162 | * This signal is emitted when the client has requested a QtShellSurface to be associated |
163 | * with \a surface. The handler for this signal is expected to create the QtShellSurface for |
164 | * \a resource and initialize it within the scope of the signal emission. If no QtShellSurface is |
165 | * created, a default one will be created instead. |
166 | */ |
167 | |
168 | /*! |
169 | * \qmlsignal void QtShell::qtShellSurfaceCreated(QtShellSurface *qtShellSurface) |
170 | * |
171 | * This signal is emitted when an QtShellSurface has been created. The supplied \a qtShellSurface is |
172 | * most commonly used to instantiate a ShellSurfaceItem. |
173 | */ |
174 | |
175 | QWaylandQtShellPrivate::QWaylandQtShellPrivate() |
176 | { |
177 | } |
178 | |
179 | void QWaylandQtShellPrivate::unregisterQtShellSurface(QWaylandQtShellSurface *qtShellSurface) |
180 | { |
181 | Q_UNUSED(qtShellSurface) |
182 | } |
183 | |
184 | void QWaylandQtShellPrivate::zqt_shell_v1_surface_create(QtWaylandServer::zqt_shell_v1::Resource *resource, wl_resource *surfaceResource, uint32_t id) |
185 | { |
186 | Q_Q(QWaylandQtShell); |
187 | QWaylandSurface *surface = QWaylandSurface::fromResource(resource: surfaceResource); |
188 | |
189 | if (!surface->setRole(role: QWaylandQtShellSurface::role(), errorResource: resource->handle, errorCode: ZQT_SHELL_V1_ERROR_ROLE)) |
190 | return; |
191 | |
192 | QWaylandResource qtShellSurfaceResource(wl_resource_create(resource->client(), &zqt_shell_surface_v1_interface, |
193 | wl_resource_get_version(resource->handle), id)); |
194 | |
195 | emit q->qtShellSurfaceRequested(surface, resource: qtShellSurfaceResource); |
196 | |
197 | QWaylandQtShellSurface *qtShellSurface = QWaylandQtShellSurface::fromResource(resource: qtShellSurfaceResource.resource()); |
198 | |
199 | if (!qtShellSurface) |
200 | qtShellSurface = new QWaylandQtShellSurface(q, surface, qtShellSurfaceResource); |
201 | |
202 | emit q->qtShellSurfaceCreated(qtShellSurface); |
203 | } |
204 | |
205 | QWaylandSurfaceRole QWaylandQtShellSurfacePrivate::s_role("qt_shell_surface" ); |
206 | |
207 | /*! |
208 | * \qmltype QtShellSurface |
209 | * \instantiates QWaylandQtShellSurface |
210 | * \inqmlmodule QtWayland.Compositor.QtShell |
211 | * \since 6.3 |
212 | * \brief Provides a simple way to identify and resize a surface. |
213 | * |
214 | * This type is part of the \l{QtShell} extension and provides a way to extend |
215 | * the functionality of an existing WaylandSurface with window management functionality. |
216 | * |
217 | * The QtShellSurface type holds the core functionality needed to create a compositor that supports |
218 | * the QtShell extension. It can be used directly, or via the QtShellChrome type, depending on what |
219 | * the needs of the compositor are. The QtShellChrome type has default behaviors and convenience |
220 | * APIs for working with QtShellSurface objects. |
221 | */ |
222 | |
223 | /*! |
224 | \qmlsignal void QtShellSurface::startMove() |
225 | |
226 | The client has requested an interactive move operation in the compositor by calling |
227 | \l{QWindow::startSystemMove()}. |
228 | |
229 | \sa capabilities |
230 | */ |
231 | |
232 | /*! |
233 | \qmlsignal void QtShellSurface::startResize(enum edges) |
234 | |
235 | The client has requested an interactive resize operation in the compositor by calling |
236 | \l{QWindow::startSystemResize()}. |
237 | |
238 | The \a edges provides information about which edge of the window should be moved during the |
239 | resize. It is a mask of the following values: |
240 | \list |
241 | \li Qt.TopEdge |
242 | \li Qt.LeftEdge |
243 | \li Qt.RightEdge |
244 | \li Qt.BottomEdge |
245 | \endlist |
246 | |
247 | \sa capabilities |
248 | */ |
249 | |
250 | QWaylandQtShellSurface::QWaylandQtShellSurface() |
251 | : QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>(*new QWaylandQtShellSurfacePrivate()) |
252 | { |
253 | } |
254 | |
255 | QWaylandQtShellSurface::QWaylandQtShellSurface(QWaylandQtShell *application, QWaylandSurface *surface, const QWaylandResource &resource) |
256 | : QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>(*new QWaylandQtShellSurfacePrivate()) |
257 | { |
258 | initialize(qtShell: application, surface, resource); |
259 | } |
260 | |
261 | /*! |
262 | * \qmlmethod void QtShellSurface::initialize(QtShell qtShell, WaylandSurface surface, WaylandResource resource) |
263 | * |
264 | * Initializes the QtShellSurface, associating it with the given \a qtShell, \a surface, and |
265 | * \a resource. |
266 | */ |
267 | void QWaylandQtShellSurface::initialize(QWaylandQtShell *qtShell, QWaylandSurface *surface, const QWaylandResource &resource) |
268 | { |
269 | Q_D(QWaylandQtShellSurface); |
270 | |
271 | d->m_qtShell = qtShell; |
272 | d->m_surface = surface; |
273 | |
274 | connect(sender: d->m_surface, signal: &QWaylandSurface::damaged, context: this, slot: &QWaylandQtShellSurface::surfaceCommitted); |
275 | |
276 | d->init(resource.resource()); |
277 | setExtensionContainer(surface); |
278 | |
279 | emit surfaceChanged(); |
280 | |
281 | QWaylandCompositorExtension::initialize(); |
282 | } |
283 | |
284 | /*! |
285 | * \qmlproperty WaylandSurface QtShellSurface::surface |
286 | * |
287 | * This property holds the surface associated with this QtShellSurface. |
288 | */ |
289 | QWaylandSurface *QWaylandQtShellSurface::surface() const |
290 | { |
291 | Q_D(const QWaylandQtShellSurface); |
292 | return d->m_surface; |
293 | } |
294 | |
295 | QWaylandQtShell *QWaylandQtShellSurface::shell() const |
296 | { |
297 | Q_D(const QWaylandQtShellSurface); |
298 | return d->m_qtShell; |
299 | } |
300 | |
301 | /*! |
302 | * \qmlproperty point QtShellSurface::windowPosition |
303 | * |
304 | * This property holds the position of the shell surface relative to its output. |
305 | */ |
306 | QPoint QWaylandQtShellSurface::windowPosition() const |
307 | { |
308 | Q_D(const QWaylandQtShellSurface); |
309 | return d->m_windowGeometry.topLeft(); |
310 | } |
311 | |
312 | void QWaylandQtShellSurface::setWindowPosition(const QPoint &position) |
313 | { |
314 | Q_D(QWaylandQtShellSurface); |
315 | |
316 | // We don't care about the ack in this case, so use UINT_MAX as serial |
317 | d->send_set_position(UINT32_MAX, position.x(), position.y()); |
318 | d->send_configure(UINT32_MAX); |
319 | |
320 | d->m_windowGeometry.moveTopLeft(p: position); |
321 | d->m_positionSet = true; |
322 | emit positionAutomaticChanged(); |
323 | emit windowGeometryChanged(); |
324 | } |
325 | |
326 | /*! |
327 | * \qmlproperty rect QtShellSurface::windowGeometry |
328 | * |
329 | * This property holds the window geometry of the shell surface. |
330 | */ |
331 | QRect QWaylandQtShellSurface::windowGeometry() const |
332 | { |
333 | Q_D(const QWaylandQtShellSurface); |
334 | return d->m_windowGeometry; |
335 | } |
336 | |
337 | /*! |
338 | * \qmlproperty size QtShellSurface::minimumSize |
339 | * |
340 | * The minimum size of the window if the client has specified one. Otherwise an invalid size. |
341 | */ |
342 | QSize QWaylandQtShellSurface::minimumSize() const |
343 | { |
344 | Q_D(const QWaylandQtShellSurface); |
345 | return d->m_minimumSize; |
346 | } |
347 | |
348 | /*! |
349 | * \qmlproperty size QtShellSurface::maximumSize |
350 | * |
351 | * The maximum size of the window if the client has specified one. Otherwise an invalid size. |
352 | */ |
353 | QSize QWaylandQtShellSurface::maximumSize() const |
354 | { |
355 | Q_D(const QWaylandQtShellSurface); |
356 | return d->m_maximumSize; |
357 | } |
358 | |
359 | /*! |
360 | * \qmlmethod void QtShellSurface::requestWindowGeometry(int windowState, rect windowGeometry) |
361 | * |
362 | * Requests a new \a windowState and \a windowGeometry for the QtShellSurface. The state and |
363 | * geometry is updated when the client has acknowledged the request (at which point it is safe to |
364 | * assume that the surface's buffer has been resized if necessary). |
365 | */ |
366 | void QWaylandQtShellSurface::requestWindowGeometry(uint windowState, const QRect &windowGeometry) |
367 | { |
368 | Q_D(QWaylandQtShellSurface); |
369 | if (!windowGeometry.isValid()) |
370 | return; |
371 | |
372 | d->configure(windowState, newGeometry: windowGeometry); |
373 | } |
374 | |
375 | void QWaylandQtShellSurfacePrivate::configure(uint windowState, const QRect &newGeometry) |
376 | { |
377 | QWaylandCompositor *compositor = m_surface != nullptr ? m_surface->compositor() : nullptr; |
378 | if (!compositor) { |
379 | qWarning() << "Failed to find QWaylandCompositor when configuring QWaylandQtShell" ; |
380 | return; |
381 | } |
382 | |
383 | uint32_t serial = compositor->nextSerial(); |
384 | m_pendingConfigures[serial] = qMakePair(value1&: windowState, value2: newGeometry); |
385 | |
386 | send_set_position(serial, newGeometry.x(), newGeometry.y()); |
387 | send_resize(serial, newGeometry.width(), newGeometry.height()); |
388 | send_set_window_state(serial, windowState & ~Qt::WindowActive); |
389 | send_configure(serial); |
390 | } |
391 | |
392 | void QWaylandQtShellSurface::setFrameMargins(const QMargins &margins) |
393 | { |
394 | Q_D(QWaylandQtShellSurface); |
395 | if (d->m_frameMargins == margins) |
396 | return; |
397 | |
398 | d->m_frameMargins = margins; |
399 | d->updateFrameMargins(); |
400 | |
401 | emit frameMarginChanged(); |
402 | } |
403 | |
404 | /*! |
405 | * \qmlproperty int QtShellSurface::frameMarginLeft |
406 | * |
407 | * This holds the window frame margin to the left of the surface. |
408 | */ |
409 | void QWaylandQtShellSurface::setFrameMarginLeft(int left) |
410 | { |
411 | Q_D(QWaylandQtShellSurface); |
412 | if (d->m_frameMargins.left() == left) |
413 | return; |
414 | |
415 | d->m_frameMargins.setLeft(left); |
416 | d->updateFrameMargins(); |
417 | |
418 | emit frameMarginChanged(); |
419 | } |
420 | |
421 | int QWaylandQtShellSurface::frameMarginLeft() const |
422 | { |
423 | Q_D(const QWaylandQtShellSurface); |
424 | return d->m_frameMargins.left(); |
425 | } |
426 | |
427 | /*! |
428 | * \qmlproperty int QtShellSurface::frameMarginRight |
429 | * |
430 | * This holds the window frame margin to the right of the surface. |
431 | */ |
432 | void QWaylandQtShellSurface::setFrameMarginRight(int right) |
433 | { |
434 | Q_D(QWaylandQtShellSurface); |
435 | if (d->m_frameMargins.right() == right) |
436 | return; |
437 | |
438 | d->m_frameMargins.setRight(right); |
439 | d->updateFrameMargins(); |
440 | |
441 | emit frameMarginChanged(); |
442 | } |
443 | |
444 | int QWaylandQtShellSurface::frameMarginRight() const |
445 | { |
446 | Q_D(const QWaylandQtShellSurface); |
447 | return d->m_frameMargins.right(); |
448 | } |
449 | |
450 | /*! |
451 | * \qmlproperty int QtShellSurface::frameMarginTop |
452 | * |
453 | * This holds the window frame margin above the surface. |
454 | */ |
455 | |
456 | void QWaylandQtShellSurface::setFrameMarginTop(int top) |
457 | { |
458 | Q_D(QWaylandQtShellSurface); |
459 | if (d->m_frameMargins.top() == top) |
460 | return; |
461 | d->m_frameMargins.setTop(top); |
462 | d->updateFrameMargins(); |
463 | |
464 | emit frameMarginChanged(); |
465 | } |
466 | |
467 | int QWaylandQtShellSurface::frameMarginTop() const |
468 | { |
469 | Q_D(const QWaylandQtShellSurface); |
470 | return d->m_frameMargins.top(); |
471 | } |
472 | |
473 | /*! |
474 | * \qmlproperty int QtShellSurface::frameMarginBottom |
475 | * |
476 | * This holds the window frame margin below the surface. |
477 | */ |
478 | void QWaylandQtShellSurface::setFrameMarginBottom(int bottom) |
479 | { |
480 | Q_D(QWaylandQtShellSurface); |
481 | if (d->m_frameMargins.bottom() == bottom) |
482 | return; |
483 | d->m_frameMargins.setBottom(bottom); |
484 | d->updateFrameMargins(); |
485 | |
486 | emit frameMarginChanged(); |
487 | } |
488 | |
489 | bool QWaylandQtShellSurface::positionAutomatic() const |
490 | { |
491 | Q_D(const QWaylandQtShellSurface); |
492 | return !d->m_positionSet; |
493 | } |
494 | |
495 | int QWaylandQtShellSurface::frameMarginBottom() const |
496 | { |
497 | Q_D(const QWaylandQtShellSurface); |
498 | return d->m_frameMargins.bottom(); |
499 | } |
500 | |
501 | /*! |
502 | * \qmlproperty int QtShellSurface::windowFlags |
503 | * |
504 | * This property holds the window flags of the QtShellSurface. |
505 | */ |
506 | uint QWaylandQtShellSurface::windowFlags() const |
507 | { |
508 | Q_D(const QWaylandQtShellSurface); |
509 | return d->m_windowFlags; |
510 | } |
511 | |
512 | /*! |
513 | * \qmlmethod void QtShellSurface::sendClose() |
514 | * |
515 | * Requests that the client application closes itself. |
516 | */ |
517 | void QWaylandQtShellSurface::sendClose() |
518 | { |
519 | Q_D(QWaylandQtShellSurface); |
520 | d->send_close(); |
521 | } |
522 | |
523 | /*! |
524 | * \qmlproperty string QtShellSurface::windowTitle |
525 | * |
526 | * This property holds the window title of the QtShellSurface. |
527 | */ |
528 | QString QWaylandQtShellSurface::windowTitle() const |
529 | { |
530 | Q_D(const QWaylandQtShellSurface); |
531 | return d->m_windowTitle; |
532 | } |
533 | |
534 | /*! |
535 | * \qmlproperty bool QtShellSurface::active |
536 | * |
537 | * This property holds whether the surface is currently considered active. |
538 | * |
539 | * \note There are no restrictions in QtShellSurface that prevents multiple surfaces from being |
540 | * active simultaneously. Such logic must either be implemented by the compositor itself, or by |
541 | * using the QtShellChrome type, which will automatically manage the activation state of surfaces. |
542 | */ |
543 | void QWaylandQtShellSurface::setActive(bool active) |
544 | { |
545 | Q_D(QWaylandQtShellSurface); |
546 | if (d->m_active == active) |
547 | return; |
548 | |
549 | d->m_active = active; |
550 | QWaylandCompositor *compositor = d->m_surface ? d->m_surface->compositor() : nullptr; |
551 | QWaylandSeat *seat = compositor ? compositor->defaultSeat() : nullptr; |
552 | if (seat && active) |
553 | seat->setKeyboardFocus(surface()); |
554 | emit activeChanged(); |
555 | } |
556 | |
557 | bool QWaylandQtShellSurface::active() const |
558 | { |
559 | Q_D(const QWaylandQtShellSurface); |
560 | return d->m_active; |
561 | } |
562 | |
563 | /*! |
564 | * \qmlproperty enum QtShellSurface::capabilities |
565 | * |
566 | * This property holds the capabilities of the compositor. By default, no special capabilities are |
567 | * enabled. |
568 | * |
569 | * \list |
570 | * \li QtShellSurface.InteractiveMove The client can trigger a server-side interactive move |
571 | * operation using \l{QWindow::startSystemMove()}. The compositor will be notified of this |
572 | * through the \l{startMove()} signal. |
573 | * \li QtShellSurface.InteractiveResize The client can trigger a server-side interactive resize |
574 | * operation using \l{QWindow::startSystemResize()}. The compositor will be notified of this |
575 | * through the \l{startResize()} signal. |
576 | * \endlist |
577 | */ |
578 | void QWaylandQtShellSurface::setCapabilities(CapabilityFlags capabilities) |
579 | { |
580 | Q_D(QWaylandQtShellSurface); |
581 | if (d->m_capabilities == capabilities) |
582 | return; |
583 | |
584 | d->m_capabilities = capabilities; |
585 | d->send_set_capabilities(capabilities); |
586 | |
587 | emit capabilitiesChanged(); |
588 | } |
589 | |
590 | QWaylandQtShellSurface::CapabilityFlags QWaylandQtShellSurface::capabilities() const |
591 | { |
592 | Q_D(const QWaylandQtShellSurface); |
593 | return d->m_capabilities; |
594 | } |
595 | |
596 | /*! |
597 | * \qmlproperty int QtShellSurface::windowState |
598 | * |
599 | * This property holds the window state of the QtShellSurface. |
600 | * |
601 | * \note When \l{requestWindowGeometry()} is called to update state of the surface, the |
602 | * \c windowState property will not be updated until the client has acknowledged the state change. |
603 | */ |
604 | uint QWaylandQtShellSurface::windowState() const |
605 | { |
606 | Q_D(const QWaylandQtShellSurface); |
607 | return d->m_windowState; |
608 | } |
609 | |
610 | void QWaylandQtShellSurface::surfaceCommitted() |
611 | { |
612 | Q_D(QWaylandQtShellSurface); |
613 | if (d->m_lastAckedConfigure < UINT32_MAX) { |
614 | QRect targetRect = d->m_windowGeometry; |
615 | uint windowState = d->m_windowState; |
616 | for (auto it = d->m_pendingConfigures.begin(); it != d->m_pendingConfigures.end(); ) { |
617 | if (it.key() == d->m_lastAckedConfigure) { |
618 | targetRect = it.value().second; |
619 | windowState = it.value().first; |
620 | } |
621 | |
622 | if (it.key() <= d->m_lastAckedConfigure) |
623 | it = d->m_pendingConfigures.erase(it); |
624 | else |
625 | break; |
626 | } |
627 | |
628 | if (d->m_windowState != windowState) { |
629 | d->m_windowState = windowState; |
630 | emit windowStateChanged(); |
631 | } |
632 | |
633 | if (d->m_windowGeometry != targetRect) { |
634 | d->m_windowGeometry = targetRect; |
635 | d->m_positionSet = true; |
636 | emit positionAutomaticChanged(); |
637 | emit windowGeometryChanged(); |
638 | } |
639 | |
640 | d->m_lastAckedConfigure = UINT32_MAX; |
641 | d->m_pendingPosition = QPoint{}; |
642 | d->m_pendingPositionValid = false; |
643 | d->m_pendingSize = QSize{}; |
644 | } else { |
645 | QRect oldRect = d->m_windowGeometry; |
646 | if (d->m_pendingPositionValid) { |
647 | d->m_windowGeometry.moveTopLeft(p: d->m_pendingPosition); |
648 | d->m_pendingPosition = QPoint{}; |
649 | d->m_pendingPositionValid = false; |
650 | d->m_positionSet = true; |
651 | emit positionAutomaticChanged(); |
652 | } |
653 | |
654 | if (d->m_pendingSize.isValid()) { |
655 | d->m_windowGeometry.setSize(d->m_pendingSize); |
656 | d->m_pendingSize = QSize{}; |
657 | } |
658 | |
659 | if (d->m_windowGeometry != oldRect) |
660 | emit windowGeometryChanged(); |
661 | } |
662 | } |
663 | |
664 | /*! |
665 | * Returns the Wayland interface for the QWaylandQtShellSurface. |
666 | */ |
667 | const wl_interface *QWaylandQtShellSurface::interface() |
668 | { |
669 | return QWaylandQtShellSurfacePrivate::interface(); |
670 | } |
671 | |
672 | QByteArray QWaylandQtShellSurface::interfaceName() |
673 | { |
674 | return QWaylandQtShellSurfacePrivate::interfaceName(); |
675 | } |
676 | |
677 | /*! |
678 | * Returns the surface role for the QWaylandQtShellSurface. |
679 | */ |
680 | QWaylandSurfaceRole *QWaylandQtShellSurface::role() |
681 | { |
682 | return &QWaylandQtShellSurfacePrivate::s_role; |
683 | } |
684 | |
685 | /*! |
686 | * Returns the QWaylandQtShellSurface corresponding to the \a resource. |
687 | */ |
688 | QWaylandQtShellSurface *QWaylandQtShellSurface::fromResource(wl_resource *resource) |
689 | { |
690 | if (auto p = QtWayland::fromResource<QWaylandQtShellSurfacePrivate *>(resource)) |
691 | return p->q_func(); |
692 | return nullptr; |
693 | } |
694 | |
695 | #if QT_CONFIG(wayland_compositor_quick) |
696 | QWaylandQuickShellIntegration *QWaylandQtShellSurface::createIntegration(QWaylandQuickShellSurfaceItem *item) |
697 | { |
698 | return new QtWayland::QtShellIntegration(item); |
699 | } |
700 | #endif |
701 | |
702 | /*! |
703 | * \internal |
704 | */ |
705 | void QWaylandQtShellSurface::initialize() |
706 | { |
707 | QWaylandShellSurfaceTemplate::initialize(); |
708 | } |
709 | |
710 | QWaylandQtShellSurfacePrivate::QWaylandQtShellSurfacePrivate() |
711 | { |
712 | } |
713 | |
714 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_ack_configure(Resource *resource, uint32_t serial) |
715 | { |
716 | Q_UNUSED(resource); |
717 | Q_Q(QWaylandQtShellSurface); |
718 | if (serial < UINT32_MAX) |
719 | m_lastAckedConfigure = serial; |
720 | |
721 | // Fake a surface commit because we won't get one as long as the window is unexposed |
722 | if (m_windowState & Qt::WindowMinimized) |
723 | q->surfaceCommitted(); |
724 | } |
725 | |
726 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_reposition(Resource *resource, int32_t x, int32_t y) |
727 | { |
728 | Q_UNUSED(resource); |
729 | |
730 | m_pendingPosition = QPoint(x, y); |
731 | m_pendingPositionValid = true; |
732 | m_lastAckedConfigure = UINT32_MAX; |
733 | } |
734 | |
735 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_size(Resource *resource, int32_t width, int32_t height) |
736 | { |
737 | Q_UNUSED(resource); |
738 | |
739 | m_pendingSize = QSize(width, height); |
740 | m_lastAckedConfigure = UINT32_MAX; |
741 | } |
742 | |
743 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_minimum_size(Resource *resource, int32_t width, int32_t height) |
744 | { |
745 | Q_UNUSED(resource); |
746 | Q_Q(QWaylandQtShellSurface); |
747 | m_minimumSize = QSize{width, height}; |
748 | emit q->minimumSizeChanged(); |
749 | } |
750 | |
751 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_maximum_size(Resource *resource, int32_t width, int32_t height) |
752 | { |
753 | Q_UNUSED(resource); |
754 | Q_Q(QWaylandQtShellSurface); |
755 | m_maximumSize = QSize{width, height}; |
756 | emit q->maximumSizeChanged(); |
757 | } |
758 | |
759 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_destroy_resource(QtWaylandServer::zqt_shell_surface_v1::Resource *resource) |
760 | { |
761 | Q_UNUSED(resource); |
762 | Q_Q(QWaylandQtShellSurface); |
763 | QWaylandQtShellPrivate::get(qtShell: m_qtShell)->unregisterQtShellSurface(qtShellSurface: q); |
764 | delete q; |
765 | } |
766 | |
767 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_destroy(QtWaylandServer::zqt_shell_surface_v1::Resource *resource) |
768 | { |
769 | wl_resource_destroy(resource->handle); |
770 | } |
771 | |
772 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_window_flags(Resource *resource, uint32_t flags) |
773 | { |
774 | Q_UNUSED(resource); |
775 | Q_Q(QWaylandQtShellSurface); |
776 | m_windowFlags = flags; |
777 | emit q->windowFlagsChanged(); |
778 | } |
779 | |
780 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_change_window_state(Resource *resource, uint32_t state) |
781 | { |
782 | Q_UNUSED(resource); |
783 | Q_Q(QWaylandQtShellSurface); |
784 | uint oldWindowState = m_windowState; |
785 | m_windowState = state & ~Qt::WindowActive; |
786 | |
787 | if (oldWindowState != m_windowState) |
788 | emit q->windowStateChanged(); |
789 | } |
790 | |
791 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_start_system_resize(Resource *resource, uint32_t serial, uint32_t edge) |
792 | { |
793 | Q_UNUSED(resource); |
794 | Q_UNUSED(serial); |
795 | Q_Q(QWaylandQtShellSurface); |
796 | emit q->startResize(edges: Qt::Edges(edge)); |
797 | } |
798 | |
799 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_start_system_move(Resource *resource, uint32_t serial) |
800 | { |
801 | Q_UNUSED(resource); |
802 | Q_UNUSED(serial); |
803 | Q_Q(QWaylandQtShellSurface); |
804 | emit q->startMove(); |
805 | } |
806 | |
807 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_window_title(Resource *resource, |
808 | const QString &title) |
809 | { |
810 | Q_UNUSED(resource); |
811 | Q_Q(QWaylandQtShellSurface); |
812 | m_windowTitle = title; |
813 | emit q->windowTitleChanged(); |
814 | } |
815 | |
816 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_request_activate(Resource *resource) |
817 | |
818 | { |
819 | Q_UNUSED(resource); |
820 | Q_Q(QWaylandQtShellSurface); |
821 | q->setActive(true); |
822 | } |
823 | |
824 | void QWaylandQtShellSurfacePrivate::updateFrameMargins() |
825 | { |
826 | send_set_frame_margins(m_frameMargins.left(), m_frameMargins.right(), |
827 | m_frameMargins.top(), m_frameMargins.bottom()); |
828 | } |
829 | |
830 | |
831 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_raise(Resource *resource) |
832 | { |
833 | Q_UNUSED(resource); |
834 | Q_Q(QWaylandQtShellSurface); |
835 | emit q->raiseRequested(); |
836 | } |
837 | |
838 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_lower(Resource *resource) |
839 | { |
840 | Q_UNUSED(resource); |
841 | Q_Q(QWaylandQtShellSurface); |
842 | emit q->lowerRequested(); |
843 | } |
844 | |
845 | QT_END_NAMESPACE |
846 | |
847 | #include "moc_qwaylandqtshell.cpp" |
848 | |