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
51/*!
52 \class QtWaylandClient::QWaylandWindow
53 \internal
54*/
80
82{
84 reset();
85
86 const QWindow *parent = window();
87 const auto tlw = QGuiApplication::topLevelWindows();
88 for (QWindow *w : tlw) {
89 if (w->transientParent() == parent)
91 }
92
93 if (mMouseGrab == this) {
94 mMouseGrab = nullptr;
95 }
96}
97
105
107{
109
112
113 auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
114 if (!parent->mSurface)
116 if (parent->wlSurface()) {
119 }
120 } else if (shouldCreateShellSurface()) {
124 if (mTransientParent) {
125 if (window()->type() == Qt::Popup) {
127 qCWarning(lcQpaWayland) << "Creating a popup with a parent," << mTransientParent->window()
128 << "which does not match the current topmost grabbing popup,"
129 << mTopPopup->window() << "With some shell surface protocols, this"
130 << "is not allowed. The wayland QPA plugin is currently handling"
131 << "it by setting the parent to the topmost grabbing popup."
132 << "Note, however, that this may cause positioning errors and"
133 << "popups closing unxpectedly. Please fix the transient parent of the popup.";
135 }
136 mTopPopup = this;
137 }
138 }
139
141 if (mShellSurface) {
142 if (mTransientParent) {
143 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup || window()->type() == Qt::Tool)
145 }
146
147 // Set initial surface title
150
151 // The appId is the desktop entry identifier that should follow the
152 // reverse DNS convention (see
153 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
154 // to xdg-shell the appId is only the name, without the .desktop suffix.
155 //
156 // If the application specifies the desktop file name use that,
157 // otherwise fall back to the executable name and prepend the
158 // reversed organization domain when available.
161 } else {
166
167 if (domainName.isEmpty()) {
169 } else {
171 for (int i = 0; i < domainName.size(); ++i)
175 }
176 }
177 // the user may have already set some window properties, so make sure to send them out
178 for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it)
180
182 } else {
183 qWarning("Could not create a shell surface object.");
184 }
185 }
186
189
190 // Enable high-dpi rendering. Scale() returns the screen scale factor and will
191 // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
192 // to inform the compositor that high-resolution buffers will be provided.
193 if (mViewport)
195 else if (mSurface->version() >= 3)
197
200 if (geometry.width() <= 0)
202 if (geometry.height() <= 0)
204
206 setMask(window()->mask());
207 if (mShellSurface) {
210 }
212
214 mSurface->commit();
215}
216
218{
220}
221
254
271
278
280{
282 if (mShellSurface) {
283 qCWarning(lcQpaWayland) << "Cannot set shell integration while there's already a shell surface created";
284 return;
285 }
287}
288
290{
291 if (!shellIntegration())
292 return false;
293
295 return false;
296
297 if (window()->inherits("QShapedPixmapWindow"))
298 return false;
299
300 if (qEnvironmentVariableIsSet("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"))
301 return !(window()->flags() & Qt::BypassWindowManagerHint);
302
303 return true;
304}
305
307{
308 return QPlatformWindow::parent() != nullptr;
309}
310
316
318{
320 mInFrameRender = false;
321}
322
350
352{
353 // Old Reset
355
356 if (mTopPopup == this)
360 mTransientParent = nullptr;
361 delete std::exchange(mShellSurface, nullptr);
362 delete std::exchange(mSubSurfaceWindow, nullptr);
364
366 mInFrameRender = false;
368 mExposed = false;
369}
370
372{
373 {
375 if (mFrameCallback) {
377 mFrameCallback = nullptr;
378 }
381 }
385 }
387}
388
390{
392 return s->m_window;
393 return nullptr;
394}
395
397{
398 return reinterpret_cast<WId>(wlSurface());
399}
400
402{
403 if (lastParent == parent)
404 return;
405
406 if (mSubSurfaceWindow && parent) { // new parent, but we were a subsurface already
407 delete mSubSurfaceWindow;
408 QWaylandWindow *p = const_cast<QWaylandWindow *>(static_cast<const QWaylandWindow *>(parent));
410 } else if ((!lastParent && parent) || (lastParent && !parent)) {
411 // we're changing role, need to make a new wl_surface
412 reset();
414 if (window()->isVisible()) {
415 initWindow();
416 }
417 }
419}
420
422{
423 return mWindowTitle;
424}
425
427{
428 const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH
430
431 const int libwaylandMaxBufferSize = 4096;
432 // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
433 // Also, QString is in utf-16, which means that in the worst case each character will be
434 // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
435 const int maxLength = libwaylandMaxBufferSize / 3 - 100;
436
438 if (truncated.size() < formatted.size()) {
439 qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported."
440 << "Truncating window title (from" << formatted.size() << "chars)";
441 }
442
444
445 if (mShellSurface)
447
450}
451
461
463{
464 return QRect(QPoint(), QSize(500,500));
465}
466
468{
470 if (mViewport)
472
473 if (mSubSurfaceWindow) {
476
481 }
482 }
483}
484
486{
487 auto rect = r;
489 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool) {
491 }
493
498
499 else
501 }
502
503 if (mShellSurface)
505
506 if (isOpaque() && mMask.isEmpty())
508
509
510 if (window()->isVisible() && rect.isValid()) {
511 ensureSize();
514
516 }
517
518 // 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
519 // On other platforms (X11) the expose event would be received deferred from the X server
520 // we want our behaviour to match, and only run after control has returned to the event loop
522}
523
525{
526 if (!isExposed())
527 return;
530 return;
531
533}
534
536{
537 if (!mSurface)
538 return;
539
541
545
547 return;
548
551
553 mSurface->set_input_region(nullptr);
554 } else {
558 }
559}
560
562{
563 if (!surfaceSize().isEmpty())
565}
566
581
592
605
607{
608 static bool sQtTestMode = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING");
610
611 if (sQtTestMode) {
613 }
615
616 /**
617 * If an expose is not handled by application code, explicitly attach a buffer
618 * This primarily is a workaround for Qt unit tests using QWindow directly and
619 * wanting focus.
620 */
626 }
627}
628
630{
632 if (mSurface) {
633 if (auto *screen = mSurface->oldestEnteredScreen())
634 return screen;
635 }
636 return QPlatformWindow::screen();
637}
638
640{
641 // Workaround for issue where setVisible may be called with the same value twice
642 if (lastVisible == visible)
643 return;
645
646 if (visible) {
648 initWindow();
650 if (mShellSurface)
652 } else {
653 // make sure isExposed is false during the next event dispatch
654 mExposed = false;
657 mSurface->attach(nullptr, 0, 0);
658 mSurface->commit();
659 }
660}
661
662
664{
665 if (mShellSurface)
667}
668
669
671{
672 if (mShellSurface)
674}
675
677{
679 if (!mSurface)
680 return;
681
682 if (mMask == mask)
683 return;
684
685 mMask = mask;
686
688
689 if (isOpaque()) {
690 if (mMask.isEmpty())
692 else
694 }
695}
696
702
704{
705 if (mShellSurface)
706 return mShellSurface->isAlertState();
707
708 return false;
709}
710
718
720{
722 return;
723
725 "QWaylandWindow::applyConfigure", "not called from main thread");
726
727 // If we're mid paint, use an exposeEvent to flush the current frame.
728 // When this completes we know that no other frames will be rendering.
729 // This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
730 if (mInFrameRender)
732 if (mShellSurface)
734
736 if (mExposed)
738 else
739 // we still need to commit the configured ack for a hidden surface
740 commit();
741}
742
744{
746 if (mSurface == nullptr)
747 return;
748
749 if (buffer) {
751 handleUpdate();
752 buffer->setBusy(true);
754 mSurface->offset(x, y);
755 mSurface->attach(buffer->buffer(), 0, 0);
756 } else {
758 }
759 } else {
760 mSurface->attach(nullptr, 0, 0);
761 }
762}
763
769
771{
773 if (mSurface == nullptr)
774 return;
775
776 const qreal s = scale();
777 if (mSurface->version() >= 4) {
778 const QRect bufferRect =
779 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
780 .toAlignedRect();
783 } else {
785 }
786}
787
789{
790 if (isExposed()) {
792 } else {
793 buffer->setBusy(false);
794 }
795}
796
801
803{
805 if (buffer->committed()) {
806 mSurface->commit();
807 qCDebug(lcWaylandBackingstore) << "Buffer already committed, not attaching.";
808 return;
809 }
810
812 if (!mSurface)
813 return;
814
816 if (mSurface->version() >= 4) {
817 const qreal s = scale();
818 for (const QRect &rect : damage) {
819 const QRect bufferRect =
820 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
821 .toAlignedRect();
824 }
825 } else {
826 for (const QRect &rect: damage)
828 }
831 mSurface->commit();
832}
833
835{
837 if (mSurface != nullptr)
838 mSurface->commit();
839}
840
842 [](void *data, wl_callback *callback, uint32_t time) {
843 Q_UNUSED(time);
844 auto *window = static_cast<QWaylandWindow*>(data);
846 }
847};
848
850{
852 if (mFrameCallback != callback) {
853 // This means the callback is already unset by QWaylandWindow::reset.
854 // The wl_callback object will be destroyed there too.
855 return;
856 }
858 mFrameCallback = nullptr;
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
889 if (mFrameCallback) {
890 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
893 }
894
895 return !mFrameCallback;
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 {
1764 if (mFrameCallback)
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 {
1774 if (mFrameCallback)
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
1796 if (mFrameCallback)
1797 return;
1798
1799 struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1804
1805 // Start a timer for handling the case when the compositor stops sending frame callbacks.
1806 if (mFrameCallbackTimeout > 0) {
1807 QMetaObject::invokeMethod(this, [this] {
1809
1810 if (mFrameCallback) {
1814 }
1815 }, Qt::QueuedConnection);
1816 }
1817}
1818
1820{
1821 qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1823}
1824
1826{
1827 mOffset += point;
1828}
1829
1835
1837{
1838 if (auto *seat = display()->lastInputDevice()) {
1841 return rc;
1842 }
1843 return false;
1844}
1845
1847{
1848 if (auto seat = display()->lastInputDevice()) {
1851 return rc;
1852 }
1853 return false;
1854}
1855
1856bool QWaylandWindow::isOpaque() const
1857{
1858 return window()->requestedFormat().alphaBufferSize() <= 0;
1859}
1860
1862{
1864
1866 return;
1867
1869
1873}
1874
1876{
1877 if (!mShellSurface) {
1878 qCWarning(lcQpaWayland) << "requestXdgActivationToken is called with no surface role created, emitting synthetic signal";
1880 return;
1881 }
1883}
1884
1886{
1887 if (mShellSurface)
1889 else
1890 qCWarning(lcQpaWayland) << "setXdgActivationToken is called with no surface role created, token" << token << "discarded";
1891}
1892
1894{
1895 if (mShellSurface)
1898}
1899
1901{
1902 if (mShellSurface)
1905}
1906
1908 while (!mChildPopups.isEmpty()) {
1909 auto popup = mChildPopups.takeLast();
1911 }
1912}
1913
1915{
1916 if (window()->isVisible()) {
1917 initWindow();
1920 }
1921}
1922
1924{
1929 } else if (event->type() == QEvent::WindowUnblocked) {
1930 // QtGui sends leave event to window under cursor when modal window opens, so we have
1931 // to send enter event when modal closes and window has cursor and gets unblocked.
1933 const auto pos = mDisplay->waylandCursor()->pos();
1935 }
1936 }
1937
1939}
1940
1942{
1943 return mSurfaceFormat;
1944}
1945
1950
1955
1959
1961{
1963}
1964
1971
1973{
1975}
1976
1977}
1978
1979QT_END_NAMESPACE
1980
1981#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)