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
484 if (parentWindow)
486 }
487}
488
490{
491 auto rect = r;
493 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool) {
495 }
497
502
503 else
505 }
506
507 if (mShellSurface)
509
510 if (isOpaque() && mMask.isEmpty())
512
513
514 if (window()->isVisible() && rect.isValid()) {
515 ensureSize();
518
520 }
521
522 // 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
523 // On other platforms (X11) the expose event would be received deferred from the X server
524 // we want our behaviour to match, and only run after control has returned to the event loop
526}
527
529{
530 if (!isExposed())
531 return;
534 return;
535
537}
538
540{
541 if (!mSurface)
542 return;
543
545
549
551 return;
552
555
557 mSurface->set_input_region(nullptr);
558 } else {
562 }
563}
564
566{
567 if (!surfaceSize().isEmpty())
569}
570
585
596
609
611{
612 static bool sQtTestMode = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING");
614
615 if (sQtTestMode) {
617 }
619
620 /**
621 * If an expose is not handled by application code, explicitly attach a buffer
622 * This primarily is a workaround for Qt unit tests using QWindow directly and
623 * wanting focus.
624 */
628 commit(&buffer, QRegion());
629 }
630}
631
633{
635 if (mSurface) {
636 if (auto *screen = mSurface->oldestEnteredScreen())
637 return screen;
638 }
639 return QPlatformWindow::screen();
640}
641
643{
644 // Workaround for issue where setVisible may be called with the same value twice
645 if (lastVisible == visible)
646 return;
648
649 if (visible) {
651 initWindow();
653 if (mShellSurface)
655 } else {
656 // make sure isExposed is false during the next event dispatch
657 mExposed = false;
660 mSurface->attach(nullptr, 0, 0);
661 mSurface->commit();
662 }
663}
664
665
667{
668 if (mShellSurface)
670}
671
672
674{
675 if (mShellSurface)
677}
678
680{
682 if (!mSurface)
683 return;
684
685 if (mMask == mask)
686 return;
687
688 mMask = mask;
689
691
692 if (isOpaque()) {
693 if (mMask.isEmpty())
695 else
697 }
698}
699
705
707{
708 if (mShellSurface)
709 return mShellSurface->isAlertState();
710
711 return false;
712}
713
721
723{
725 return;
726
728 "QWaylandWindow::applyConfigure", "not called from main thread");
729
730 // If we're mid paint, use an exposeEvent to flush the current frame.
731 // When this completes we know that no other frames will be rendering.
732 // This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
733 if (mInFrameRender)
735 if (mShellSurface)
737
739 if (mExposed)
741 else
742 // we still need to commit the configured ack for a hidden surface
743 commit();
744}
745
747{
749 if (mSurface == nullptr)
750 return;
751
752 if (buffer) {
754 handleUpdate();
755 buffer->setBusy(true);
757 mSurface->offset(x, y);
758 mSurface->attach(buffer->buffer(), 0, 0);
759 } else {
761 }
762 } else {
763 mSurface->attach(nullptr, 0, 0);
764 }
765}
766
772
774{
776 if (mSurface == nullptr)
777 return;
778
779 const qreal s = scale();
780 if (mSurface->version() >= 4) {
781 const QRect bufferRect =
782 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
783 .toAlignedRect();
786 } else {
788 }
789}
790
792{
793 if (isExposed()) {
795 } else {
796 buffer->setBusy(false);
797 }
798}
799
804
806{
808
810 if (buffer->committed()) {
811 mSurface->commit();
812 qCDebug(lcWaylandBackingstore) << "Buffer already committed, not attaching.";
813 return;
814 }
815
816 if (!mSurface)
817 return;
818
820 if (mSurface->version() >= 4) {
821 const qreal s = scale();
822 for (const QRect &rect : damage) {
823 const QRect bufferRect =
824 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
825 .toAlignedRect();
828 }
829 } else {
830 for (const QRect &rect: damage)
832 }
835 mSurface->commit();
836}
837
839{
841 if (mSurface != nullptr)
842 mSurface->commit();
843}
844
846 [](void *data, wl_callback *callback, uint32_t time) {
847 Q_UNUSED(time);
848 auto *window = static_cast<QWaylandWindow*>(data);
850 }
851};
852
854{
856 if (mFrameCallback != callback) {
857 // This means the callback is already unset by QWaylandWindow::reset.
858 // The wl_callback object will be destroyed there too.
859 return;
860 }
862 mFrameCallback = nullptr;
864
865 // The rest can wait until we can run it on the correct thread
867 // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
868 // in the single-threaded case.
870 }
872}
873
875{
877 bool wasExposed = isExposed();
879 // Did setting mFrameCallbackTimedOut make the window exposed?
883
884}
885
887{
889
892
893 if (mFrameCallback) {
894 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
897 }
898
899 return !mFrameCallback;
900}
901
911
916
923
924/*!
925 * Size, with decorations (including including eventual shadows) in wl_surface coordinates
926 */
931
944
945/*!
946 * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
947 * topLeft is where the shadow stops and the decorations border start.
948 */
954
955/*!
956 * Converts from wl_surface coordinates to Qt window coordinates. Qt window
957 * coordinates start inside (not including) the window decorations, while
958 * wl_surface coordinates start at the first pixel of the buffer. Potentially,
959 * this should be in the window shadow, although we don't have those. So for
960 * now, it's the first pixel of the decorations.
961 */
967
969{
971 return mSurface ? mSurface->object() : nullptr;
972}
973
978
980{
982 return mSubSurfaceWindow->object();
983 if (mShellSurface)
984 return mShellSurface->surfaceRole();
985 return {};
986}
987
992
994{
998 return nullptr;
999 return static_cast<QWaylandScreen *>(platformScreen);
1000}
1001
1007
1009{
1011 if (mSurface == nullptr || mSurface->version() < 2)
1012 return;
1013
1016
1017 if (mSurface->version() >= 6) {
1019 if (auto screen = waylandScreen())
1021 } else {
1022 if (auto screen = window()->screen())
1024 }
1025
1027
1029 case Qt::PrimaryOrientation:
1031 break;
1034 break;
1035 case Qt::PortraitOrientation:
1037 break;
1040 break;
1043 break;
1044 default:
1045 Q_UNREACHABLE();
1046 }
1048}
1049
1055
1061
1063{
1064 const bool wasPopup = mFlags.testFlag(Qt::Popup);
1065 const bool isPopup = flags.testFlag(Qt::Popup);
1066
1067 mFlags = flags;
1068 // changing role is not allowed on XdgShell on the same wl_surface
1069 if (wasPopup != isPopup) {
1070 reset();
1072 if (window()->isVisible()) {
1073 initWindow();
1074 }
1075 } else {
1076 if (mShellSurface)
1078 }
1079
1081
1084}
1085
1087{
1088 return mFlags;
1089}
1090
1092{
1094 "QWaylandWindow::createDecoration", "not called from main thread");
1095 // TODO: client side decorations do not work with Vulkan backend.
1097 return false;
1099 return false;
1100
1101 static bool decorationPluginFailed = false;
1102 bool decoration = false;
1103 switch (window()->type()) {
1104 case Qt::Window:
1105 case Qt::Widget:
1106 case Qt::Dialog:
1107 case Qt::Tool:
1108 case Qt::Drawer:
1109 decoration = true;
1110 break;
1111 default:
1112 break;
1113 }
1115 decoration = false;
1117 decoration = false;
1119 decoration = false;
1121 decoration = false;
1122
1126 if (mWindowDecoration) {
1128 }
1129
1131 if (decorations.empty()) {
1132 qWarning() << "No decoration plugins available. Running with no decorations.";
1134 return false;
1135 }
1136
1138 QByteArray decorationPluginName = qgetenv("QT_WAYLAND_DECORATION");
1142 qWarning() << "Requested decoration " << targetKey << " not found, falling back to default";
1143 targetKey = QString(); // fallthrough
1144 }
1145 }
1146
1147 if (targetKey.isEmpty()) {
1148 auto unixServices = dynamic_cast<QDesktopUnixServices *>(
1151 if (desktopNames.contains("GNOME")) {
1152 if (decorations.contains("adwaita"_L1))
1153 targetKey = "adwaita"_L1;
1154 else if (decorations.contains("gnome"_L1))
1155 targetKey = "gnome"_L1;
1156 } else {
1157 // Do not use Adwaita/GNOME decorations on other DEs
1158 decorations.removeAll("adwaita"_L1);
1159 decorations.removeAll("gnome"_L1);
1160 }
1161 }
1162
1163 if (targetKey.isEmpty())
1164 targetKey = decorations.first(); // first come, first served.
1165
1167 if (!mWindowDecoration) {
1168 qWarning() << "Could not create decoration from factory! Running with no decorations.";
1170 return false;
1171 }
1174 }
1175 } else {
1177 }
1178
1183 subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
1184 }
1186
1187 // creating a decoration changes our margins which in turn change size hints
1189
1190 // This is a special case where the buffer is recreated, but since
1191 // the content rect remains the same, the widgets remain the same
1192 // size and are not redrawn, leaving the new buffer empty. As a simple
1193 // work-around, we trigger a full extra update whenever the client-side
1194 // window decorations are toggled while the window is showing.
1196 }
1197
1198 return mWindowDecoration.get();
1199}
1200
1205
1206static QWaylandWindow *closestShellSurfaceWindow(QWindow *window)
1207{
1208 while (window) {
1209 auto w = static_cast<QWaylandWindow *>(window->handle());
1210 if (w && w->shellSurface())
1211 return w;
1212 window = window->transientParent() ? window->transientParent() : window->parent();
1213 }
1214 return nullptr;
1215}
1216
1221
1223{
1224 // Take the closest window with a shell surface, since the transient parent may be a
1225 // QWidgetWindow or some other window without a shell surface, which is then not able to
1226 // get mouse events.
1228 return transientParent;
1229
1230 if (window()->type() == Qt::Popup) {
1231 if (mTopPopup)
1232 return mTopPopup;
1233 }
1234
1235 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) {
1236 if (auto lastInputWindow = display()->lastInputWindow())
1238 }
1239
1240 return nullptr;
1241}
1242
1244{
1245 // There's currently no way to get info about the actual hardware device in use.
1246 // At least we get the correct seat.
1248 if (e.type == QEvent::Leave) {
1252 } else {
1254 }
1255#if QT_CONFIG(cursor)
1257#endif
1258 return;
1259 }
1260
1261#if QT_CONFIG(cursor)
1262 if (e.type == QEvent::Enter) {
1264 }
1265#endif
1266
1269 } else {
1270 switch (e.type) {
1271 case QEvent::Enter:
1273#if QT_CONFIG(cursor)
1275#endif
1276 break;
1279 case QEvent::MouseMove:
1281 break;
1282 case QEvent::Wheel:
1285 e.phase, e.source, e.inverted);
1286 break;
1287 default:
1288 Q_UNREACHABLE();
1289 }
1290 }
1291}
1292
1293#ifndef QT_NO_GESTURES
1296{
1297 switch (e.state) {
1298 case Qt::GestureStarted:
1300 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1301
1303 // whole gesture sequence will be ignored
1305 return;
1306 }
1307
1312 e.local, e.global, e.fingers);
1313 break;
1314 case Qt::GestureUpdated:
1316 return;
1317
1318 if (!e.delta.isNull()) {
1322 0, e.delta, e.local, e.global, e.fingers);
1323 }
1324 break;
1325 case Qt::GestureFinished:
1326 case Qt::GestureCanceled:
1329 return;
1330 }
1331
1333 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1334
1336
1337 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1338 // this part of information is lost.
1342 e.local, e.global, e.fingers);
1343 break;
1344 default:
1345 break;
1346 }
1347}
1348
1351{
1352 switch (e.state) {
1353 case Qt::GestureStarted:
1355 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1356
1358 // whole gesture sequence will be ignored
1360 return;
1361 }
1362
1367 e.local, e.global, e.fingers);
1368 break;
1369 case Qt::GestureUpdated:
1371 return;
1372
1373 if (!e.delta.isNull()) {
1377 0, e.delta, e.local, e.global, e.fingers);
1378 }
1379 if (e.rotation_delta != 0) {
1384 e.local, e.global, e.fingers);
1385 }
1386 if (e.scale_delta != 0) {
1390 e.scale_delta,
1391 e.local, e.global, e.fingers);
1392 }
1393 break;
1394 case Qt::GestureFinished:
1395 case Qt::GestureCanceled:
1398 return;
1399 }
1400
1402 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1403
1405
1406 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1407 // this part of information is lost.
1411 e.local, e.global, e.fingers);
1412 break;
1413 default:
1414 break;
1415 }
1416}
1417#endif // #ifndef QT_NO_GESTURES
1418
1419
1426
1436
1438{
1439 // There's currently no way to get info about the actual hardware device in use.
1440 // At least we get the correct seat.
1447 }
1448 return;
1449 }
1450
1452 QRect windowRect(0 + marg.left(),
1453 0 + marg.top(),
1454 geometry().size().width(),
1455 geometry().size().height());
1462#if QT_CONFIG(cursor)
1464#endif
1466 }
1467
1468 switch (e.type) {
1469 case QEvent::Enter:
1471#if QT_CONFIG(cursor)
1473#endif
1474 break;
1477 case QEvent::MouseMove:
1479 break;
1480 case QEvent::Wheel: {
1484 e.phase, e.source, e.inverted);
1485 break;
1486 }
1487 default:
1488 Q_UNREACHABLE();
1489 }
1490
1493 } else {
1497 }
1498 }
1499}
1500
1502{
1504
1505 if (!newScreen || newScreen->screen() == window()->screen())
1506 return;
1507
1509
1511 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool
1512 && geometry().topLeft() != newScreen->geometry().topLeft()) {
1513 auto geometry = this->geometry();
1516 }
1517
1518 updateScale();
1520}
1521
1523{
1524 if (mFractionalScale) {
1529 return;
1530 }
1531
1532 if (mSurface && mSurface->version() >= 6) {
1536 return;
1537 }
1538
1539 int scale = screen()->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(screen())->scale();
1540 setScale(scale);
1541}
1542
1544{
1546 return;
1547 mScale = newScale;
1548
1549 if (mSurface) {
1550 if (mViewport)
1552 else if (mSurface->version() >= 3)
1554 }
1555 ensureSize();
1556
1558 if (isExposed()) {
1559 // redraw at the new DPR
1560 window()->requestUpdate();
1562 }
1563}
1564
1565#if QT_CONFIG(cursor)
1567{
1570 } else if (mHasStoredCursor) {
1572 } else {
1574 }
1575}
1576
1578{
1579 mHasStoredCursor = false;
1580}
1581
1582// Keep track of application set cursors on each window
1585 mHasStoredCursor = true;
1586}
1587
1589 if (!device || !device->pointer() || device->pointer()->focusSurface() != waylandSurface())
1590 return;
1591
1594}
1595#endif
1596
1602
1604{
1605 if (!window()->isVisible())
1606 return false;
1607
1609 return false;
1610
1611 if (mShellSurface)
1612 return mShellSurface->isExposed();
1613
1615 return mSubSurfaceWindow->parent()->isExposed();
1616
1618}
1619
1621{
1622 bool exposed = calculateExposure();
1623 if (exposed == mExposed)
1624 return;
1625
1626 mExposed = exposed;
1627
1628 if (!exposed)
1630 else
1632
1634 auto subWindow = subSurface->window();
1636 }
1637}
1638
1640{
1641 return mExposed;
1642}
1643
1645{
1646 return mDisplay->isWindowActivated(this);
1647}
1648
1650{
1651 return devicePixelRatio();
1652}
1653
1658
1660{
1661 if (window()->type() != Qt::Popup) {
1662 qWarning("This plugin supports grabbing the mouse only for popup windows");
1663 return false;
1664 }
1665
1666 mMouseGrab = grab ? this : nullptr;
1667 return true;
1668}
1669
1674
1679
1684
1694
1704
1712
1714{
1715 return m_properties;
1716}
1717
1722
1727
1728#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1730{
1731 mBackingStore = dynamic_cast<QWaylandShmBackingStore *>(store);
1732}
1733#endif
1734
1736{
1738 return;
1739
1740 {
1742
1748 }
1750 return;
1751 }
1753 }
1754
1755 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
1758}
1759
1761{
1762 qCDebug(lcWaylandBackingstore) << "requestUpdate";
1763 Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
1764
1765 // If we have a frame callback all is good and will be taken care of there
1766 {
1768 if (mFrameCallback)
1769 return;
1770 }
1771
1772 // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1773 // so use invokeMethod to delay the delivery a bit.
1774 QMetaObject::invokeMethod(this, [this] {
1775 // Things might have changed in the meantime
1776 {
1778 if (mFrameCallback)
1779 return;
1780 }
1783 }, Qt::QueuedConnection);
1784}
1785
1786// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1787// with eglSwapBuffers) to know when it's time to commit the next one.
1788// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1790{
1792 qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
1793
1794 // TODO: Should sync subsurfaces avoid requesting frame callbacks?
1796 if (!mSurface)
1797 return;
1798
1800 if (mFrameCallback)
1801 return;
1802
1803 struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1808
1809 // Start a timer for handling the case when the compositor stops sending frame callbacks.
1810 if (mFrameCallbackTimeout > 0) {
1811 QMetaObject::invokeMethod(this, [this] {
1813
1814 if (mFrameCallback) {
1818 }
1819 }, Qt::QueuedConnection);
1820 }
1821}
1822
1828
1830{
1831 qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1833
1834 // an update request doesn't always force a commit if the window isn't damaged, so also force it
1836 mPendingForcedUpdate = false;
1839 }
1840}
1841
1843{
1844 mOffset += point;
1845}
1846
1852
1854{
1855 if (auto *seat = display()->lastInputDevice()) {
1858 return rc;
1859 }
1860 return false;
1861}
1862
1864{
1865 if (auto seat = display()->lastInputDevice()) {
1868 return rc;
1869 }
1870 return false;
1871}
1872
1873bool QWaylandWindow::isOpaque() const
1874{
1875 return window()->requestedFormat().alphaBufferSize() <= 0;
1876}
1877
1879{
1881
1883 return;
1884
1886
1890}
1891
1893{
1894 if (!mShellSurface) {
1895 qCWarning(lcQpaWayland) << "requestXdgActivationToken is called with no surface role created, emitting synthetic signal";
1897 return;
1898 }
1900}
1901
1903{
1904 if (mShellSurface)
1906 else
1907 qCWarning(lcQpaWayland) << "setXdgActivationToken is called with no surface role created, token" << token << "discarded";
1908}
1909
1911{
1912 if (mShellSurface)
1915}
1916
1918{
1919 if (mShellSurface)
1922}
1923
1925 while (!mChildPopups.isEmpty()) {
1926 auto popup = mChildPopups.takeLast();
1928 }
1929}
1930
1932{
1933 if (window()->isVisible()) {
1934 initWindow();
1937 }
1938}
1939
1941{
1946 } else if (event->type() == QEvent::WindowUnblocked) {
1947 // QtGui sends leave event to window under cursor when modal window opens, so we have
1948 // to send enter event when modal closes and window has cursor and gets unblocked.
1950 const auto pos = mDisplay->waylandCursor()->pos();
1952 }
1953 }
1954
1956}
1957
1959{
1960 return mSurfaceFormat;
1961}
1962
1967
1972
1976
1978{
1980}
1981
1988
1990{
1992}
1993
1995{
1996 if (!window() || window()->flags() & Qt::WindowDoesNotAcceptFocus)
1997 return;
1998
2000 const bool isSubsurface = !!subSurfaceWindow();
2001
2002 auto toplevelIsActive = [&](QWaylandWindow *w) -> bool {
2003 while (w && w->subSurfaceWindow())
2004 w = static_cast<QWaylandWindow *>(w->QPlatformWindow::parent());
2005 return mDisplay->isWindowActivated(w);
2006 };
2007
2011 mDisplay->setActiveSubSurface(nullptr);
2012}
2013
2014}
2015
2016QT_END_NAMESPACE
2017
2018#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)