1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2017 Eurogiciel, author: <philippe.coval@eurogiciel.fr>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qwaylandxdgshell_p.h"
6
7#include "qwaylandxdgexporterv2_p.h"
8
9#include <QtWaylandClient/private/qwaylanddisplay_p.h>
10#include <QtWaylandClient/private/qwaylandwindow_p.h>
11#include <QtWaylandClient/private/qwaylandinputdevice_p.h>
12#include <QtWaylandClient/private/qwaylandscreen_p.h>
13#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
14
15#include <QtGui/QGuiApplication>
16#include <QtGui/private/qwindow_p.h>
17
18QT_BEGIN_NAMESPACE
19
20namespace QtWaylandClient {
21
22QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface)
23 : QtWayland::xdg_toplevel(xdgSurface->get_toplevel())
24 , m_xdgSurface(xdgSurface)
25{
26 QWindow *window = xdgSurface->window()->window();
27 if (auto *decorationManager = m_xdgSurface->m_shell->decorationManager()) {
28 if (!(window->flags() & Qt::FramelessWindowHint))
29 m_decoration = decorationManager->createToplevelDecoration(object());
30 }
31 requestWindowStates(states: window->windowStates());
32 requestWindowFlags(flags: window->flags());
33}
34
35QWaylandXdgSurface::Toplevel::~Toplevel()
36{
37 // The protocol spec requires that the decoration object is deleted before xdg_toplevel.
38 delete m_decoration;
39 m_decoration = nullptr;
40
41 if (isInitialized())
42 destroy();
43}
44
45void QWaylandXdgSurface::Toplevel::applyConfigure()
46{
47 if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen)))
48 m_normalSize = m_xdgSurface->m_window->windowContentGeometry().size();
49
50 if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)
51 && !m_xdgSurface->m_window->display()->isKeyboardAvailable())
52 m_xdgSurface->m_window->display()->handleWindowActivated(window: m_xdgSurface->m_window);
53
54 if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)
55 && !m_xdgSurface->m_window->display()->isKeyboardAvailable())
56 m_xdgSurface->m_window->display()->handleWindowDeactivated(window: m_xdgSurface->m_window);
57
58 m_xdgSurface->m_window->handleToplevelWindowTilingStatesChanged(states: m_toplevelStates);
59 m_xdgSurface->m_window->handleWindowStatesChanged(states: m_pending.states);
60
61 // If the width or height is zero, the client should decide the size on its own.
62 QSize surfaceSize;
63
64 if (m_pending.size.width() > 0) {
65 surfaceSize.setWidth(m_pending.size.width());
66 } else {
67 if (Q_UNLIKELY(m_pending.states & (Qt::WindowMaximized | Qt::WindowFullScreen))) {
68 qCWarning(lcQpaWayland) << "Configure event with maximized or fullscreen state contains invalid width:" << m_pending.size.width();
69 } else {
70 int width = m_normalSize.width();
71 if (!m_pending.bounds.isEmpty())
72 width = std::min(a: width, b: m_pending.bounds.width());
73 surfaceSize.setWidth(width);
74 }
75 }
76
77 if (m_pending.size.height() > 0) {
78 surfaceSize.setHeight(m_pending.size.height());
79 } else {
80 if (Q_UNLIKELY(m_pending.states & (Qt::WindowMaximized | Qt::WindowFullScreen))) {
81 qCWarning(lcQpaWayland) << "Configure event with maximized or fullscreen state contains invalid height:" << m_pending.size.height();
82 } else {
83 int height = m_normalSize.height();
84 if (!m_pending.bounds.isEmpty())
85 height = std::min(a: height, b: m_pending.bounds.height());
86 surfaceSize.setHeight(height);
87 }
88 }
89
90 if (!surfaceSize.isEmpty())
91 m_xdgSurface->m_window->resizeFromApplyConfigure(sizeWithMargins: surfaceSize.grownBy(m: m_xdgSurface->m_window->windowContentMargins()));
92
93 m_applied = m_pending;
94 qCDebug(lcQpaWayland) << "Applied pending xdg_toplevel configure event:" << m_applied.size << m_applied.states;
95}
96
97bool QWaylandXdgSurface::Toplevel::wantsDecorations()
98{
99 if (m_decoration && (m_decoration->pending() == QWaylandXdgToplevelDecorationV1::mode_server_side
100 || !m_decoration->isConfigured()))
101 return false;
102
103 return !(m_pending.states & Qt::WindowFullScreen);
104}
105
106void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure_bounds(int32_t width, int32_t height)
107{
108 m_pending.bounds = QSize(width, height);
109}
110
111void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t height, wl_array *states)
112{
113 m_pending.size = QSize(width, height);
114
115 auto *xdgStates = static_cast<uint32_t *>(states->data);
116 size_t numStates = states->size / sizeof(uint32_t);
117
118 m_pending.states = Qt::WindowNoState;
119 m_toplevelStates = QWaylandWindow::WindowNoState;
120
121 for (size_t i = 0; i < numStates; i++) {
122 switch (xdgStates[i]) {
123 case XDG_TOPLEVEL_STATE_ACTIVATED:
124 m_pending.states |= Qt::WindowActive;
125 break;
126 case XDG_TOPLEVEL_STATE_MAXIMIZED:
127 m_pending.states |= Qt::WindowMaximized;
128 break;
129 case XDG_TOPLEVEL_STATE_FULLSCREEN:
130 m_pending.states |= Qt::WindowFullScreen;
131 break;
132 case XDG_TOPLEVEL_STATE_TILED_LEFT:
133 m_toplevelStates |= QWaylandWindow::WindowTiledLeft;
134 break;
135 case XDG_TOPLEVEL_STATE_TILED_RIGHT:
136 m_toplevelStates |= QWaylandWindow::WindowTiledRight;
137 break;
138 case XDG_TOPLEVEL_STATE_TILED_TOP:
139 m_toplevelStates |= QWaylandWindow::WindowTiledTop;
140 break;
141 case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
142 m_toplevelStates |= QWaylandWindow::WindowTiledBottom;
143 break;
144 default:
145 break;
146 }
147 }
148 qCDebug(lcQpaWayland) << "Received xdg_toplevel.configure with" << m_pending.size
149 << "and" << m_pending.states;
150}
151
152void QWaylandXdgSurface::Toplevel::xdg_toplevel_close()
153{
154 m_xdgSurface->m_window->window()->close();
155}
156
157void QWaylandXdgSurface::Toplevel::requestWindowFlags(Qt::WindowFlags flags)
158{
159 if (m_decoration) {
160 if (flags & Qt::FramelessWindowHint) {
161 delete m_decoration;
162 m_decoration = nullptr;
163 } else {
164 m_decoration->unsetMode();
165 }
166 }
167}
168
169void QWaylandXdgSurface::Toplevel::requestWindowStates(Qt::WindowStates states)
170{
171 // Re-send what's different from the applied state
172 Qt::WindowStates changedStates = m_applied.states ^ states;
173
174 if (changedStates & Qt::WindowMaximized) {
175 if (states & Qt::WindowMaximized)
176 set_maximized();
177 else
178 unset_maximized();
179 }
180
181 if (changedStates & Qt::WindowFullScreen) {
182 if (states & Qt::WindowFullScreen) {
183 auto screen = m_xdgSurface->window()->waylandScreen();
184 if (screen) {
185 set_fullscreen(screen->output());
186 }
187 } else
188 unset_fullscreen();
189 }
190
191 // Minimized state is not reported by the protocol, so always send it
192 if (states & Qt::WindowMinimized) {
193 set_minimized();
194 m_xdgSurface->window()->handleWindowStatesChanged(states: states & ~Qt::WindowMinimized);
195 }
196}
197
198QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResizeEdges(Qt::Edges edges)
199{
200 return static_cast<enum resize_edge>(
201 ((edges & Qt::TopEdge) ? resize_edge_top : 0)
202 | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0)
203 | ((edges & Qt::LeftEdge) ? resize_edge_left : 0)
204 | ((edges & Qt::RightEdge) ? resize_edge_right : 0));
205}
206
207QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent,
208 QtWayland::xdg_positioner *positioner)
209 : m_xdgSurface(xdgSurface)
210 , m_parentXdgSurface(qobject_cast<QWaylandXdgSurface *>(object: parent->shellSurface()))
211 , m_parent(parent)
212{
213
214 init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr,
215 positioner->object()));
216}
217
218QWaylandXdgSurface::Popup::~Popup()
219{
220 if (isInitialized())
221 destroy();
222
223 if (m_grabbing) {
224 auto *shell = m_xdgSurface->m_shell;
225 Q_ASSERT(shell->m_topmostGrabbingPopup == this);
226 shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr;
227 m_grabbing = false;
228
229 // Synthesize Qt enter/leave events for popup
230 QWindow *leave = nullptr;
231 if (m_xdgSurface && m_xdgSurface->window())
232 leave = m_xdgSurface->window()->window();
233 QWindowSystemInterface::handleLeaveEvent(window: leave);
234
235 if (QWindow *enter = QGuiApplication::topLevelAt(pos: QCursor::pos()))
236 QWindowSystemInterface::handleEnterEvent(window: enter, local: enter->mapFromGlobal(pos: QCursor::pos()), global: QCursor::pos());
237 }
238}
239
240void QWaylandXdgSurface::Popup::applyConfigure()
241{
242 if (m_pendingGeometry.isValid()) {
243 QRect geometryWithMargins = m_pendingGeometry.marginsAdded(margins: m_xdgSurface->m_window->windowContentMargins());
244 QMargins parentMargins = m_parent->windowContentMargins() - m_parent->clientSideMargins();
245 QRect globalGeometry = geometryWithMargins.translated(m_parent->geometry().topLeft() + QPoint(parentMargins.left(), parentMargins.top()));
246 m_xdgSurface->setGeometryFromApplyConfigure(globalPosition: globalGeometry.topLeft(), sizeWithMargins: globalGeometry.size());
247 }
248 resetConfiguration();
249}
250
251void QWaylandXdgSurface::Popup::resetConfiguration()
252{
253 m_pendingGeometry = QRect();
254}
255
256void QWaylandXdgSurface::Popup::grab(QWaylandInputDevice *seat, uint serial)
257{
258 m_xdgSurface->m_shell->m_topmostGrabbingPopup = this;
259 xdg_popup::grab(seat->wl_seat(), serial);
260 m_grabbing = true;
261}
262
263void QWaylandXdgSurface::Popup::xdg_popup_configure(int32_t x, int32_t y, int32_t width, int32_t height)
264{
265 m_pendingGeometry = QRect(x, y, width, height);
266}
267
268void QWaylandXdgSurface::Popup::xdg_popup_popup_done()
269{
270 m_xdgSurface->m_window->window()->close();
271}
272
273QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *surface, QWaylandWindow *window)
274 : QWaylandShellSurface(window)
275 , xdg_surface(surface)
276 , m_shell(shell)
277 , m_window(window)
278{
279 QWaylandDisplay *display = window->display();
280 Qt::WindowType type = window->window()->type();
281 auto *transientParent = window->transientParent();
282
283 if (type == Qt::ToolTip && transientParent) {
284 setPopup(transientParent);
285 } else if (type == Qt::Popup && transientParent && display->lastInputDevice()) {
286 setGrabPopup(parent: transientParent, device: display->lastInputDevice(), serial: display->lastInputSerial());
287 } else {
288 setToplevel();
289 if (transientParent) {
290 auto parentXdgSurface = qobject_cast<QWaylandXdgSurface *>(object: transientParent->shellSurface());
291 if (parentXdgSurface)
292 m_toplevel->set_parent(parentXdgSurface->m_toplevel->object());
293 }
294 }
295 setSizeHints();
296}
297
298QWaylandXdgSurface::~QWaylandXdgSurface()
299{
300 if (m_toplevel) {
301 delete m_toplevel;
302 m_toplevel = nullptr;
303 }
304 if (m_popup) {
305 delete m_popup;
306 m_popup = nullptr;
307 }
308 destroy();
309}
310
311bool QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges)
312{
313 if (!m_toplevel || !m_toplevel->isInitialized())
314 return false;
315
316 auto resizeEdges = Toplevel::convertToResizeEdges(edges);
317 m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges);
318 return true;
319}
320
321bool QWaylandXdgSurface::move(QWaylandInputDevice *inputDevice)
322{
323 if (m_toplevel && m_toplevel->isInitialized()) {
324 m_toplevel->move(inputDevice->wl_seat(), inputDevice->serial());
325 return true;
326 }
327 return false;
328}
329
330bool QWaylandXdgSurface::showWindowMenu(QWaylandInputDevice *seat)
331{
332 if (m_toplevel && m_toplevel->isInitialized()) {
333 QPoint position = seat->pointerSurfacePosition().toPoint();
334 m_toplevel->show_window_menu(seat->wl_seat(), seat->serial(), position.x(), position.y());
335 return true;
336 }
337 return false;
338}
339
340void QWaylandXdgSurface::setTitle(const QString &title)
341{
342 if (m_toplevel)
343 m_toplevel->set_title(title);
344}
345
346void QWaylandXdgSurface::setAppId(const QString &appId)
347{
348 if (m_toplevel)
349 m_toplevel->set_app_id(appId);
350
351 m_appId = appId;
352}
353
354void QWaylandXdgSurface::setWindowFlags(Qt::WindowFlags flags)
355{
356 if (m_toplevel)
357 m_toplevel->requestWindowFlags(flags);
358}
359
360bool QWaylandXdgSurface::isExposed() const
361{
362 return m_configured || m_pendingConfigureSerial;
363}
364
365bool QWaylandXdgSurface::handleExpose(const QRegion &region)
366{
367 if (!isExposed() && !region.isEmpty()) {
368 m_exposeRegion = region;
369 return true;
370 }
371 return false;
372}
373
374void QWaylandXdgSurface::applyConfigure()
375{
376 // It is a redundant ack_configure, so skipped.
377 if (m_pendingConfigureSerial == m_appliedConfigureSerial)
378 return;
379
380 if (m_toplevel)
381 m_toplevel->applyConfigure();
382 if (m_popup)
383 m_popup->applyConfigure();
384 m_appliedConfigureSerial = m_pendingConfigureSerial;
385
386 m_configured = true;
387 ack_configure(m_appliedConfigureSerial);
388}
389
390bool QWaylandXdgSurface::wantsDecorations() const
391{
392 return m_toplevel && m_toplevel->wantsDecorations();
393}
394
395void QWaylandXdgSurface::propagateSizeHints()
396{
397 setSizeHints();
398
399 if (m_toplevel && m_window)
400 m_window->commit();
401}
402
403void QWaylandXdgSurface::setWindowGeometry(const QRect &rect)
404{
405 set_window_geometry(rect.x(), rect.y(), rect.width(), rect.height());
406}
407
408void QWaylandXdgSurface::setSizeHints()
409{
410 if (m_toplevel && m_window) {
411 const int minWidth = qMax(0, m_window->windowMinimumSize().width());
412 const int minHeight = qMax(0, m_window->windowMinimumSize().height());
413 int maxWidth = qMax(0, m_window->windowMaximumSize().width());
414 int maxHeight = qMax(0, m_window->windowMaximumSize().height());
415 if (maxWidth == QWINDOWSIZE_MAX)
416 maxWidth = 0;
417 if (maxHeight == QWINDOWSIZE_MAX)
418 maxHeight = 0;
419
420 // It will not change min/max sizes if invalid.
421 if (minWidth > maxWidth || minHeight > maxHeight)
422 return;
423
424 m_toplevel->set_min_size(minWidth, minHeight);
425 m_toplevel->set_max_size(maxWidth, maxHeight);
426 }
427}
428
429void *QWaylandXdgSurface::nativeResource(const QByteArray &resource)
430{
431 QByteArray lowerCaseResource = resource.toLower();
432 if (lowerCaseResource == "xdg_surface")
433 return object();
434 else if (lowerCaseResource == "xdg_toplevel" && m_toplevel)
435 return m_toplevel->object();
436 else if (lowerCaseResource == "xdg_popup" && m_popup)
437 return m_popup->object();
438 return nullptr;
439}
440
441std::any QWaylandXdgSurface::surfaceRole() const
442{
443 if (m_toplevel)
444 return m_toplevel->object();
445 if (m_popup)
446 return m_popup->object();
447 return {};
448}
449
450void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states)
451{
452 if (m_toplevel)
453 m_toplevel->requestWindowStates(states);
454 else
455 qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel zxdg_surface_v6.";
456}
457
458void QWaylandXdgSurface::setToplevel()
459{
460 Q_ASSERT(!m_toplevel && !m_popup);
461 m_toplevel = new Toplevel(this);
462}
463
464void QWaylandXdgSurface::setPopup(QWaylandWindow *parent)
465{
466 Q_ASSERT(!m_toplevel && !m_popup);
467
468 auto positioner = new QtWayland::xdg_positioner(m_shell->m_xdgWmBase->create_positioner());
469 // set_popup expects a position relative to the parent
470 QRect windowGeometry = m_window->windowContentGeometry();
471 QMargins windowMargins = m_window->windowContentMargins() - m_window->clientSideMargins();
472 QMargins parentMargins = parent->windowContentMargins() - parent->clientSideMargins();
473
474 // These property overrides may be removed when public API becomes available
475 QRect placementAnchor = m_window->window()->property("_q_waylandPopupAnchorRect").toRect();
476 if (!placementAnchor.isValid()) {
477 placementAnchor = QRect(m_window->geometry().topLeft() - parent->geometry().topLeft(), QSize(1,1));
478 }
479 placementAnchor.translate(dx: windowMargins.left(), dy: windowMargins.top());
480 placementAnchor.translate(dx: -parentMargins.left(), dy: -parentMargins.top());
481
482 uint32_t anchor = QtWayland::xdg_positioner::anchor_top_right;
483 const QVariant anchorVariant = m_window->window()->property("_q_waylandPopupAnchor");
484 if (anchorVariant.isValid()) {
485 switch (anchorVariant.value<Qt::Edges>()) {
486 case Qt::Edges():
487 anchor = QtWayland::xdg_positioner::anchor_none;
488 break;
489 case Qt::TopEdge:
490 anchor = QtWayland::xdg_positioner::anchor_top;
491 break;
492 case Qt::TopEdge | Qt::RightEdge:
493 anchor = QtWayland::xdg_positioner::anchor_top_right;
494 break;
495 case Qt::RightEdge:
496 anchor = QtWayland::xdg_positioner::anchor_right;
497 break;
498 case Qt::BottomEdge | Qt::RightEdge:
499 anchor = QtWayland::xdg_positioner::anchor_bottom_right;
500 break;
501 case Qt::BottomEdge:
502 anchor = QtWayland::xdg_positioner::anchor_bottom;
503 break;
504 case Qt::BottomEdge | Qt::LeftEdge:
505 anchor = QtWayland::xdg_positioner::anchor_bottom_left;
506 break;
507 case Qt::LeftEdge:
508 anchor = QtWayland::xdg_positioner::anchor_left;
509 break;
510 case Qt::TopEdge | Qt::LeftEdge:
511 anchor = QtWayland::xdg_positioner::anchor_top_left;
512 break;
513 }
514 }
515
516 uint32_t gravity = QtWayland::xdg_positioner::gravity_bottom_right;
517 const QVariant popupGravityVariant = m_window->window()->property("_q_waylandPopupGravity");
518 if (popupGravityVariant.isValid()) {
519 switch (popupGravityVariant.value<Qt::Edges>()) {
520 case Qt::Edges():
521 gravity = QtWayland::xdg_positioner::gravity_none;
522 break;
523 case Qt::TopEdge:
524 gravity = QtWayland::xdg_positioner::gravity_top;
525 break;
526 case Qt::TopEdge | Qt::RightEdge:
527 gravity = QtWayland::xdg_positioner::gravity_top_right;
528 break;
529 case Qt::RightEdge:
530 gravity = QtWayland::xdg_positioner::gravity_right;
531 break;
532 case Qt::BottomEdge | Qt::RightEdge:
533 gravity = QtWayland::xdg_positioner::gravity_bottom_right;
534 break;
535 case Qt::BottomEdge:
536 gravity = QtWayland::xdg_positioner::gravity_bottom;
537 break;
538 case Qt::BottomEdge | Qt::LeftEdge:
539 gravity = QtWayland::xdg_positioner::gravity_bottom_left;
540 break;
541 case Qt::LeftEdge:
542 gravity = QtWayland::xdg_positioner::gravity_left;
543 break;
544 case Qt::TopEdge | Qt::LeftEdge:
545 gravity = QtWayland::xdg_positioner::gravity_top_left;
546 break;
547 }
548 }
549
550 uint32_t constraintAdjustment = QtWayland::xdg_positioner::constraint_adjustment_slide_x | QtWayland::xdg_positioner::constraint_adjustment_slide_y;
551 const QVariant constraintAdjustmentVariant = m_window->window()->property("_q_waylandPopupConstraintAdjustment");
552 if (constraintAdjustmentVariant.isValid()) {
553 constraintAdjustment = constraintAdjustmentVariant.toUInt();
554 }
555
556 positioner->set_anchor_rect(placementAnchor.x(),
557 placementAnchor.y(),
558 placementAnchor.width(),
559 placementAnchor.height());
560 positioner->set_anchor(anchor);
561 positioner->set_gravity(gravity);
562 positioner->set_size(windowGeometry.width(), windowGeometry.height());
563 positioner->set_constraint_adjustment(constraintAdjustment);
564 m_popup = new Popup(this, parent, positioner);
565 positioner->destroy();
566
567 delete positioner;
568}
569
570void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevice *device, int serial)
571{
572 auto parentXdgSurface = qobject_cast<QWaylandXdgSurface *>(object: parent->shellSurface());
573 auto *top = m_shell->m_topmostGrabbingPopup;
574
575 if (top && top->m_xdgSurface != parentXdgSurface) {
576 qCWarning(lcQpaWayland) << "setGrabPopup called with a parent," << parentXdgSurface
577 << "which does not match the current topmost grabbing popup,"
578 << top->m_xdgSurface << "According to the xdg-shell protocol, this"
579 << "is not allowed. The wayland QPA plugin is currently handling"
580 << "it by setting the parent to the topmost grabbing popup."
581 << "Note, however, that this may cause positioning errors and"
582 << "popups closing unxpectedly because xdg-shell mandate that child"
583 << "popups close before parents";
584 parent = top->m_xdgSurface->m_window;
585 }
586 setPopup(parent);
587 m_popup->grab(seat: device, serial);
588
589 // Synthesize Qt enter/leave events for popup
590 if (!parent)
591 return;
592 QWindow *current = QGuiApplication::topLevelAt(pos: QCursor::pos());
593 QWindow *leave = parent->window();
594 if (current != leave)
595 return;
596
597 QWindowSystemInterface::handleLeaveEvent(window: leave);
598
599 QWindow *enter = nullptr;
600 if (m_popup && m_popup->m_xdgSurface && m_popup->m_xdgSurface->window())
601 enter = m_popup->m_xdgSurface->window()->window();
602
603 if (enter)
604 QWindowSystemInterface::handleEnterEvent(window: enter, local: enter->mapFromGlobal(pos: QCursor::pos()), global: QCursor::pos());
605}
606
607void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial)
608{
609 m_pendingConfigureSerial = serial;
610 if (!m_configured) {
611 // We have to do the initial applyConfigure() immediately, since that is the expose.
612 applyConfigure();
613 m_exposeRegion = QRegion(QRect(QPoint(), m_window->geometry().size()));
614 } else {
615 // Later configures are probably resizes, so we have to queue them up for a time when we
616 // are not painting to the window.
617 m_window->applyConfigureWhenPossible();
618 }
619
620 if (!m_exposeRegion.isEmpty()) {
621 m_window->handleExpose(region: m_exposeRegion);
622 m_exposeRegion = QRegion();
623 }
624}
625
626bool QWaylandXdgSurface::requestActivate()
627{
628 if (auto *activation = m_shell->activation()) {
629 if (!m_activationToken.isEmpty()) {
630 activation->activate(m_activationToken, window()->wlSurface());
631 m_activationToken = {};
632 return true;
633 } else if (const auto token = qEnvironmentVariable(varName: "XDG_ACTIVATION_TOKEN"); !token.isEmpty()) {
634 activation->activate(token, window()->wlSurface());
635 qunsetenv(varName: "XDG_ACTIVATION_TOKEN");
636 return true;
637 } else {
638 const auto focusWindow = QGuiApplication::focusWindow();
639 // At least GNOME requires to request the token in order to get the
640 // focus stealing prevention indication, so requestXdgActivationToken call
641 // is still necessary in that case.
642 const auto wlWindow = focusWindow ? static_cast<QWaylandWindow*>(focusWindow->handle()) : m_window;
643 if (const auto xdgSurface = qobject_cast<QWaylandXdgSurface *>(object: wlWindow->shellSurface())) {
644 if (const auto seat = wlWindow->display()->lastInputDevice()) {
645 const auto tokenProvider = activation->requestXdgActivationToken(
646 wlWindow->display(), wlWindow->wlSurface(), seat->serial(), xdgSurface->m_appId);
647 connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
648 [this, tokenProvider](const QString &token) {
649 m_shell->activation()->activate(token, window()->wlSurface());
650 tokenProvider->deleteLater();
651 });
652 return true;
653 }
654 }
655 }
656 }
657 return false;
658}
659
660void QWaylandXdgSurface::requestXdgActivationToken(quint32 serial)
661{
662 if (auto *activation = m_shell->activation()) {
663 auto tokenProvider = activation->requestXdgActivationToken(
664 m_shell->m_display, m_window->wlSurface(), serial, m_appId);
665 connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
666 [this, tokenProvider](const QString &token) {
667 Q_EMIT m_window->xdgActivationTokenCreated(token);
668 tokenProvider->deleteLater();
669 });
670 } else {
671 QWaylandShellSurface::requestXdgActivationToken(serial);
672 }
673}
674
675void QWaylandXdgSurface::setXdgActivationToken(const QString &token)
676{
677 if (m_shell->activation()) {
678 m_activationToken = token;
679 } else {
680 qCWarning(lcQpaWayland) << "zxdg_activation_v1 not available";
681 }
682}
683
684void QWaylandXdgSurface::setAlertState(bool enabled)
685{
686 if (m_alertState == enabled)
687 return;
688
689 m_alertState = enabled;
690
691 if (!m_alertState)
692 return;
693
694 auto *activation = m_shell->activation();
695 if (!activation)
696 return;
697
698 const auto tokenProvider = activation->requestXdgActivationToken(
699 m_shell->m_display, m_window->wlSurface(), std::nullopt, m_appId);
700 connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
701 [this, tokenProvider](const QString &token) {
702 m_shell->activation()->activate(token, m_window->wlSurface());
703 tokenProvider->deleteLater();
704 });
705}
706
707QString QWaylandXdgSurface::externWindowHandle()
708{
709 if (!m_toplevel || !m_shell->exporter()) {
710 return QString();
711 }
712 if (!m_toplevel->m_exported) {
713 m_toplevel->m_exported.reset(other: m_shell->exporter()->exportToplevel(surface: m_window->wlSurface()));
714 // handle events is sent immediately
715 m_shell->display()->forceRoundTrip();
716 }
717 return m_toplevel->m_exported->handle();
718}
719
720QWaylandXdgShell::QWaylandXdgShell(QWaylandDisplay *display, QtWayland::xdg_wm_base *xdgWmBase)
721 : m_display(display), m_xdgWmBase(xdgWmBase)
722{
723 display->addRegistryListener(listener: &QWaylandXdgShell::handleRegistryGlobal, data: this);
724}
725
726QWaylandXdgShell::~QWaylandXdgShell()
727{
728 m_display->removeListener(listener: &QWaylandXdgShell::handleRegistryGlobal, data: this);
729}
730
731void QWaylandXdgShell::handleRegistryGlobal(void *data, wl_registry *registry, uint id,
732 const QString &interface, uint version)
733{
734 QWaylandXdgShell *xdgShell = static_cast<QWaylandXdgShell *>(data);
735 if (interface == QLatin1String(QWaylandXdgDecorationManagerV1::interface()->name))
736 xdgShell->m_xdgDecorationManager.reset(new QWaylandXdgDecorationManagerV1(registry, id, version));
737
738 if (interface == QLatin1String(QWaylandXdgActivationV1::interface()->name)) {
739 xdgShell->m_xdgActivation.reset(new QWaylandXdgActivationV1(registry, id, version));
740 }
741
742 if (interface == QLatin1String(QWaylandXdgExporterV2::interface()->name)) {
743 xdgShell->m_xdgExporter.reset(other: new QWaylandXdgExporterV2(registry, id, version));
744 }
745}
746
747}
748
749QT_END_NAMESPACE
750
751#include "moc_qwaylandxdgshell_p.cpp"
752

source code of qtwayland/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp