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
6
24
25#include <QtCore/QFileInfo>
26#include <QtCore/QPointer>
27#include <QtCore/QRegularExpression>
28#include <QtGui/QWindow>
29
30#include <QGuiApplication>
31#include <qpa/qwindowsysteminterface.h>
32#include <QtGui/private/qguiapplication_p.h>
33#include <QtGui/private/qwindow_p.h>
34
35#include <QtCore/QDebug>
36#include <QtCore/QThread>
37
38#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
39
41
42using namespace Qt::StringLiterals;
43
44namespace QtWaylandClient {
45
46Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore")
47
50
76
78{
80 reset();
81
82 const QWindow *parent = window();
83 const auto tlw = QGuiApplication::topLevelWindows();
84 for (QWindow *w : tlw) {
85 if (w->transientParent() == parent)
87 }
88
89 if (mMouseGrab == this) {
90 mMouseGrab = nullptr;
91 }
92}
93
101
103{
105
108
109 auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
110 if (!parent->mSurface)
112 if (parent->wlSurface()) {
115 }
116 } else if (shouldCreateShellSurface()) {
120 if (mTransientParent) {
121 if (window()->type() == Qt::Popup) {
123 qCWarning(lcQpaWayland) << "Creating a popup with a parent," << mTransientParent->window()
124 << "which does not match the current topmost grabbing popup,"
125 << mTopPopup->window() << "With some shell surface protocols, this"
126 << "is not allowed. The wayland QPA plugin is currently handling"
127 << "it by setting the parent to the topmost grabbing popup."
128 << "Note, however, that this may cause positioning errors and"
129 << "popups closing unxpectedly. Please fix the transient parent of the popup.";
131 }
132 mTopPopup = this;
133 }
134 }
135
137 if (mShellSurface) {
138 if (mTransientParent) {
139 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup || window()->type() == Qt::Tool)
141 }
142
143 // Set initial surface title
146
147 // The appId is the desktop entry identifier that should follow the
148 // reverse DNS convention (see
149 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
150 // to xdg-shell the appId is only the name, without the .desktop suffix.
151 //
152 // If the application specifies the desktop file name use that,
153 // otherwise fall back to the executable name and prepend the
154 // reversed organization domain when available.
157 } else {
162
163 if (domainName.isEmpty()) {
165 } else {
167 for (int i = 0; i < domainName.size(); ++i)
171 }
172 }
173 // the user may have already set some window properties, so make sure to send them out
174 for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it)
176
178 } else {
179 qWarning("Could not create a shell surface object.");
180 }
181 }
182
185
186 // Enable high-dpi rendering. Scale() returns the screen scale factor and will
187 // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
188 // to inform the compositor that high-resolution buffers will be provided.
189 if (mViewport)
191 else if (mSurface->version() >= 3)
193
196 if (geometry.width() <= 0)
198 if (geometry.height() <= 0)
200
202 setMask(window()->mask());
203 if (mShellSurface) {
206 }
208
210 mSurface->commit();
211}
212
214{
216}
217
250
267
274
276{
278 if (mShellSurface) {
279 qCWarning(lcQpaWayland) << "Cannot set shell integration while there's already a shell surface created";
280 return;
281 }
283}
284
286{
287 if (!shellIntegration())
288 return false;
289
291 return false;
292
293 if (window()->inherits("QShapedPixmapWindow"))
294 return false;
295
296 if (qEnvironmentVariableIsSet("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"))
297 return !(window()->flags() & Qt::BypassWindowManagerHint);
298
299 return true;
300}
301
303{
304 return QPlatformWindow::parent() != nullptr;
305}
306
312
314{
316 mInFrameRender = false;
317}
318
346
348{
349 // Old Reset
351
352 if (mTopPopup == this)
356 mTransientParent = nullptr;
357 delete std::exchange(mShellSurface, nullptr);
358 delete std::exchange(mSubSurfaceWindow, nullptr);
360
362 mInFrameRender = false;
364 mExposed = false;
365}
366
368{
369 {
371 if (mFrameCallback) {
373 mFrameCallback = nullptr;
374 }
377 }
381 }
383}
384
386{
388 return s->m_window;
389 return nullptr;
390}
391
393{
394 return reinterpret_cast<WId>(wlSurface());
395}
396
398{
399 if (lastParent == parent)
400 return;
401
402 if (mSubSurfaceWindow && parent) { // new parent, but we were a subsurface already
403 delete mSubSurfaceWindow;
404 QWaylandWindow *p = const_cast<QWaylandWindow *>(static_cast<const QWaylandWindow *>(parent));
406 } else if ((!lastParent && parent) || (lastParent && !parent)) {
407 // we're changing role, need to make a new wl_surface
408 reset();
410 if (window()->isVisible()) {
411 initWindow();
412 }
413 }
415}
416
418{
419 return mWindowTitle;
420}
421
423{
424 const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH
426
427 const int libwaylandMaxBufferSize = 4096;
428 // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
429 // Also, QString is in utf-16, which means that in the worst case each character will be
430 // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
431 const int maxLength = libwaylandMaxBufferSize / 3 - 100;
432
434 if (truncated.size() < formatted.size()) {
435 qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported."
436 << "Truncating window title (from" << formatted.size() << "chars)";
437 }
438
440
441 if (mShellSurface)
443
446}
447
457
459{
460 return QRect(QPoint(), QSize(500,500));
461}
462
464{
466 if (mViewport)
468
469 if (mSubSurfaceWindow) {
472
477 }
478 }
479}
480
482{
483 auto rect = r;
485 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool) {
487 }
489
494
495 else
497 }
498
499 if (mShellSurface)
501
502 if (isOpaque() && mMask.isEmpty())
504
505
506 if (window()->isVisible() && rect.isValid()) {
507 ensureSize();
510
512 mSentInitialResize = true;
513 }
514
515 // 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
516 // On other platforms (X11) the expose event would be received deferred from the X server
517 // we want our behaviour to match, and only run after control has returned to the event loop
519}
520
522{
523 if (!isExposed())
524 return;
527 return;
528
530}
531
533{
534 if (!mSurface)
535 return;
536
538
542
544 return;
545
548
550 mSurface->set_input_region(nullptr);
551 } else {
555 }
556}
557
559{
560 if (!surfaceSize().isEmpty())
562}
563
578
589
602
604{
605 static bool sQtTestMode = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING");
607
608 if (sQtTestMode) {
610 }
612
613 /**
614 * If an expose is not handled by application code, explicitly attach a buffer
615 * This primarily is a workaround for Qt unit tests using QWindow directly and
616 * wanting focus.
617 */
623 }
624}
625
627{
629 if (mSurface) {
630 if (auto *screen = mSurface->oldestEnteredScreen())
631 return screen;
632 }
633 return QPlatformWindow::screen();
634}
635
637{
638 // Workaround for issue where setVisible may be called with the same value twice
639 if (lastVisible == visible)
640 return;
642
643 if (visible) {
645 initWindow();
647 if (mShellSurface)
649 } else {
650 // make sure isExposed is false during the next event dispatch
651 mExposed = false;
654 mSurface->attach(nullptr, 0, 0);
655 mSurface->commit();
656 }
657}
658
659
661{
662 if (mShellSurface)
664}
665
666
668{
669 if (mShellSurface)
671}
672
674{
676 if (!mSurface)
677 return;
678
679 if (mMask == mask)
680 return;
681
682 mMask = mask;
683
685
686 if (isOpaque()) {
687 if (mMask.isEmpty())
689 else
691 }
692}
693
699
701{
702 if (mShellSurface)
703 return mShellSurface->isAlertState();
704
705 return false;
706}
707
715
717{
719 return;
720
722 "QWaylandWindow::applyConfigure", "not called from main thread");
723
724 // If we're mid paint, use an exposeEvent to flush the current frame.
725 // When this completes we know that no other frames will be rendering.
726 // This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
727 if (mInFrameRender)
729 if (mShellSurface)
731
733 if (mExposed)
735 else
736 // we still need to commit the configured ack for a hidden surface
737 commit();
738}
739
741{
743 if (mSurface == nullptr)
744 return;
745
746 if (buffer) {
748 handleUpdate();
749 buffer->setBusy(true);
751 mSurface->offset(x, y);
752 mSurface->attach(buffer->buffer(), 0, 0);
753 } else {
755 }
756 } else {
757 mSurface->attach(nullptr, 0, 0);
758 }
759}
760
766
768{
770 if (mSurface == nullptr)
771 return;
772
773 const qreal s = scale();
774 if (mSurface->version() >= 4) {
775 const QRect bufferRect =
776 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
777 .toAlignedRect();
780 } else {
782 }
783}
784
786{
787 if (isExposed()) {
789 } else {
790 buffer->setBusy(false);
791 }
792}
793
798
800{
802 if (buffer->committed()) {
803 mSurface->commit();
804 qCDebug(lcWaylandBackingstore) << "Buffer already committed, not attaching.";
805 return;
806 }
807
809 if (!mSurface)
810 return;
811
813 if (mSurface->version() >= 4) {
814 const qreal s = scale();
815 for (const QRect &rect : damage) {
816 const QRect bufferRect =
817 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
818 .toAlignedRect();
821 }
822 } else {
823 for (const QRect &rect: damage)
825 }
828 mSurface->commit();
829}
830
832{
834 if (mSurface != nullptr)
835 mSurface->commit();
836}
837
839 [](void *data, wl_callback *callback, uint32_t time) {
840 Q_UNUSED(time);
841 auto *window = static_cast<QWaylandWindow*>(data);
843 }
844};
845
847{
849 if (!mFrameCallback) {
850 // This means the callback is already unset by QWaylandWindow::reset.
851 // The wl_callback object will be destroyed there too.
852 return;
853 }
856 mFrameCallback = nullptr;
857
860
861 // The rest can wait until we can run it on the correct thread
863 // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
864 // in the single-threaded case.
866 }
868}
869
871{
873 bool wasExposed = isExposed();
875 // Did setting mFrameCallbackTimedOut make the window exposed?
879
880}
881
883{
885
888
890 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
893 }
894
896}
897
907
912
919
920/*!
921 * Size, with decorations (including including eventual shadows) in wl_surface coordinates
922 */
927
940
941/*!
942 * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
943 * topLeft is where the shadow stops and the decorations border start.
944 */
950
951/*!
952 * Converts from wl_surface coordinates to Qt window coordinates. Qt window
953 * coordinates start inside (not including) the window decorations, while
954 * wl_surface coordinates start at the first pixel of the buffer. Potentially,
955 * this should be in the window shadow, although we don't have those. So for
956 * now, it's the first pixel of the decorations.
957 */
963
965{
967 return mSurface ? mSurface->object() : nullptr;
968}
969
974
976{
978 return mSubSurfaceWindow->object();
979 if (mShellSurface)
980 return mShellSurface->surfaceRole();
981 return {};
982}
983
988
990{
994 return nullptr;
995 return static_cast<QWaylandScreen *>(platformScreen);
996}
997
1003
1005{
1007 if (mSurface == nullptr || mSurface->version() < 2)
1008 return;
1009
1012
1013 if (mSurface->version() >= 6) {
1015 if (auto screen = waylandScreen())
1017 } else {
1018 if (auto screen = window()->screen())
1020 }
1021
1023
1025 case Qt::PrimaryOrientation:
1027 break;
1030 break;
1031 case Qt::PortraitOrientation:
1033 break;
1036 break;
1039 break;
1040 default:
1041 Q_UNREACHABLE();
1042 }
1044}
1045
1051
1057
1059{
1060 const bool wasPopup = mFlags.testFlag(Qt::Popup);
1061 const bool isPopup = flags.testFlag(Qt::Popup);
1062
1063 mFlags = flags;
1064 // changing role is not allowed on XdgShell on the same wl_surface
1065 if (wasPopup != isPopup) {
1066 reset();
1068 if (window()->isVisible()) {
1069 initWindow();
1070 }
1071 } else {
1072 if (mShellSurface)
1074 }
1075
1077
1080}
1081
1083{
1084 return mFlags;
1085}
1086
1088{
1090 "QWaylandWindow::createDecoration", "not called from main thread");
1091 // TODO: client side decorations do not work with Vulkan backend.
1093 return false;
1095 return false;
1096
1097 static bool decorationPluginFailed = false;
1098 bool decoration = false;
1099 switch (window()->type()) {
1100 case Qt::Window:
1101 case Qt::Widget:
1102 case Qt::Dialog:
1103 case Qt::Tool:
1104 case Qt::Drawer:
1105 decoration = true;
1106 break;
1107 default:
1108 break;
1109 }
1111 decoration = false;
1113 decoration = false;
1115 decoration = false;
1117 decoration = false;
1118
1122 if (mWindowDecoration) {
1124 }
1125
1127 if (decorations.empty()) {
1128 qWarning() << "No decoration plugins available. Running with no decorations.";
1130 return false;
1131 }
1132
1134 QByteArray decorationPluginName = qgetenv("QT_WAYLAND_DECORATION");
1138 qWarning() << "Requested decoration " << targetKey << " not found, falling back to default";
1139 targetKey = QString(); // fallthrough
1140 }
1141 }
1142
1143 if (targetKey.isEmpty()) {
1144 auto unixServices = dynamic_cast<QDesktopUnixServices *>(
1147 if (desktopNames.contains("GNOME")) {
1148 if (decorations.contains("adwaita"_L1))
1149 targetKey = "adwaita"_L1;
1150 else if (decorations.contains("gnome"_L1))
1151 targetKey = "gnome"_L1;
1152 } else {
1153 // Do not use Adwaita/GNOME decorations on other DEs
1154 decorations.removeAll("adwaita"_L1);
1155 decorations.removeAll("gnome"_L1);
1156 }
1157 }
1158
1159 if (targetKey.isEmpty())
1160 targetKey = decorations.first(); // first come, first served.
1161
1163 if (!mWindowDecoration) {
1164 qWarning() << "Could not create decoration from factory! Running with no decorations.";
1166 return false;
1167 }
1170 }
1171 } else {
1173 }
1174
1179 subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
1180 }
1182
1183 // creating a decoration changes our margins which in turn change size hints
1185
1186 // This is a special case where the buffer is recreated, but since
1187 // the content rect remains the same, the widgets remain the same
1188 // size and are not redrawn, leaving the new buffer empty. As a simple
1189 // work-around, we trigger a full extra update whenever the client-side
1190 // window decorations are toggled while the window is showing.
1191 window()->requestUpdate();
1192 }
1193
1194 return mWindowDecoration.get();
1195}
1196
1201
1202static QWaylandWindow *closestShellSurfaceWindow(QWindow *window)
1203{
1204 while (window) {
1205 auto w = static_cast<QWaylandWindow *>(window->handle());
1206 if (w && w->shellSurface())
1207 return w;
1208 window = window->transientParent() ? window->transientParent() : window->parent();
1209 }
1210 return nullptr;
1211}
1212
1217
1219{
1220 // Take the closest window with a shell surface, since the transient parent may be a
1221 // QWidgetWindow or some other window without a shell surface, which is then not able to
1222 // get mouse events.
1224 return transientParent;
1225
1226 if (window()->type() == Qt::Popup) {
1227 if (mTopPopup)
1228 return mTopPopup;
1229 }
1230
1231 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) {
1232 if (auto lastInputWindow = display()->lastInputWindow())
1234 }
1235
1236 return nullptr;
1237}
1238
1240{
1241 // There's currently no way to get info about the actual hardware device in use.
1242 // At least we get the correct seat.
1244 if (e.type == QEvent::Leave) {
1248 } else {
1250 }
1251#if QT_CONFIG(cursor)
1253#endif
1254 return;
1255 }
1256
1257#if QT_CONFIG(cursor)
1258 if (e.type == QEvent::Enter) {
1260 }
1261#endif
1262
1265 } else {
1266 switch (e.type) {
1267 case QEvent::Enter:
1269#if QT_CONFIG(cursor)
1271#endif
1272 break;
1275 case QEvent::MouseMove:
1277 break;
1278 case QEvent::Wheel:
1281 e.phase, e.source, e.inverted);
1282 break;
1283 default:
1284 Q_UNREACHABLE();
1285 }
1286 }
1287}
1288
1289#ifndef QT_NO_GESTURES
1292{
1293 switch (e.state) {
1294 case Qt::GestureStarted:
1296 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1297
1299 // whole gesture sequence will be ignored
1301 return;
1302 }
1303
1308 e.local, e.global, e.fingers);
1309 break;
1310 case Qt::GestureUpdated:
1312 return;
1313
1314 if (!e.delta.isNull()) {
1318 0, e.delta, e.local, e.global, e.fingers);
1319 }
1320 break;
1321 case Qt::GestureFinished:
1322 case Qt::GestureCanceled:
1325 return;
1326 }
1327
1329 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1330
1332
1333 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1334 // this part of information is lost.
1338 e.local, e.global, e.fingers);
1339 break;
1340 default:
1341 break;
1342 }
1343}
1344
1347{
1348 switch (e.state) {
1349 case Qt::GestureStarted:
1351 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1352
1354 // whole gesture sequence will be ignored
1356 return;
1357 }
1358
1363 e.local, e.global, e.fingers);
1364 break;
1365 case Qt::GestureUpdated:
1367 return;
1368
1369 if (!e.delta.isNull()) {
1373 0, e.delta, e.local, e.global, e.fingers);
1374 }
1375 if (e.rotation_delta != 0) {
1380 e.local, e.global, e.fingers);
1381 }
1382 if (e.scale_delta != 0) {
1386 e.scale_delta,
1387 e.local, e.global, e.fingers);
1388 }
1389 break;
1390 case Qt::GestureFinished:
1391 case Qt::GestureCanceled:
1394 return;
1395 }
1396
1398 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1399
1401
1402 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1403 // this part of information is lost.
1407 e.local, e.global, e.fingers);
1408 break;
1409 default:
1410 break;
1411 }
1412}
1413#endif // #ifndef QT_NO_GESTURES
1414
1415
1422
1432
1434{
1435 // There's currently no way to get info about the actual hardware device in use.
1436 // At least we get the correct seat.
1443 }
1444 return;
1445 }
1446
1448 QRect windowRect(0 + marg.left(),
1449 0 + marg.top(),
1450 geometry().size().width(),
1451 geometry().size().height());
1458#if QT_CONFIG(cursor)
1460#endif
1462 }
1463
1464 switch (e.type) {
1465 case QEvent::Enter:
1467#if QT_CONFIG(cursor)
1469#endif
1470 break;
1473 case QEvent::MouseMove:
1475 break;
1476 case QEvent::Wheel: {
1480 e.phase, e.source, e.inverted);
1481 break;
1482 }
1483 default:
1484 Q_UNREACHABLE();
1485 }
1486
1489 } else {
1493 }
1494 }
1495}
1496
1498{
1500
1501 if (!newScreen || newScreen->screen() == window()->screen())
1502 return;
1503
1505
1507 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool
1508 && geometry().topLeft() != newScreen->geometry().topLeft()) {
1509 auto geometry = this->geometry();
1512 }
1513
1514 updateScale();
1516}
1517
1519{
1520 if (mFractionalScale) {
1525 return;
1526 }
1527
1528 if (mSurface && mSurface->version() >= 6) {
1532 return;
1533 }
1534
1535 int scale = screen()->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(screen())->scale();
1536 setScale(scale);
1537}
1538
1540{
1542 return;
1543 mScale = newScale;
1544
1545 if (mSurface) {
1546 if (mViewport)
1548 else if (mSurface->version() >= 3)
1550 }
1551 ensureSize();
1552
1554 if (isExposed()) {
1555 // redraw at the new DPR
1556 window()->requestUpdate();
1558 }
1559}
1560
1561#if QT_CONFIG(cursor)
1563{
1566 } else if (mHasStoredCursor) {
1568 } else {
1570 }
1571}
1572
1574{
1575 mHasStoredCursor = false;
1576}
1577
1578// Keep track of application set cursors on each window
1581 mHasStoredCursor = true;
1582}
1583
1585 if (!device || !device->pointer() || device->pointer()->focusWindow() != this)
1586 return;
1587
1590}
1591#endif
1592
1598
1600{
1601 if (!window()->isVisible())
1602 return false;
1603
1605 return false;
1606
1607 if (mShellSurface)
1608 return mShellSurface->isExposed();
1609
1611 return mSubSurfaceWindow->parent()->isExposed();
1612
1614}
1615
1617{
1618 bool exposed = calculateExposure();
1619 if (exposed == mExposed)
1620 return;
1621
1622 mExposed = exposed;
1623
1624 if (!exposed)
1626 else
1628
1630 auto subWindow = subSurface->window();
1632 }
1633}
1634
1636{
1637 return mExposed;
1638}
1639
1641{
1642 return mDisplay->isWindowActivated(this);
1643}
1644
1646{
1647 return devicePixelRatio();
1648}
1649
1654
1656{
1657 if (window()->type() != Qt::Popup) {
1658 qWarning("This plugin supports grabbing the mouse only for popup windows");
1659 return false;
1660 }
1661
1662 mMouseGrab = grab ? this : nullptr;
1663 return true;
1664}
1665
1670
1675
1680
1690
1700
1708
1710{
1711 return m_properties;
1712}
1713
1718
1723
1724#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1726{
1727 mBackingStore = dynamic_cast<QWaylandShmBackingStore *>(store);
1728}
1729#endif
1730
1732{
1734 return;
1735
1736 {
1738
1744 }
1746 return;
1747 }
1749 }
1750
1751 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
1754}
1755
1757{
1758 qCDebug(lcWaylandBackingstore) << "requestUpdate";
1759 Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
1760
1761 // If we have a frame callback all is good and will be taken care of there
1762 {
1765 return;
1766 }
1767
1768 // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1769 // so use invokeMethod to delay the delivery a bit.
1770 QMetaObject::invokeMethod(this, [this] {
1771 // Things might have changed in the meantime
1772 {
1775 return;
1776 }
1779 }, Qt::QueuedConnection);
1780}
1781
1782// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1783// with eglSwapBuffers) to know when it's time to commit the next one.
1784// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1786{
1788 qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
1789
1790 // TODO: Should sync subsurfaces avoid requesting frame callbacks?
1792 if (!mSurface)
1793 return;
1794
1797 return;
1798
1799 struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1805
1806 // Start a timer for handling the case when the compositor stops sending frame callbacks.
1807 if (mFrameCallbackTimeout > 0) {
1808 QMetaObject::invokeMethod(this, [this] {
1810
1815 }
1816 }, Qt::QueuedConnection);
1817 }
1818}
1819
1821{
1822 qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1824}
1825
1827{
1828 mOffset += point;
1829}
1830
1836
1838{
1839 if (auto *seat = display()->lastInputDevice()) {
1842 return rc;
1843 }
1844 return false;
1845}
1846
1848{
1849 if (auto seat = display()->lastInputDevice()) {
1852 return rc;
1853 }
1854 return false;
1855}
1856
1857bool QWaylandWindow::isOpaque() const
1858{
1859 return window()->requestedFormat().alphaBufferSize() <= 0;
1860}
1861
1863{
1865
1867 return;
1868
1870
1874}
1875
1877{
1878 if (!mShellSurface) {
1879 qCWarning(lcQpaWayland) << "requestXdgActivationToken is called with no surface role created, emitting synthetic signal";
1881 return;
1882 }
1884}
1885
1887{
1888 if (mShellSurface)
1890 else
1891 qCWarning(lcQpaWayland) << "setXdgActivationToken is called with no surface role created, token" << token << "discarded";
1892}
1893
1895{
1896 if (mShellSurface)
1899}
1900
1902{
1903 if (mShellSurface)
1906}
1907
1909 while (!mChildPopups.isEmpty()) {
1910 auto popup = mChildPopups.takeLast();
1912 }
1913}
1914
1916{
1917 if (window()->isVisible()) {
1918 initWindow();
1921 }
1922}
1923
1925{
1930 } else if (event->type() == QEvent::WindowUnblocked) {
1931 // QtGui sends leave event to window under cursor when modal window opens, so we have
1932 // to send enter event when modal closes and window has cursor and gets unblocked.
1934 const auto pos = mDisplay->waylandCursor()->pos();
1936 }
1937 }
1938
1940}
1941
1943{
1944 return mSurfaceFormat;
1945}
1946
1951
1956
1960
1962{
1964}
1965
1972
1974{
1976}
1977
1978}
1979
1980QT_END_NAMESPACE
1981
1982#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)