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
75
77{
79 reset();
80
81 const QWindow *parent = window();
82 const auto tlw = QGuiApplication::topLevelWindows();
83 for (QWindow *w : tlw) {
84 if (w->transientParent() == parent)
86 }
87
88 if (mMouseGrab == this) {
89 mMouseGrab = nullptr;
90 }
91}
92
100
102{
104
107
108 auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
109 if (!parent->mSurface)
111 if (parent->wlSurface()) {
114 }
115 } else if (shouldCreateShellSurface()) {
119 if (mTransientParent) {
120 if (window()->type() == Qt::Popup) {
122 qCWarning(lcQpaWayland) << "Creating a popup with a parent," << mTransientParent->window()
123 << "which does not match the current topmost grabbing popup,"
124 << mTopPopup->window() << "With some shell surface protocols, this"
125 << "is not allowed. The wayland QPA plugin is currently handling"
126 << "it by setting the parent to the topmost grabbing popup."
127 << "Note, however, that this may cause positioning errors and"
128 << "popups closing unxpectedly. Please fix the transient parent of the popup.";
130 }
131 mTopPopup = this;
132 }
133 }
134
136 if (mShellSurface) {
137 if (mTransientParent) {
138 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup || window()->type() == Qt::Tool)
140 }
141
142 // Set initial surface title
145
146 // The appId is the desktop entry identifier that should follow the
147 // reverse DNS convention (see
148 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
149 // to xdg-shell the appId is only the name, without the .desktop suffix.
150 //
151 // If the application specifies the desktop file name use that,
152 // otherwise fall back to the executable name and prepend the
153 // reversed organization domain when available.
156 } else {
161
162 if (domainName.isEmpty()) {
164 } else {
166 for (int i = 0; i < domainName.size(); ++i)
170 }
171 }
172 // the user may have already set some window properties, so make sure to send them out
173 for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it)
175
177 } else {
178 qWarning("Could not create a shell surface object.");
179 }
180 }
181
184
185 // Enable high-dpi rendering. Scale() returns the screen scale factor and will
186 // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
187 // to inform the compositor that high-resolution buffers will be provided.
188 if (mViewport)
190 else if (mSurface->version() >= 3)
192
195 if (geometry.width() <= 0)
197 if (geometry.height() <= 0)
199
201 setMask(window()->mask());
202 if (mShellSurface) {
205 }
207
209 mSurface->commit();
210}
211
213{
215}
216
218{
220 {
229 mSurface->m_window = this;
230 }
232
235
238 }
239 // The fractional scale manager check is needed to work around Gnome < 36 where viewports don't work
240 // Right now viewports are only necessary when a fractional scale manager is used
243 }
244
247 // TODO try a similar (same primaries + supported transfer function) color space if this fails?
254 } else {
255 qCWarning(lcQpaWayland) << "couldn't create image description for requested color space" << requestedColorSpace;
256 }
257 }
258}
259
266
268{
270 if (mShellSurface) {
271 qCWarning(lcQpaWayland) << "Cannot set shell integration while there's already a shell surface created";
272 return;
273 }
275}
276
278{
279 if (!shellIntegration())
280 return false;
281
283 return false;
284
285 if (window()->inherits("QShapedPixmapWindow"))
286 return false;
287
288 if (qEnvironmentVariableIsSet("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"))
289 return !(window()->flags() & Qt::BypassWindowManagerHint);
290
291 return true;
292}
293
295{
296 return QPlatformWindow::parent() != nullptr;
297}
298
304
306{
308 mInFrameRender = false;
309}
310
338
340{
341 // Old Reset
343
344 if (mTopPopup == this)
348 mTransientParent = nullptr;
349 delete std::exchange(mShellSurface, nullptr);
350 delete std::exchange(mSubSurfaceWindow, nullptr);
352
354 mInFrameRender = false;
356 mExposed = false;
357}
358
360{
361 {
363 if (mFrameCallback) {
365 mFrameCallback = nullptr;
366 }
369 }
373 }
375}
376
378{
380 return s->m_window;
381 return nullptr;
382}
383
385{
386 return reinterpret_cast<WId>(wlSurface());
387}
388
390{
391 if (lastParent == parent)
392 return;
393
394 if (mSubSurfaceWindow && parent) { // new parent, but we were a subsurface already
395 delete mSubSurfaceWindow;
396 QWaylandWindow *p = const_cast<QWaylandWindow *>(static_cast<const QWaylandWindow *>(parent));
398 } else if ((!lastParent && parent) || (lastParent && !parent)) {
399 // we're changing role, need to make a new wl_surface
400 reset();
402 if (window()->isVisible()) {
403 initWindow();
404 }
405 }
407}
408
410{
411 return mWindowTitle;
412}
413
415{
416 const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH
418
419 const int libwaylandMaxBufferSize = 4096;
420 // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
421 // Also, QString is in utf-16, which means that in the worst case each character will be
422 // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
423 const int maxLength = libwaylandMaxBufferSize / 3 - 100;
424
426 if (truncated.size() < formatted.size()) {
427 qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported."
428 << "Truncating window title (from" << formatted.size() << "chars)";
429 }
430
432
433 if (mShellSurface)
435
438}
439
449
451{
452 return QRect(QPoint(), QSize(500,500));
453}
454
456{
458 if (mViewport)
460
461 if (mSubSurfaceWindow) {
464
469 }
470 }
471}
472
474{
475 auto rect = r;
477 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool) {
479 }
481
486 else
488 }
489
490 if (mShellSurface)
492
493 if (isOpaque() && mMask.isEmpty())
495
496
497 if (window()->isVisible() && rect.isValid()) {
498 ensureSize();
501
503 mSentInitialResize = true;
504 }
505
506 // 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
507 // On other platforms (X11) the expose event would be received deferred from the X server
508 // we want our behaviour to match, and only run after control has returned to the event loop
510}
511
513{
514 if (!isExposed())
515 return;
518 return;
519
521}
522
524{
525 if (!mSurface)
526 return;
527
529
533
535 return;
536
539
541 mSurface->set_input_region(nullptr);
542 } else {
546 }
547}
548
550{
551 if (!surfaceSize().isEmpty())
553}
554
569
580
593
595{
596 static bool sQtTestMode = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING");
598
599 if (sQtTestMode) {
601 }
603
604 /**
605 * If an expose is not handled by application code, explicitly attach a buffer
606 * This primarily is a workaround for Qt unit tests using QWindow directly and
607 * wanting focus.
608 */
614 }
615}
616
618{
620 if (mSurface) {
621 if (auto *screen = mSurface->oldestEnteredScreen())
622 return screen;
623 }
624 return QPlatformWindow::screen();
625}
626
628{
629 // Workaround for issue where setVisible may be called with the same value twice
630 if (lastVisible == visible)
631 return;
633
634 if (visible) {
636 initWindow();
638 if (mShellSurface)
640 } else {
641 // make sure isExposed is false during the next event dispatch
642 mExposed = false;
645 mSurface->attach(nullptr, 0, 0);
646 mSurface->commit();
647 }
648}
649
650
652{
653 if (mShellSurface)
655}
656
657
659{
660 if (mShellSurface)
662}
663
665{
667 if (!mSurface)
668 return;
669
670 if (mMask == mask)
671 return;
672
673 mMask = mask;
674
676
677 if (isOpaque()) {
678 if (mMask.isEmpty())
680 else
682 }
683}
684
690
692{
693 if (mShellSurface)
694 return mShellSurface->isAlertState();
695
696 return false;
697}
698
706
708{
710 return;
711
713 "QWaylandWindow::applyConfigure", "not called from main thread");
714
715 // If we're mid paint, use an exposeEvent to flush the current frame.
716 // When this completes we know that no other frames will be rendering.
717 // This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
718 if (mInFrameRender)
720 if (mShellSurface)
722
724 if (mExposed)
726 else
727 // we still need to commit the configured ack for a hidden surface
728 commit();
729}
730
732{
734 if (mSurface == nullptr)
735 return;
736
737 if (buffer) {
739 handleUpdate();
740 buffer->setBusy(true);
742 mSurface->offset(x, y);
743 mSurface->attach(buffer->buffer(), 0, 0);
744 } else {
746 }
747 } else {
748 mSurface->attach(nullptr, 0, 0);
749 }
750}
751
757
759{
761 if (mSurface == nullptr)
762 return;
763
764 const qreal s = scale();
765 if (mSurface->version() >= 4) {
766 const QRect bufferRect =
767 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
768 .toAlignedRect();
771 } else {
773 }
774}
775
777{
778 if (isExposed()) {
780 } else {
781 buffer->setBusy(false);
782 }
783}
784
789
791{
793 if (buffer->committed()) {
794 mSurface->commit();
795 qCDebug(lcWaylandBackingstore) << "Buffer already committed, not attaching.";
796 return;
797 }
798
800 if (!mSurface)
801 return;
802
804 if (mSurface->version() >= 4) {
805 const qreal s = scale();
806 for (const QRect &rect : damage) {
807 const QRect bufferRect =
808 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
809 .toAlignedRect();
812 }
813 } else {
814 for (const QRect &rect: damage)
816 }
819 mSurface->commit();
820}
821
823{
825 if (mSurface != nullptr)
826 mSurface->commit();
827}
828
830 [](void *data, wl_callback *callback, uint32_t time) {
831 Q_UNUSED(time);
832 auto *window = static_cast<QWaylandWindow*>(data);
834 }
835};
836
838{
840 if (!mFrameCallback) {
841 // This means the callback is already unset by QWaylandWindow::reset.
842 // The wl_callback object will be destroyed there too.
843 return;
844 }
847 mFrameCallback = nullptr;
848
851
852 // The rest can wait until we can run it on the correct thread
854 // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
855 // in the single-threaded case.
857 }
859}
860
862{
864 bool wasExposed = isExposed();
866 // Did setting mFrameCallbackTimedOut make the window exposed?
870
871}
872
874{
876
879
881 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
884 }
885
887}
888
898
903
910
911/*!
912 * Size, with decorations (including including eventual shadows) in wl_surface coordinates
913 */
918
931
932/*!
933 * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
934 * topLeft is where the shadow stops and the decorations border start.
935 */
941
942/*!
943 * Converts from wl_surface coordinates to Qt window coordinates. Qt window
944 * coordinates start inside (not including) the window decorations, while
945 * wl_surface coordinates start at the first pixel of the buffer. Potentially,
946 * this should be in the window shadow, although we don't have those. So for
947 * now, it's the first pixel of the decorations.
948 */
954
956{
958 return mSurface ? mSurface->object() : nullptr;
959}
960
965
967{
969 return mSubSurfaceWindow->object();
970 if (mShellSurface)
971 return mShellSurface->surfaceRole();
972 return {};
973}
974
979
981{
985 return nullptr;
986 return static_cast<QWaylandScreen *>(platformScreen);
987}
988
994
996{
998 if (mSurface == nullptr || mSurface->version() < 2)
999 return;
1000
1003
1004 if (mSurface->version() >= 6) {
1006 if (auto screen = waylandScreen())
1008 } else {
1009 if (auto screen = window()->screen())
1011 }
1012
1014
1016 case Qt::PrimaryOrientation:
1018 break;
1021 break;
1022 case Qt::PortraitOrientation:
1024 break;
1027 break;
1030 break;
1031 default:
1032 Q_UNREACHABLE();
1033 }
1035}
1036
1042
1048
1050{
1051 const bool wasPopup = mFlags.testFlag(Qt::Popup);
1052 const bool isPopup = flags.testFlag(Qt::Popup);
1053
1054 mFlags = flags;
1055 // changing role is not allowed on XdgShell on the same wl_surface
1056 if (wasPopup != isPopup) {
1057 reset();
1059 if (window()->isVisible()) {
1060 initWindow();
1061 }
1062 } else {
1063 if (mShellSurface)
1065 }
1066
1068
1071}
1072
1074{
1075 return mFlags;
1076}
1077
1079{
1081 "QWaylandWindow::createDecoration", "not called from main thread");
1082 // TODO: client side decorations do not work with Vulkan backend.
1084 return false;
1086 return false;
1087
1088 static bool decorationPluginFailed = false;
1089 bool decoration = false;
1090 switch (window()->type()) {
1091 case Qt::Window:
1092 case Qt::Widget:
1093 case Qt::Dialog:
1094 case Qt::Tool:
1095 case Qt::Drawer:
1096 decoration = true;
1097 break;
1098 default:
1099 break;
1100 }
1102 decoration = false;
1104 decoration = false;
1106 decoration = false;
1108 decoration = false;
1109
1113 if (mWindowDecoration) {
1115 }
1116
1118 if (decorations.empty()) {
1119 qWarning() << "No decoration plugins available. Running with no decorations.";
1121 return false;
1122 }
1123
1125 QByteArray decorationPluginName = qgetenv("QT_WAYLAND_DECORATION");
1129 qWarning() << "Requested decoration " << targetKey << " not found, falling back to default";
1130 targetKey = QString(); // fallthrough
1131 }
1132 }
1133
1134 if (targetKey.isEmpty()) {
1135 auto unixServices = dynamic_cast<QDesktopUnixServices *>(
1138 if (desktopNames.contains("GNOME")) {
1139 if (decorations.contains("adwaita"_L1))
1140 targetKey = "adwaita"_L1;
1141 else if (decorations.contains("gnome"_L1))
1142 targetKey = "gnome"_L1;
1143 } else {
1144 // Do not use Adwaita/GNOME decorations on other DEs
1145 decorations.removeAll("adwaita"_L1);
1146 decorations.removeAll("gnome"_L1);
1147 }
1148 }
1149
1150 if (targetKey.isEmpty())
1151 targetKey = decorations.first(); // first come, first served.
1152
1154 if (!mWindowDecoration) {
1155 qWarning() << "Could not create decoration from factory! Running with no decorations.";
1157 return false;
1158 }
1161 }
1162 } else {
1164 }
1165
1170 subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
1171 }
1173
1174 // creating a decoration changes our margins which in turn change size hints
1176
1177 // This is a special case where the buffer is recreated, but since
1178 // the content rect remains the same, the widgets remain the same
1179 // size and are not redrawn, leaving the new buffer empty. As a simple
1180 // work-around, we trigger a full extra update whenever the client-side
1181 // window decorations are toggled while the window is showing.
1182 window()->requestUpdate();
1183 }
1184
1185 return mWindowDecoration.get();
1186}
1187
1192
1193static QWaylandWindow *closestShellSurfaceWindow(QWindow *window)
1194{
1195 while (window) {
1196 auto w = static_cast<QWaylandWindow *>(window->handle());
1197 if (w && w->shellSurface())
1198 return w;
1199 window = window->transientParent() ? window->transientParent() : window->parent();
1200 }
1201 return nullptr;
1202}
1203
1208
1210{
1211 // Take the closest window with a shell surface, since the transient parent may be a
1212 // QWidgetWindow or some other window without a shell surface, which is then not able to
1213 // get mouse events.
1215 return transientParent;
1216
1217 if (window()->type() == Qt::Popup) {
1218 if (mTopPopup)
1219 return mTopPopup;
1220 }
1221
1222 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) {
1223 if (auto lastInputWindow = display()->lastInputWindow())
1225 }
1226
1227 return nullptr;
1228}
1229
1231{
1232 // There's currently no way to get info about the actual hardware device in use.
1233 // At least we get the correct seat.
1235 if (e.type == QEvent::Leave) {
1239 } else {
1241 }
1242#if QT_CONFIG(cursor)
1244#endif
1245 return;
1246 }
1247
1248#if QT_CONFIG(cursor)
1249 if (e.type == QEvent::Enter) {
1251 }
1252#endif
1253
1256 } else {
1257 switch (e.type) {
1258 case QEvent::Enter:
1260#if QT_CONFIG(cursor)
1262#endif
1263 break;
1266 case QEvent::MouseMove:
1268 break;
1269 case QEvent::Wheel:
1272 e.phase, e.source, e.inverted);
1273 break;
1274 default:
1275 Q_UNREACHABLE();
1276 }
1277 }
1278}
1279
1280#ifndef QT_NO_GESTURES
1283{
1284 switch (e.state) {
1285 case Qt::GestureStarted:
1287 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1288
1290 // whole gesture sequence will be ignored
1292 return;
1293 }
1294
1299 e.local, e.global, e.fingers);
1300 break;
1301 case Qt::GestureUpdated:
1303 return;
1304
1305 if (!e.delta.isNull()) {
1309 0, e.delta, e.local, e.global, e.fingers);
1310 }
1311 break;
1312 case Qt::GestureFinished:
1313 case Qt::GestureCanceled:
1316 return;
1317 }
1318
1320 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1321
1323
1324 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1325 // this part of information is lost.
1329 e.local, e.global, e.fingers);
1330 break;
1331 default:
1332 break;
1333 }
1334}
1335
1338{
1339 switch (e.state) {
1340 case Qt::GestureStarted:
1342 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1343
1345 // whole gesture sequence will be ignored
1347 return;
1348 }
1349
1354 e.local, e.global, e.fingers);
1355 break;
1356 case Qt::GestureUpdated:
1358 return;
1359
1360 if (!e.delta.isNull()) {
1364 0, e.delta, e.local, e.global, e.fingers);
1365 }
1366 if (e.rotation_delta != 0) {
1371 e.local, e.global, e.fingers);
1372 }
1373 if (e.scale_delta != 0) {
1377 e.scale_delta,
1378 e.local, e.global, e.fingers);
1379 }
1380 break;
1381 case Qt::GestureFinished:
1382 case Qt::GestureCanceled:
1385 return;
1386 }
1387
1389 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1390
1392
1393 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1394 // this part of information is lost.
1398 e.local, e.global, e.fingers);
1399 break;
1400 default:
1401 break;
1402 }
1403}
1404#endif // #ifndef QT_NO_GESTURES
1405
1406
1413
1423
1425{
1426 // There's currently no way to get info about the actual hardware device in use.
1427 // At least we get the correct seat.
1434 }
1435 return;
1436 }
1437
1439 QRect windowRect(0 + marg.left(),
1440 0 + marg.top(),
1441 geometry().size().width(),
1442 geometry().size().height());
1449#if QT_CONFIG(cursor)
1451#endif
1453 }
1454
1455 switch (e.type) {
1456 case QEvent::Enter:
1458#if QT_CONFIG(cursor)
1460#endif
1461 break;
1464 case QEvent::MouseMove:
1466 break;
1467 case QEvent::Wheel: {
1471 e.phase, e.source, e.inverted);
1472 break;
1473 }
1474 default:
1475 Q_UNREACHABLE();
1476 }
1477
1480 } else {
1484 }
1485 }
1486}
1487
1489{
1491
1492 if (!newScreen || newScreen->screen() == window()->screen())
1493 return;
1494
1496
1498 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool
1499 && geometry().topLeft() != newScreen->geometry().topLeft()) {
1500 auto geometry = this->geometry();
1503 }
1504
1505 updateScale();
1507}
1508
1510{
1511 if (mFractionalScale) {
1516 return;
1517 }
1518
1519 if (mSurface && mSurface->version() >= 6) {
1523 return;
1524 }
1525
1526 int scale = screen()->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(screen())->scale();
1527 setScale(scale);
1528}
1529
1531{
1533 return;
1534 mScale = newScale;
1535
1536 if (mSurface) {
1537 if (mViewport)
1539 else if (mSurface->version() >= 3)
1541 }
1542 ensureSize();
1543
1545 if (isExposed()) {
1546 // redraw at the new DPR
1547 window()->requestUpdate();
1549 }
1550}
1551
1552#if QT_CONFIG(cursor)
1554{
1557 } else if (mHasStoredCursor) {
1559 } else {
1561 }
1562}
1563
1565{
1566 mHasStoredCursor = false;
1567}
1568
1569// Keep track of application set cursors on each window
1572 mHasStoredCursor = true;
1573}
1574
1576 if (!device || !device->pointer() || device->pointer()->focusWindow() != this)
1577 return;
1578
1581}
1582#endif
1583
1589
1591{
1592 if (!window()->isVisible())
1593 return false;
1594
1596 return false;
1597
1598 if (mShellSurface)
1599 return mShellSurface->isExposed();
1600
1602 return mSubSurfaceWindow->parent()->isExposed();
1603
1605}
1606
1608{
1609 bool exposed = calculateExposure();
1610 if (exposed == mExposed)
1611 return;
1612
1613 mExposed = exposed;
1614
1615 if (!exposed)
1617 else
1619
1621 auto subWindow = subSurface->window();
1623 }
1624}
1625
1627{
1628 return mExposed;
1629}
1630
1632{
1633 return mDisplay->isWindowActivated(this);
1634}
1635
1637{
1638 return devicePixelRatio();
1639}
1640
1645
1647{
1648 if (window()->type() != Qt::Popup) {
1649 qWarning("This plugin supports grabbing the mouse only for popup windows");
1650 return false;
1651 }
1652
1653 mMouseGrab = grab ? this : nullptr;
1654 return true;
1655}
1656
1661
1666
1671
1681
1691
1699
1701{
1702 return m_properties;
1703}
1704
1709
1714
1715#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1717{
1718 mBackingStore = dynamic_cast<QWaylandShmBackingStore *>(store);
1719}
1720#endif
1721
1723{
1725 return;
1726
1727 {
1729
1735 }
1737 return;
1738 }
1740 }
1741
1742 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
1745}
1746
1748{
1749 qCDebug(lcWaylandBackingstore) << "requestUpdate";
1750 Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
1751
1752 // If we have a frame callback all is good and will be taken care of there
1753 {
1756 return;
1757 }
1758
1759 // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1760 // so use invokeMethod to delay the delivery a bit.
1761 QMetaObject::invokeMethod(this, [this] {
1762 // Things might have changed in the meantime
1763 {
1766 return;
1767 }
1770 }, Qt::QueuedConnection);
1771}
1772
1773// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1774// with eglSwapBuffers) to know when it's time to commit the next one.
1775// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1777{
1779 qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
1780
1781 // TODO: Should sync subsurfaces avoid requesting frame callbacks?
1783 if (!mSurface)
1784 return;
1785
1788 return;
1789
1790 struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1796
1797 // Start a timer for handling the case when the compositor stops sending frame callbacks.
1798 if (mFrameCallbackTimeout > 0) {
1799 QMetaObject::invokeMethod(this, [this] {
1801
1806 }
1807 }, Qt::QueuedConnection);
1808 }
1809}
1810
1812{
1813 qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1815}
1816
1818{
1819 mOffset += point;
1820}
1821
1827
1829{
1830 if (auto *seat = display()->lastInputDevice()) {
1833 return rc;
1834 }
1835 return false;
1836}
1837
1839{
1840 if (auto seat = display()->lastInputDevice()) {
1843 return rc;
1844 }
1845 return false;
1846}
1847
1848bool QWaylandWindow::isOpaque() const
1849{
1850 return window()->requestedFormat().alphaBufferSize() <= 0;
1851}
1852
1854{
1856
1858 return;
1859
1861
1865}
1866
1868{
1869 if (!mShellSurface) {
1870 qCWarning(lcQpaWayland) << "requestXdgActivationToken is called with no surface role created, emitting synthetic signal";
1872 return;
1873 }
1875}
1876
1878{
1879 if (mShellSurface)
1881 else
1882 qCWarning(lcQpaWayland) << "setXdgActivationToken is called with no surface role created, token" << token << "discarded";
1883}
1884
1886{
1887 if (mShellSurface)
1890}
1891
1893{
1894 if (mShellSurface)
1897}
1898
1900 while (!mChildPopups.isEmpty()) {
1901 auto popup = mChildPopups.takeLast();
1903 }
1904}
1905
1907{
1908 if (window()->isVisible()) {
1909 initWindow();
1912 }
1913}
1914
1916{
1921 } else if (event->type() == QEvent::WindowUnblocked) {
1922 // QtGui sends leave event to window under cursor when modal window opens, so we have
1923 // to send enter event when modal closes and window has cursor and gets unblocked.
1925 const auto pos = mDisplay->waylandCursor()->pos();
1927 }
1928 }
1929
1931}
1932
1934{
1935 return mSurfaceFormat;
1936}
1937
1942
1947
1948}
1949
1950QT_END_NAMESPACE
1951
1952#include "moc_qwaylandwindow_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")
static QWaylandWindow * closestShellSurfaceWindow(QWindow *window)