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
26
27#include <QtCore/QFileInfo>
28#include <QtCore/QPointer>
29#include <QtCore/QRegularExpression>
30#include <QtGui/QWindow>
31
32#include <QGuiApplication>
33#include <qpa/qwindowsysteminterface.h>
34#include <QtGui/private/qguiapplication_p.h>
35#include <QtGui/private/qwindow_p.h>
36
37#include <QtCore/QDebug>
38#include <QtCore/QThread>
39
40#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
41
43
44using namespace Qt::StringLiterals;
45
46namespace QtWaylandClient {
47
48Q_LOGGING_CATEGORY(lcWaylandBackingstore, "qt.qpa.wayland.backingstore")
49
52
53/*!
54 \class QtWaylandClient::QWaylandWindow
55 \internal
56*/
82
84{
86 reset();
87
88 const QWindow *parent = window();
89 const auto tlw = QGuiApplication::topLevelWindows();
90 for (QWindow *w : tlw) {
91 if (w->transientParent() == parent)
93 }
94
95 if (mMouseGrab == this) {
96 mMouseGrab = nullptr;
97 }
98}
99
107
109{
111
114
115 auto *parent = static_cast<QWaylandWindow *>(QPlatformWindow::parent());
116 if (!parent->mSurface)
118 if (parent->wlSurface()) {
121 }
122 } else if (shouldCreateShellSurface()) {
126 if (mTransientParent) {
127 if (window()->type() == Qt::Popup) {
129 qCWarning(lcQpaWayland) << "Creating a popup with a parent," << mTransientParent->window()
130 << "which does not match the current topmost grabbing popup,"
131 << mTopPopup->window() << "With some shell surface protocols, this"
132 << "is not allowed. The wayland QPA plugin is currently handling"
133 << "it by setting the parent to the topmost grabbing popup."
134 << "Note, however, that this may cause positioning errors and"
135 << "popups closing unxpectedly. Please fix the transient parent of the popup.";
137 }
138 mTopPopup = this;
139 }
140 }
141
143 if (mShellSurface) {
144 if (mTransientParent) {
145 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup || window()->type() == Qt::Tool)
147 }
148
149 // Set initial surface title
152
153 // The appId is the desktop entry identifier that should follow the
154 // reverse DNS convention (see
155 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html). According
156 // to xdg-shell the appId is only the name, without the .desktop suffix.
157 //
158 // If the application specifies the desktop file name use that,
159 // otherwise fall back to the executable name and prepend the
160 // reversed organization domain when available.
163 } else {
168
169 if (domainName.isEmpty()) {
171 } else {
173 for (int i = 0; i < domainName.size(); ++i)
177 }
178 }
179 // the user may have already set some window properties, so make sure to send them out
180 for (auto it = m_properties.cbegin(); it != m_properties.cend(); ++it)
182
184 } else {
185 qWarning("Could not create a shell surface object.");
186 }
187 }
188
191
192 // Enable high-dpi rendering. Scale() returns the screen scale factor and will
193 // typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
194 // to inform the compositor that high-resolution buffers will be provided.
195 if (mViewport)
197 else if (mSurface->version() >= 3)
199
202 if (geometry.width() <= 0)
204 if (geometry.height() <= 0)
206
208 setMask(window()->mask());
209 if (mShellSurface) {
212 }
214
216 mSurface->commit();
217
220 }
221}
222
224{
226}
227
260
277
284
286{
288 if (mShellSurface) {
289 qCWarning(lcQpaWayland) << "Cannot set shell integration while there's already a shell surface created";
290 return;
291 }
293}
294
296{
297 if (!shellIntegration())
298 return false;
299
301 return false;
302
303 if (window()->inherits("QShapedPixmapWindow"))
304 return false;
305
306 if (qEnvironmentVariableIsSet("QT_WAYLAND_USE_BYPASSWINDOWMANAGERHINT"))
307 return !(window()->flags() & Qt::BypassWindowManagerHint);
308
309 return true;
310}
311
313{
314 return QPlatformWindow::parent() != nullptr;
315}
316
322
324{
326 mInFrameRender = false;
327}
328
330{
332
335
336 if (mSurface) {
337 {
344
345 // Destroy surface last.
346 // Some compositors (e.g. ChromeOS Sommelier) crash if they receive
347 // destroy requests for these objects after the associated surface
348 // has already been destroyed.
349 mSurface.reset();
350 }
352 }
353
354
355 mScale = std::nullopt;
357 mMask = QRegion();
358
361
363}
364
366{
367 // Old Reset
369
370 if (mTopPopup == this)
374 mTransientParent = nullptr;
375 delete std::exchange(mShellSurface, nullptr);
376 delete std::exchange(mSubSurfaceWindow, nullptr);
378
380 mInFrameRender = false;
382 mExposed = false;
383}
384
386{
387 {
389 if (mFrameCallback) {
391 mFrameCallback = nullptr;
392 }
395 }
399 }
401}
402
404{
406 return s->m_window;
407 return nullptr;
408}
409
411{
412 return reinterpret_cast<WId>(wlSurface());
413}
414
416{
417 if (lastParent == parent)
418 return;
419
420 if (mSubSurfaceWindow && parent) { // new parent, but we were a subsurface already
421 delete mSubSurfaceWindow;
422 QWaylandWindow *p = const_cast<QWaylandWindow *>(static_cast<const QWaylandWindow *>(parent));
424 } else if ((!lastParent && parent) || (lastParent && !parent)) {
425 // we're changing role, need to make a new wl_surface
426 reset();
428 if (window()->isVisible()) {
429 initWindow();
430 }
431 }
433}
434
436{
437 return mWindowTitle;
438}
439
441{
442 const QString separator = QString::fromUtf8(" \xe2\x80\x94 "); // unicode character U+2014, EM DASH
444
445 const int libwaylandMaxBufferSize = 4096;
446 // Some parts of the buffer is used for metadata, so subtract 100 to be on the safe side.
447 // Also, QString is in utf-16, which means that in the worst case each character will be
448 // three bytes when converted to utf-8 (which is what libwayland uses), so divide by three.
449 const int maxLength = libwaylandMaxBufferSize / 3 - 100;
450
452 if (truncated.size() < formatted.size()) {
453 qCWarning(lcQpaWayland) << "Window titles longer than" << maxLength << "characters are not supported."
454 << "Truncating window title (from" << formatted.size() << "chars)";
455 }
456
458
459 if (mShellSurface)
461
464}
465
475
477{
478 return QRect(QPoint(), QSize(500,500));
479}
480
482{
484 if (mViewport)
486
487 if (mSubSurfaceWindow) {
490
492 if (parentWindow)
494 }
495}
496
498{
499 auto rect = r;
501 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool) {
503 }
505
510
511 else
513 }
514
515 if (mShellSurface)
517
518 if (isOpaque() && mMask.isEmpty())
520
521
522 if (window()->isVisible() && rect.isValid()) {
523 ensureSize();
526
528 }
529
530 // 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
531 // On other platforms (X11) the expose event would be received deferred from the X server
532 // we want our behaviour to match, and only run after control has returned to the event loop
534}
535
537{
538 if (!isExposed())
539 return;
542 return;
543
545}
546
548{
549 if (!mSurface)
550 return;
551
553
557
559 return;
560
563
565 mSurface->set_input_region(nullptr);
566 } else {
570 }
571}
572
574{
575 if (!surfaceSize().isEmpty())
577}
578
593
604
617
619{
620 static bool sQtTestMode = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING");
622
623 if (sQtTestMode) {
625 }
627
628 /**
629 * If an expose is not handled by application code, explicitly attach a buffer
630 * This primarily is a workaround for Qt unit tests using QWindow directly and
631 * wanting focus.
632 */
636 commit(&buffer, QRegion());
637 }
638}
639
641{
643 if (mSurface) {
644 if (auto *screen = mSurface->oldestEnteredScreen())
645 return screen;
646 }
647 return QPlatformWindow::screen();
648}
649
651{
652 // Workaround for issue where setVisible may be called with the same value twice
653 if (lastVisible == visible)
654 return;
656
657 if (visible) {
659 initWindow();
661 if (mShellSurface)
663 } else {
664 // make sure isExposed is false during the next event dispatch
665 mExposed = false;
668 mSurface->attach(nullptr, 0, 0);
669 mSurface->commit();
670 }
671}
672
673
675{
676 if (mShellSurface)
678}
679
680
682{
683 if (mShellSurface)
685}
686
688{
690 if (!mSurface)
691 return;
692
693 if (mMask == mask)
694 return;
695
696 mMask = mask;
697
699
700 if (isOpaque()) {
701 if (mMask.isEmpty())
703 else
705 }
706}
707
713
715{
716 if (mShellSurface)
717 return mShellSurface->isAlertState();
718
719 return false;
720}
721
729
731{
733 return;
734
736 "QWaylandWindow::applyConfigure", "not called from main thread");
737
738 // If we're mid paint, use an exposeEvent to flush the current frame.
739 // When this completes we know that no other frames will be rendering.
740 // This could be improved in future as we 're blocking for not just the frame to finish but one additional extra frame.
741 if (mInFrameRender)
743 if (mShellSurface)
745
747 if (mExposed)
749 else
750 // we still need to commit the configured ack for a hidden surface
751 commit();
752}
753
755{
757 if (mSurface == nullptr)
758 return;
759
760 if (buffer) {
762 handleUpdate();
763 buffer->setBusy(true);
765 mSurface->offset(x, y);
766 mSurface->attach(buffer->buffer(), 0, 0);
767 } else {
769 }
770 } else {
771 mSurface->attach(nullptr, 0, 0);
772 }
773}
774
780
782{
784 if (mSurface == nullptr)
785 return;
786
787 const qreal s = scale();
788 if (mSurface->version() >= 4) {
789 const QRect bufferRect =
790 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
791 .toAlignedRect();
794 } else {
796 }
797}
798
800{
801 if (isExposed()) {
803 } else if (!buffer->committed()) {
804 buffer->setBusy(false);
805 }
806}
807
812
814{
816
818 if (buffer->committed()) {
819 mSurface->commit();
820 qCDebug(lcWaylandBackingstore) << "Buffer already committed, not attaching.";
821 return;
822 }
823
824 if (!mSurface)
825 return;
826
828 if (mSurface->version() >= 4) {
829 const qreal s = scale();
830 for (const QRect &rect : damage) {
831 const QRect bufferRect =
832 QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
833 .toAlignedRect();
836 }
837 } else {
838 for (const QRect &rect: damage)
840 }
843 mSurface->commit();
844}
845
847{
849 if (mSurface != nullptr)
850 mSurface->commit();
851}
852
854 [](void *data, wl_callback *callback, uint32_t time) {
855 Q_UNUSED(time);
856 auto *window = static_cast<QWaylandWindow*>(data);
858 }
859};
860
862{
864 if (mFrameCallback != callback) {
865 // This means the callback is already unset by QWaylandWindow::reset.
866 // The wl_callback object will be destroyed there too.
867 return;
868 }
870 mFrameCallback = nullptr;
872
873 // The rest can wait until we can run it on the correct thread
875 // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
876 // in the single-threaded case.
878 }
880}
881
883{
885 bool wasExposed = isExposed();
887 // Did setting mFrameCallbackTimedOut make the window exposed?
891
892}
893
895{
897
900
901 if (mFrameCallback) {
902 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
905 }
906
907 return !mFrameCallback;
908}
909
919
924
931
932/*!
933 * Size, with decorations (including including eventual shadows) in wl_surface coordinates
934 */
939
952
953/*!
954 * Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
955 * topLeft is where the shadow stops and the decorations border start.
956 */
962
963/*!
964 * Converts from wl_surface coordinates to Qt window coordinates. Qt window
965 * coordinates start inside (not including) the window decorations, while
966 * wl_surface coordinates start at the first pixel of the buffer. Potentially,
967 * this should be in the window shadow, although we don't have those. So for
968 * now, it's the first pixel of the decorations.
969 */
975
980
989
991{
993 return mSurface ? mSurface->object() : nullptr;
994}
995
1000
1002{
1004 return mSubSurfaceWindow->object();
1005 if (mShellSurface)
1006 return mShellSurface->surfaceRole();
1007 return {};
1008}
1009
1014
1016{
1020 return nullptr;
1021 return static_cast<QWaylandScreen *>(platformScreen);
1022}
1023
1029
1031{
1033 if (mSurface == nullptr || mSurface->version() < 2)
1034 return;
1035
1038
1039 if (mSurface->version() >= 6) {
1041 if (auto screen = waylandScreen())
1043 } else {
1044 if (auto screen = window()->screen())
1046 }
1047
1049
1051 case Qt::PrimaryOrientation:
1053 break;
1056 break;
1057 case Qt::PortraitOrientation:
1059 break;
1062 break;
1065 break;
1066 default:
1067 Q_UNREACHABLE();
1068 }
1070}
1071
1077
1083
1085{
1086 const bool wasPopup = mFlags.testFlag(Qt::Popup);
1087 const bool isPopup = flags.testFlag(Qt::Popup);
1088
1089 mFlags = flags;
1090 // changing role is not allowed on XdgShell on the same wl_surface
1091 if (wasPopup != isPopup) {
1092 reset();
1094 if (window()->isVisible()) {
1095 initWindow();
1096 }
1097 } else {
1098 if (mShellSurface)
1100 }
1101
1103
1107 m_cutouts.reset();
1109 }
1110
1113}
1114
1116{
1117 return mFlags;
1118}
1119
1121{
1123 "QWaylandWindow::createDecoration", "not called from main thread");
1124 // TODO: client side decorations do not work with Vulkan backend.
1126 return false;
1128 return false;
1129
1130 static bool decorationPluginFailed = false;
1131 bool decoration = false;
1132 switch (window()->type()) {
1133 case Qt::Window:
1134 case Qt::Widget:
1135 case Qt::Dialog:
1136 case Qt::Tool:
1137 case Qt::Drawer:
1138 decoration = true;
1139 break;
1140 default:
1141 break;
1142 }
1144 decoration = false;
1146 decoration = false;
1148 decoration = false;
1150 decoration = false;
1151
1155 if (mWindowDecoration) {
1157 }
1158
1160 if (decorations.empty()) {
1161 qWarning() << "No decoration plugins available. Running with no decorations.";
1163 return false;
1164 }
1165
1167 QByteArray decorationPluginName = qgetenv("QT_WAYLAND_DECORATION");
1171 qWarning() << "Requested decoration " << targetKey << " not found, falling back to default";
1172 targetKey = QString(); // fallthrough
1173 }
1174 }
1175
1176 if (targetKey.isEmpty()) {
1177 auto unixServices = dynamic_cast<QDesktopUnixServices *>(
1180 if (desktopNames.contains("GNOME")) {
1181 if (decorations.contains("adwaita"_L1))
1182 targetKey = "adwaita"_L1;
1183 else if (decorations.contains("gnome"_L1))
1184 targetKey = "gnome"_L1;
1185 } else {
1186 // Do not use Adwaita/GNOME decorations on other DEs
1187 decorations.removeAll("adwaita"_L1);
1188 decorations.removeAll("gnome"_L1);
1189 }
1190 }
1191
1192 if (targetKey.isEmpty())
1193 targetKey = decorations.first(); // first come, first served.
1194
1196 if (!mWindowDecoration) {
1197 qWarning() << "Could not create decoration from factory! Running with no decorations.";
1199 return false;
1200 }
1203 }
1204 } else {
1206 }
1207
1212 subsurf->set_position(pos.x() + m.left(), pos.y() + m.top());
1213 }
1215
1216 // creating a decoration changes our margins which in turn change size hints
1218
1219 // This is a special case where the buffer is recreated, but since
1220 // the content rect remains the same, the widgets remain the same
1221 // size and are not redrawn, leaving the new buffer empty. As a simple
1222 // work-around, we trigger a full extra update whenever the client-side
1223 // window decorations are toggled while the window is showing.
1225 }
1226
1227 return mWindowDecoration.get();
1228}
1229
1234
1235static QWaylandWindow *closestShellSurfaceWindow(QWindow *window)
1236{
1237 while (window) {
1238 auto w = static_cast<QWaylandWindow *>(window->handle());
1239 if (w && w->shellSurface())
1240 return w;
1241 window = window->transientParent() ? window->transientParent() : window->parent();
1242 }
1243 return nullptr;
1244}
1245
1250
1252{
1253 // Take the closest window with a shell surface, since the transient parent may be a
1254 // QWidgetWindow or some other window without a shell surface, which is then not able to
1255 // get mouse events.
1257 return transientParent;
1258
1259 if (window()->type() == Qt::Popup) {
1260 if (mTopPopup)
1261 return mTopPopup;
1262 }
1263
1264 if (window()->type() == Qt::ToolTip || window()->type() == Qt::Popup) {
1265 if (auto lastInputWindow = display()->lastInputWindow())
1267 }
1268
1269 return nullptr;
1270}
1271
1273{
1274 // There's currently no way to get info about the actual hardware device in use.
1275 // At least we get the correct seat.
1277 if (e.type == QEvent::Leave) {
1281 } else {
1283 }
1284#if QT_CONFIG(cursor)
1286#endif
1287 return;
1288 }
1289
1290#if QT_CONFIG(cursor)
1291 if (e.type == QEvent::Enter) {
1293 }
1294#endif
1295
1298 } else {
1299 switch (e.type) {
1300 case QEvent::Enter:
1302#if QT_CONFIG(cursor)
1304#endif
1305 break;
1308 case QEvent::MouseMove:
1310 break;
1311 case QEvent::Wheel:
1314 e.phase, e.source, e.inverted);
1315 break;
1316 default:
1317 Q_UNREACHABLE();
1318 }
1319 }
1320}
1321
1322#ifndef QT_NO_GESTURES
1325{
1326 switch (e.state) {
1327 case Qt::GestureStarted:
1329 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1330
1332 // whole gesture sequence will be ignored
1334 return;
1335 }
1336
1341 e.local, e.global, e.fingers);
1342 break;
1343 case Qt::GestureUpdated:
1345 return;
1346
1347 if (!e.delta.isNull()) {
1351 0, e.delta, e.local, e.global, e.fingers);
1352 }
1353 break;
1354 case Qt::GestureFinished:
1355 case Qt::GestureCanceled:
1358 return;
1359 }
1360
1362 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1363
1365
1366 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1367 // this part of information is lost.
1371 e.local, e.global, e.fingers);
1372 break;
1373 default:
1374 break;
1375 }
1376}
1377
1380{
1381 switch (e.state) {
1382 case Qt::GestureStarted:
1384 qCWarning(lcQpaWaylandInput) << "Unexpected GestureStarted while already active";
1385
1387 // whole gesture sequence will be ignored
1389 return;
1390 }
1391
1396 e.local, e.global, e.fingers);
1397 break;
1398 case Qt::GestureUpdated:
1400 return;
1401
1402 if (!e.delta.isNull()) {
1406 0, e.delta, e.local, e.global, e.fingers);
1407 }
1408 if (e.rotation_delta != 0) {
1413 e.local, e.global, e.fingers);
1414 }
1415 if (e.scale_delta != 0) {
1419 e.scale_delta,
1420 e.local, e.global, e.fingers);
1421 }
1422 break;
1423 case Qt::GestureFinished:
1424 case Qt::GestureCanceled:
1427 return;
1428 }
1429
1431 qCWarning(lcQpaWaylandInput) << "Unexpected" << (e.state == Qt::GestureFinished ? "GestureFinished" : "GestureCanceled");
1432
1434
1435 // There's currently no way to expose cancelled gestures to the rest of Qt, so
1436 // this part of information is lost.
1440 e.local, e.global, e.fingers);
1441 break;
1442 default:
1443 break;
1444 }
1445}
1446#endif // #ifndef QT_NO_GESTURES
1447
1448
1455
1465
1467{
1468 // There's currently no way to get info about the actual hardware device in use.
1469 // At least we get the correct seat.
1476 }
1477 return;
1478 }
1479
1481 QRect windowRect(0 + marg.left(),
1482 0 + marg.top(),
1483 geometry().size().width(),
1484 geometry().size().height());
1491#if QT_CONFIG(cursor)
1493#endif
1495 }
1496
1497 switch (e.type) {
1498 case QEvent::Enter:
1500#if QT_CONFIG(cursor)
1502#endif
1503 break;
1506 case QEvent::MouseMove:
1508 break;
1509 case QEvent::Wheel: {
1513 e.phase, e.source, e.inverted);
1514 break;
1515 }
1516 default:
1517 Q_UNREACHABLE();
1518 }
1519
1522 } else {
1526 }
1527 }
1528}
1529
1531{
1533
1534 if (!newScreen || newScreen->screen() == window()->screen())
1535 return;
1536
1538
1540 && window()->type() != Qt::ToolTip && window()->type() != Qt::Tool
1541 && geometry().topLeft() != newScreen->geometry().topLeft()) {
1542 auto geometry = this->geometry();
1545 }
1546
1547 updateScale();
1549}
1550
1552{
1553 if (mFractionalScale) {
1558 return;
1559 }
1560
1561 if (mSurface && mSurface->version() >= 6) {
1565 return;
1566 }
1567
1568 int scale = screen()->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(screen())->scale();
1569 setScale(scale);
1570}
1571
1573{
1575 return;
1576 mScale = newScale;
1577
1578 if (mSurface) {
1579 if (mViewport)
1581 else if (mSurface->version() >= 3)
1583 }
1584 ensureSize();
1585
1587 if (isExposed()) {
1588 // redraw at the new DPR
1589 window()->requestUpdate();
1591 }
1592}
1593
1594#if QT_CONFIG(cursor)
1596{
1599 } else if (mHasStoredCursor) {
1601 } else {
1603 }
1604}
1605
1607{
1608 mHasStoredCursor = false;
1609}
1610
1611// Keep track of application set cursors on each window
1614 mHasStoredCursor = true;
1615}
1616
1618 if (!device || !device->pointer() || device->pointer()->focusSurface() != waylandSurface())
1619 return;
1620
1623}
1624#endif
1625
1631
1633{
1634 if (!window()->isVisible())
1635 return false;
1636
1638 return false;
1639
1640 if (mShellSurface)
1641 return mShellSurface->isExposed();
1642
1644 return mSubSurfaceWindow->parent()->isExposed();
1645
1647}
1648
1650{
1651 bool exposed = calculateExposure();
1652 if (exposed == mExposed)
1653 return;
1654
1655 mExposed = exposed;
1656
1657 if (!exposed)
1659 else
1661
1663 auto subWindow = subSurface->window();
1665 }
1666}
1667
1669{
1670 return mExposed;
1671}
1672
1674{
1675 return mDisplay->isWindowActivated(this);
1676}
1677
1679{
1680 return devicePixelRatio();
1681}
1682
1687
1689{
1690 if (window()->type() != Qt::Popup) {
1691 qWarning("This plugin supports grabbing the mouse only for popup windows");
1692 return false;
1693 }
1694
1695 mMouseGrab = grab ? this : nullptr;
1696 return true;
1697}
1698
1703
1708
1713
1723
1733
1741
1743{
1744 return m_properties;
1745}
1746
1751
1756
1757#ifdef QT_PLATFORM_WINDOW_HAS_VIRTUAL_SET_BACKING_STORE
1759{
1760 mBackingStore = dynamic_cast<QWaylandShmBackingStore *>(store);
1761}
1762#endif
1763
1765{
1767 return;
1768
1769 {
1771
1777 }
1779 return;
1780 }
1782 }
1783
1784 qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
1787}
1788
1790{
1791 qCDebug(lcWaylandBackingstore) << "requestUpdate";
1792 Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
1793
1794 // If we have a frame callback all is good and will be taken care of there
1795 {
1797 if (mFrameCallback)
1798 return;
1799 }
1800
1801 // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
1802 // so use invokeMethod to delay the delivery a bit.
1803 QMetaObject::invokeMethod(this, [this] {
1804 // Things might have changed in the meantime
1805 {
1807 if (mFrameCallback)
1808 return;
1809 }
1812 }, Qt::QueuedConnection);
1813}
1814
1815// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
1816// with eglSwapBuffers) to know when it's time to commit the next one.
1817// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
1819{
1821 qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
1822
1823 // TODO: Should sync subsurfaces avoid requesting frame callbacks?
1825 if (!mSurface)
1826 return;
1827
1829 if (mFrameCallback)
1830 return;
1831
1832 struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
1837
1838 // Start a timer for handling the case when the compositor stops sending frame callbacks.
1839 if (mFrameCallbackTimeout > 0) {
1840 QMetaObject::invokeMethod(this, [this] {
1842
1843 if (mFrameCallback) {
1847 }
1848 }, Qt::QueuedConnection);
1849 }
1850}
1851
1857
1859{
1860 qCDebug(lcWaylandBackingstore) << "deliverUpdateRequest";
1862
1863 // an update request doesn't always force a commit if the window isn't damaged, so also force it
1865 mPendingForcedUpdate = false;
1868 }
1869}
1870
1872{
1873 mOffset += point;
1874}
1875
1881
1883{
1884 if (auto *seat = display()->lastInputDevice()) {
1887 return rc;
1888 }
1889 return false;
1890}
1891
1893{
1894 if (auto seat = display()->lastInputDevice()) {
1897 return rc;
1898 }
1899 return false;
1900}
1901
1902bool QWaylandWindow::isOpaque() const
1903{
1904 return window()->requestedFormat().alphaBufferSize() <= 0;
1905}
1906
1908{
1910
1912 return;
1913
1915
1919}
1920
1922{
1923 if (!mShellSurface) {
1924 qCWarning(lcQpaWayland) << "requestXdgActivationToken is called with no surface role created, emitting synthetic signal";
1926 return;
1927 }
1929}
1930
1932{
1933 if (mShellSurface)
1935 else
1936 qCWarning(lcQpaWayland) << "setXdgActivationToken is called with no surface role created, token" << token << "discarded";
1937}
1938
1940{
1941 if (mShellSurface)
1944}
1945
1947{
1948 if (mShellSurface)
1951}
1952
1954 while (!mChildPopups.isEmpty()) {
1955 auto popup = mChildPopups.takeLast();
1957 }
1958}
1959
1961{
1962 if (window()->isVisible()) {
1963 initWindow();
1966 }
1967}
1968
1970{
1975 } else if (event->type() == QEvent::WindowUnblocked) {
1976 // QtGui sends leave event to window under cursor when modal window opens, so we have
1977 // to send enter event when modal closes and window has cursor and gets unblocked.
1979 const auto pos = mDisplay->waylandCursor()->pos();
1981 }
1982 }
1983
1985}
1986
1988{
1989 return mSurfaceFormat;
1990}
1991
1996
2001
2005
2007{
2009}
2010
2017
2019{
2021}
2022
2024{
2025 if (!window() || window()->flags() & Qt::WindowDoesNotAcceptFocus)
2026 return;
2027
2029 const bool isSubsurface = !!subSurfaceWindow();
2030
2031 auto toplevelIsActive = [&](QWaylandWindow *w) -> bool {
2032 while (w && w->subSurfaceWindow())
2033 w = static_cast<QWaylandWindow *>(w->QPlatformWindow::parent());
2034 return mDisplay->isWindowActivated(w);
2035 };
2036
2040 mDisplay->setActiveSubSurface(nullptr);
2041}
2042
2043}
2044
2045QT_END_NAMESPACE
2046
2047#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)