Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
25
26#include <QtCore/QFileInfo>
27#include <QtCore/QPointer>
28#include <QtCore/QRegularExpression>
29#include <QtGui/QWindow>
30
31#include <QGuiApplication>
32#include <qpa/qwindowsysteminterface.h>
33#include <QtGui/private/qguiapplication_p.h>
34#include <QtGui/private/qwindow_p.h>
35
36#include <QtCore/QDebug>
37#include <QtCore/QThread>
38
39#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
40
42
43using namespace Qt::StringLiterals;
44
45namespace QtWaylandClient {
46
47Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore")
48
51
52/*!
53 \class QtWaylandClient::QWaylandWindow
54 \internal
55*/
81
83{
85 reset();
86
87 const QWindow *parent = window();
88 const auto tlw = QGuiApplication::topLevelWindows();
89 for (QWindow *w : tlw) {
90 if (w->transientParent() == parent)
92 }
93
94 if (mMouseGrab == this) {
95 mMouseGrab = nullptr;
96 }
97}
98
106
108{
110
113
114 auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
115 if (!parent->mSurface)
117 if (parent->wlSurface()) {
120 }
121 } else if (shouldCreateShellSurface()) {
125 if (mTransientParent) {
126 if (window()->type() == Qt::Popup) {
128 qCWarning(lcQpaWayland) << "Creating a popup with a parent," << mTransientParent->window()
129 << "which does not match the current topmost grabbing popup,"
130 << mTopPopup->window() << "With some shell surface protocols, this"
131 << "is not allowed. The wayland QPA plugin is currently handling"
132 << "it by setting the parent to the topmost grabbing popup."
133 << "Note, however, that this may cause positioning errors and"
134 << "popups closing unxpectedly. Please fix the transient parent of the popup.";
136 }
137 mTopPopup = this;
138 }
139 }
140
142 if (mShellSurface) {
143 if (mTransientParent) {
144 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup || window()->type() == Qt::Tool)
146 }
147
148 // Set initial surface title
151
152 // The appId is the desktop entry identifier that should follow the
153 // reverse DNS convention (see
154 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
155 // to xdg-shell the appId is only the name, without the .desktop suffix.
156 //
157 // If the application specifies the desktop file name use that,
158 // otherwise fall back to the executable name and prepend the
159 // reversed organization domain when available.
162 } else {
167
168 if (domainName.isEmpty()) {
170 } else {
172 for (int i = 0; i < domainName.size(); ++i)
176 }
177 }
178 // the user may have already set some window properties, so make sure to send them out
179 for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it)
181
183 } else {
184 qWarning("Could not create a shell surface object.");
185 }
186 }
187
190
191 // Enable high-dpi rendering. Scale() returns the screen scale factor and will
192 // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
193 // to inform the compositor that high-resolution buffers will be provided.
194 if (mViewport)
196 else if (mSurface->version() >= 3)
198
201 if (geometry.width() <= 0)
203 if (geometry.height() <= 0)
205
207 setMask(window()->mask());
208 if (mShellSurface) {
211 }
213
215 mSurface->commit();
216}
217
219{
221}
222
255
272
279
281{
283 if (mShellSurface) {
284 qCWarning(lcQpaWayland) << "Cannot set shell integration while there's already a shell surface created";
285 return;
286 }
288}
289
291{
292 if (!shellIntegration())
293 return false;
294
296 return false;
297
298 if (window()->inherits("QShapedPixmapWindow"))
299 return false;
300
301 if (qEnvironmentVariableIsSet("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"))
302 return !(window()->flags() & Qt::BypassWindowManagerHint);
303
304 return true;
305}
306
308{
309 return QPlatformWindow::parent() != nullptr;
310}
311
317
319{
321 mInFrameRender = false;
322}
323
325{
327
328 if (mSurface) {
329 {
336
337 // Destroy surface last.
338 // Some compositors (e.g. ChromeOS Sommelier) crash if they receive
339 // destroy requests for these objects after the associated surface
340 // has already been destroyed.
341 mSurface.reset();
342 }
344 }
345
346
347 mScale = std::nullopt;
349 mMask = QRegion();
350
353
355}
356
358{
359 // Old Reset
361
362 if (mTopPopup == this)
366 mTransientParent = nullptr;
367 delete std::exchange(mShellSurface, nullptr);
368 delete std::exchange(mSubSurfaceWindow, nullptr);
370
372 mInFrameRender = false;
374 mExposed = false;
375}
376
378{
379 {
381 if (mFrameCallback) {
383 mFrameCallback = nullptr;
384 }
387 }
391 }
393}
394
396{
398 return s->m_window;
399 return nullptr;
400}
401
403{
404 return reinterpret_cast<WId>(wlSurface());
405}
406
408{
409 if (lastParent == parent)
410 return;
411
412 if (mSubSurfaceWindow && parent) { // new parent, but we were a subsurface already
413 delete mSubSurfaceWindow;
414 QWaylandWindow *p = const_cast<QWaylandWindow *>(static_cast<const QWaylandWindow *>(parent));
416 } else if ((!lastParent && parent) || (lastParent && !parent)) {
417 // we're changing role, need to make a new wl_surface
418 reset();
420 if (window()->isVisible()) {
421 initWindow();
422 }
423 }
425}
426
428{
429 return mWindowTitle;
430}
431
433{
434 const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH
436
437 const int libwaylandMaxBufferSize = 4096;
438 // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
439 // Also, QString is in utf-16, which means that in the worst case each character will be
440 // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
441 const int maxLength = libwaylandMaxBufferSize / 3 - 100;
442
444 if (truncated.size() < formatted.size()) {
445 qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported."
446 << "Truncating window title (from" << formatted.size() << "chars)";
447 }
448
450
451 if (mShellSurface)
453
456}
457
467
469{
470 return QRect(QPoint(), QSize(500,500));
471}
472
474{
476 if (mViewport)
478
479 if (mSubSurfaceWindow) {
482
487 }
488 }
489}
490
492{
493 auto rect = r;
495 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool) {
497 }
499
504
505 else
507 }
508
509 if (mShellSurface)
511
512 if (isOpaque() && mMask.isEmpty())
514
515
516 if (window()->isVisible() && rect.isValid()) {
517 ensureSize();
520
522 }
523
524 // Wayland has no concept of areas being exposed or not, only the entire window, when our geometry changes, we need to flag the new area as exposed
525 // On other platforms (X11) the expose event would be received deferred from the X server
526 // we want our behaviour to match, and only run after control has returned to the event loop
528}
529
531{
532 if (!isExposed())
533 return;
536 return;
537
539}
540
542{
543 if (!mSurface)
544 return;
545
547
551
553 return;
554
557
559 mSurface->set_input_region(nullptr);
560 } else {
564 }
565}
566
568{
569 if (!surfaceSize().isEmpty())
571}
572
587
598
611
613{
614 static bool sQtTestMode = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING");
616
617 if (sQtTestMode) {
619 }
621
622 /**
623 * If an expose is not handled by application code, explicitly attach a buffer
624 * This primarily is a workaround for Qt unit tests using QWindow directly and
625 * wanting focus.
626 */
630 commit(&buffer, QRegion());
631 }
632}
633
635{
637 if (mSurface) {
638 if (auto *screen = mSurface->oldestEnteredScreen())
639 return screen;
640 }
641 return QPlatformWindow::screen();
642}
643
645{
646 // Workaround for issue where setVisible may be called with the same value twice
647 if (lastVisible == visible)
648 return;
650
651 if (visible) {
653 initWindow();
655 if (mShellSurface)
657 } else {
658 // make sure isExposed is false during the next event dispatch
659 mExposed = false;
662 mSurface->attach(nullptr, 0, 0);
663 mSurface->commit();
664 }
665}
666
667
669{
670 if (mShellSurface)
672}
673
674
676{
677 if (mShellSurface)
679}
680
682{
684 if (!mSurface)
685 return;
686
687 if (mMask == mask)
688 return;
689
690 mMask = mask;
691
693
694 if (isOpaque()) {
695 if (mMask.isEmpty())
697 else
699 }
700}
701
707
709{
710 if (mShellSurface)
711 return mShellSurface->isAlertState();
712
713 return false;
714}
715
723
725{
727 return;
728
730 "QWaylandWindow::applyConfigure", "not called from main thread");
731
732 // If we're mid paint, use an exposeEvent to flush the current frame.
733 // When this completes we know that no other frames will be rendering.
734 // This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
735 if (mInFrameRender)
737 if (mShellSurface)
739
741 if (mExposed)
743 else
744 // we still need to commit the configured ack for a hidden surface
745 commit();
746}
747
749{
751 if (mSurface == nullptr)
752 return;
753
754 if (buffer) {
756 handleUpdate();
757 buffer->setBusy(true);
759 mSurface->offset(x, y);
760 mSurface->attach(buffer->buffer(), 0, 0);
761 } else {
763 }
764 } else {
765 mSurface->attach(nullptr, 0, 0);
766 }
767}
768
774
776{
778 if (mSurface == nullptr)
779 return;
780
781 const qreal s = scale();
782 if (mSurface->version() >= 4) {
783 const QRect bufferRect =
784 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
785 .toAlignedRect();
788 } else {
790 }
791}
792
794{
795 if (isExposed()) {
797 } else {
798 buffer->setBusy(false);
799 }
800}
801
806
808{
810 if (buffer->committed()) {
811 mSurface->commit();
812 qCDebug(lcWaylandBackingstore) << "Buffer already committed, not attaching.";
813 return;
814 }
815
817 if (!mSurface)
818 return;
819
821 if (mSurface->version() >= 4) {
822 const qreal s = scale();
823 for (const QRect &rect : damage) {
824 const QRect bufferRect =
825 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
826 .toAlignedRect();
829 }
830 } else {
831 for (const QRect &rect: damage)
833 }
836 mSurface->commit();
837}
838
840{
842 if (mSurface != nullptr)
843 mSurface->commit();
844}
845
847 [](void *data, wl_callback *callback, uint32_t time) {
848 Q_UNUSED(time);
849 auto *window = static_cast<QWaylandWindow*>(data);
851 }
852};
853
855{
857 if (mFrameCallback != callback) {
858 // This means the callback is already unset by QWaylandWindow::reset.
859 // The wl_callback object will be destroyed there too.
860 return;
861 }
863 mFrameCallback = nullptr;
865
866 // The rest can wait until we can run it on the correct thread
868 // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
869 // in the single-threaded case.
871 }
873}
874
876{
878 bool wasExposed = isExposed();
880 // Did setting mFrameCallbackTimedOut make the window exposed?
884
885}
886
888{
890
893
894 if (mFrameCallback) {
895 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
898 }
899
900 return !mFrameCallback;
901}
902
912
917
924
925/*!
926 * Size, with decorations (including including eventual shadows) in wl_surface coordinates
927 */
932
945
946/*!
947 * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
948 * topLeft is where the shadow stops and the decorations border start.
949 */
955
956/*!
957 * Converts from wl_surface coordinates to Qt window coordinates. Qt window
958 * coordinates start inside (not including) the window decorations, while
959 * wl_surface coordinates start at the first pixel of the buffer. Potentially,
960 * this should be in the window shadow, although we don't have those. So for
961 * now, it's the first pixel of the decorations.
962 */
968
970{
972 return mSurface ? mSurface->object() : nullptr;
973}
974
979
981{
983 return mSubSurfaceWindow->object();
984 if (mShellSurface)
985 return mShellSurface->surfaceRole();
986 return {};
987}
988
993
995{
999 return nullptr;
1000 return static_cast<QWaylandScreen *>(platformScreen);
1001}
1002
1008
1010{
1012 if (mSurface == nullptr || mSurface->version() < 2)
1013 return;
1014
1017
1018 if (mSurface->version() >= 6) {
1020 if (auto screen = waylandScreen())
1022 } else {
1023 if (auto screen = window()->screen())
1025 }
1026
1028
1030 case Qt::PrimaryOrientation:
1032 break;
1035 break;
1036 case Qt::PortraitOrientation:
1038 break;
1041 break;
1044 break;
1045 default:
1046 Q_UNREACHABLE();
1047 }
1049}
1050
1056
1062
1064{
1065 const bool wasPopup = mFlags.testFlag(Qt::Popup);
1066 const bool isPopup = flags.testFlag(Qt::Popup);
1067
1068 mFlags = flags;
1069 // changing role is not allowed on XdgShell on the same wl_surface
1070 if (wasPopup != isPopup) {
1071 reset();
1073 if (window()->isVisible()) {
1074 initWindow();
1075 }
1076 } else {
1077 if (mShellSurface)
1079 }
1080
1082
1085}
1086
1088{
1089 return mFlags;
1090}
1091
1093{
1095 "QWaylandWindow::createDecoration", "not called from main thread");
1096 // TODO: client side decorations do not work with Vulkan backend.
1098 return false;
1100 return false;
1101
1102 static bool decorationPluginFailed = false;
1103 bool decoration = false;
1104 switch (window()->type()) {
1105 case Qt::Window:
1106 case Qt::Widget:
1107 case Qt::Dialog:
1108 case Qt::Tool:
1109 case Qt::Drawer:
1110 decoration = true;
1111 break;
1112 default:
1113 break;
1114 }
1116 decoration = false;
1118 decoration = false;
1120 decoration = false;
1122 decoration = false;
1123
1127 if (mWindowDecoration) {
1129 }
1130
1132 if (decorations.empty()) {
1133 qWarning() << "No decoration plugins available. Running with no decorations.";
1135 return false;
1136 }
1137
1139 QByteArray decorationPluginName = qgetenv("QT_WAYLAND_DECORATION");
1143 qWarning() << "Requested decoration " << targetKey << " not found, falling back to default";
1144 targetKey = QString(); // fallthrough
1145 }
1146 }
1147
1148 if (targetKey.isEmpty()) {
1149 auto unixServices = dynamic_cast<QDesktopUnixServices *>(
1152 if (desktopNames.contains("GNOME")) {
1153 if (decorations.contains("adwaita"_L1))
1154 targetKey = "adwaita"_L1;
1155 else if (decorations.contains("gnome"_L1))
1156 targetKey = "gnome"_L1;
1157 } else {
1158 // Do not use Adwaita/GNOME decorations on other DEs
1159 decorations.removeAll("adwaita"_L1);
1160 decorations.removeAll("gnome"_L1);
1161 }
1162 }
1163
1164 if (targetKey.isEmpty())
1165 targetKey = decorations.first(); // first come, first served.
1166
1168 if (!mWindowDecoration) {
1169 qWarning() << "Could not create decoration from factory! Running with no decorations.";
1171 return false;
1172 }
1175 }
1176 } else {
1178 }
1179
1184 subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
1185 }
1187
1188 // creating a decoration changes our margins which in turn change size hints
1190
1191 // This is a special case where the buffer is recreated, but since
1192 // the content rect remains the same, the widgets remain the same
1193 // size and are not redrawn, leaving the new buffer empty. As a simple
1194 // work-around, we trigger a full extra update whenever the client-side
1195 // window decorations are toggled while the window is showing.
1196 window()->requestUpdate();
1197 }
1198
1199 return mWindowDecoration.get();
1200}
1201
1206
1207static QWaylandWindow *closestShellSurfaceWindow(QWindow *window)
1208{
1209 while (window) {
1210 auto w = static_cast<QWaylandWindow *>(window->handle());
1211 if (w && w->shellSurface())
1212 return w;
1213 window = window->transientParent() ? window->transientParent() : window->parent();
1214 }
1215 return nullptr;
1216}
1217
1222
1224{
1225 // Take the closest window with a shell surface, since the transient parent may be a
1226 // QWidgetWindow or some other window without a shell surface, which is then not able to
1227 // get mouse events.
1229 return transientParent;
1230
1231 if (window()->type() == Qt::Popup) {
1232 if (mTopPopup)
1233 return mTopPopup;
1234 }
1235
1236 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) {
1237 if (auto lastInputWindow = display()->lastInputWindow())
1239 }
1240
1241 return nullptr;
1242}
1243
1245{
1246 // There's currently no way to get info about the actual hardware device in use.
1247 // At least we get the correct seat.
1249 if (e.type == QEvent::Leave) {
1253 } else {
1255 }
1256#if QT_CONFIG(cursor)
1258#endif
1259 return;
1260 }
1261
1262#if QT_CONFIG(cursor)
1263 if (e.type == QEvent::Enter) {
1265 }
1266#endif
1267
1270 } else {
1271 switch (e.type) {
1272 case QEvent::Enter:
1274#if QT_CONFIG(cursor)
1276#endif
1277 break;
1280 case QEvent::MouseMove:
1282 break;
1283 case QEvent::Wheel:
1286 e.phase, e.source, e.inverted);
1287 break;
1288 default:
1289 Q_UNREACHABLE();
1290 }
1291 }
1292}
1293
1294#ifndef QT_NO_GESTURES
1297{
1298 switch (e.state) {
1299 case Qt::GestureStarted:
1301 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1302
1304 // whole gesture sequence will be ignored
1306 return;
1307 }
1308
1313 e.local, e.global, e.fingers);
1314 break;
1315 case Qt::GestureUpdated:
1317 return;
1318
1319 if (!e.delta.isNull()) {
1323 0, e.delta, e.local, e.global, e.fingers);
1324 }
1325 break;
1326 case Qt::GestureFinished:
1327 case Qt::GestureCanceled:
1330 return;
1331 }
1332
1334 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1335
1337
1338 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1339 // this part of information is lost.
1343 e.local, e.global, e.fingers);
1344 break;
1345 default:
1346 break;
1347 }
1348}
1349
1352{
1353 switch (e.state) {
1354 case Qt::GestureStarted:
1356 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1357
1359 // whole gesture sequence will be ignored
1361 return;
1362 }
1363
1368 e.local, e.global, e.fingers);
1369 break;
1370 case Qt::GestureUpdated:
1372 return;
1373
1374 if (!e.delta.isNull()) {
1378 0, e.delta, e.local, e.global, e.fingers);
1379 }
1380 if (e.rotation_delta != 0) {
1385 e.local, e.global, e.fingers);
1386 }
1387 if (e.scale_delta != 0) {
1391 e.scale_delta,
1392 e.local, e.global, e.fingers);
1393 }
1394 break;
1395 case Qt::GestureFinished:
1396 case Qt::GestureCanceled:
1399 return;
1400 }
1401
1403 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1404
1406
1407 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1408 // this part of information is lost.
1412 e.local, e.global, e.fingers);
1413 break;
1414 default:
1415 break;
1416 }
1417}
1418#endif // #ifndef QT_NO_GESTURES
1419
1420
1427
1437
1439{
1440 // There's currently no way to get info about the actual hardware device in use.
1441 // At least we get the correct seat.
1448 }
1449 return;
1450 }
1451
1453 QRect windowRect(0 + marg.left(),
1454 0 + marg.top(),
1455 geometry().size().width(),
1456 geometry().size().height());
1463#if QT_CONFIG(cursor)
1465#endif
1467 }
1468
1469 switch (e.type) {
1470 case QEvent::Enter:
1472#if QT_CONFIG(cursor)
1474#endif
1475 break;
1478 case QEvent::MouseMove:
1480 break;
1481 case QEvent::Wheel: {
1485 e.phase, e.source, e.inverted);
1486 break;
1487 }
1488 default:
1489 Q_UNREACHABLE();
1490 }
1491
1494 } else {
1498 }
1499 }
1500}
1501
1503{
1505
1506 if (!newScreen || newScreen->screen() == window()->screen())
1507 return;
1508
1510
1512 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool
1513 && geometry().topLeft() != newScreen->geometry().topLeft()) {
1514 auto geometry = this->geometry();
1517 }
1518
1519 updateScale();
1521}
1522
1524{
1525 if (mFractionalScale) {
1530 return;
1531 }
1532
1533 if (mSurface && mSurface->version() >= 6) {
1537 return;
1538 }
1539
1540 int scale = screen()->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(screen())->scale();
1541 setScale(scale);
1542}
1543
1545{
1547 return;
1548 mScale = newScale;
1549
1550 if (mSurface) {
1551 if (mViewport)
1553 else if (mSurface->version() >= 3)
1555 }
1556 ensureSize();
1557
1559 if (isExposed()) {
1560 // redraw at the new DPR
1561 window()->requestUpdate();
1563 }
1564}
1565
1566#if QT_CONFIG(cursor)
1568{
1571 } else if (mHasStoredCursor) {
1573 } else {
1575 }
1576}
1577
1579{
1580 mHasStoredCursor = false;
1581}
1582
1583// Keep track of application set cursors on each window
1586 mHasStoredCursor = true;
1587}
1588
1590 if (!device || !device->pointer() || device->pointer()->focusSurface() != waylandSurface())
1591 return;
1592
1595}
1596#endif
1597
1603
1605{
1606 if (!window()->isVisible())
1607 return false;
1608
1610 return false;
1611
1612 if (mShellSurface)
1613 return mShellSurface->isExposed();
1614
1616 return mSubSurfaceWindow->parent()->isExposed();
1617
1619}
1620
1622{
1623 bool exposed = calculateExposure();
1624 if (exposed == mExposed)
1625 return;
1626
1627 mExposed = exposed;
1628
1629 if (!exposed)
1631 else
1633
1635 auto subWindow = subSurface->window();
1637 }
1638}
1639
1641{
1642 return mExposed;
1643}
1644
1646{
1647 return mDisplay->isWindowActivated(this);
1648}
1649
1651{
1652 return devicePixelRatio();
1653}
1654
1659
1661{
1662 if (window()->type() != Qt::Popup) {
1663 qWarning("This plugin supports grabbing the mouse only for popup windows");
1664 return false;
1665 }
1666
1667 mMouseGrab = grab ? this : nullptr;
1668 return true;
1669}
1670
1675
1680
1685
1695
1705
1713
1715{
1716 return m_properties;
1717}
1718
1723
1728
1729#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1731{
1732 mBackingStore = dynamic_cast<QWaylandShmBackingStore *>(store);
1733}
1734#endif
1735
1737{
1739 return;
1740
1741 {
1743
1749 }
1751 return;
1752 }
1754 }
1755
1756 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
1759}
1760
1762{
1763 qCDebug(lcWaylandBackingstore) << "requestUpdate";
1764 Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
1765
1766 // If we have a frame callback all is good and will be taken care of there
1767 {
1769 if (mFrameCallback)
1770 return;
1771 }
1772
1773 // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1774 // so use invokeMethod to delay the delivery a bit.
1775 QMetaObject::invokeMethod(this, [this] {
1776 // Things might have changed in the meantime
1777 {
1779 if (mFrameCallback)
1780 return;
1781 }
1784 }, Qt::QueuedConnection);
1785}
1786
1787// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1788// with eglSwapBuffers) to know when it's time to commit the next one.
1789// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1791{
1793 qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
1794
1795 // TODO: Should sync subsurfaces avoid requesting frame callbacks?
1797 if (!mSurface)
1798 return;
1799
1801 if (mFrameCallback)
1802 return;
1803
1804 struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1809
1810 // Start a timer for handling the case when the compositor stops sending frame callbacks.
1811 if (mFrameCallbackTimeout > 0) {
1812 QMetaObject::invokeMethod(this, [this] {
1814
1815 if (mFrameCallback) {
1819 }
1820 }, Qt::QueuedConnection);
1821 }
1822}
1823
1825{
1826 qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1828}
1829
1831{
1832 mOffset += point;
1833}
1834
1840
1842{
1843 if (auto *seat = display()->lastInputDevice()) {
1846 return rc;
1847 }
1848 return false;
1849}
1850
1852{
1853 if (auto seat = display()->lastInputDevice()) {
1856 return rc;
1857 }
1858 return false;
1859}
1860
1861bool QWaylandWindow::isOpaque() const
1862{
1863 return window()->requestedFormat().alphaBufferSize() <= 0;
1864}
1865
1867{
1869
1871 return;
1872
1874
1878}
1879
1881{
1882 if (!mShellSurface) {
1883 qCWarning(lcQpaWayland) << "requestXdgActivationToken is called with no surface role created, emitting synthetic signal";
1885 return;
1886 }
1888}
1889
1891{
1892 if (mShellSurface)
1894 else
1895 qCWarning(lcQpaWayland) << "setXdgActivationToken is called with no surface role created, token" << token << "discarded";
1896}
1897
1899{
1900 if (mShellSurface)
1903}
1904
1906{
1907 if (mShellSurface)
1910}
1911
1913 while (!mChildPopups.isEmpty()) {
1914 auto popup = mChildPopups.takeLast();
1916 }
1917}
1918
1920{
1921 if (window()->isVisible()) {
1922 initWindow();
1925 }
1926}
1927
1929{
1934 } else if (event->type() == QEvent::WindowUnblocked) {
1935 // QtGui sends leave event to window under cursor when modal window opens, so we have
1936 // to send enter event when modal closes and window has cursor and gets unblocked.
1938 const auto pos = mDisplay->waylandCursor()->pos();
1940 }
1941 }
1942
1944}
1945
1947{
1948 return mSurfaceFormat;
1949}
1950
1955
1960
1964
1966{
1968}
1969
1976
1978{
1980}
1981
1983{
1984 if (!window() || window()->flags() & Qt::WindowDoesNotAcceptFocus)
1985 return;
1986
1988 const bool isSubsurface = !!subSurfaceWindow();
1989
1990 auto toplevelIsActive = [&](QWaylandWindow *w) -> bool {
1991 while (w && w->subSurfaceWindow())
1992 w = static_cast<QWaylandWindow *>(w->QPlatformWindow::parent());
1993 return mDisplay->isWindowActivated(w);
1994 };
1995
1999 mDisplay->setActiveSubSurface(nullptr);
2000}
2001
2002}
2003
2004QT_END_NAMESPACE
2005
2006#include "moc_qwaylandwindow_p.cpp"
Combined button and popup list for selecting options.
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")
static QWaylandWindow * closestShellSurfaceWindow(QWindow *window)