From 0b803993e706c6abb7eb3c49e3fc3a1fd7e288dc Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Mon, 8 Apr 2019 13:51:28 +0200 Subject: [PATCH 01/25] Client: Add safer fromObject function to scanner Makes the scanner produce generated static functions such as QtWaylandClient::wl_surface *wl_surface::fromObject(struct ::wl_surface *object); Which casts from the wayland-scanner generated types, such as struct ::wl_surface *, to types types generated by qtwaylandscanner, but performs a check to see if the listener is set to the wrapper class first (at least for interfaces with events). This lets us easily fix crashes in a couple of places where we receive events with wayland objects that we didn't create. Also adds nullptr checks whenever we use the fromWlSurface() and fromWlOutput() functions to handle failed conversions. Task-number: QTBUG-73801 Fixes: QTBUG-74085 Change-Id: I9f33c31c7d1a939ccb3ebbbcb0eb67af10037237 Reviewed-by: Jaroslaw Kubik Reviewed-by: Paul Olav Tvete (cherry picked from commit c6476ae915b8ac64121d379274363227b1c300ff) Reviewed-by: Pier Luigi Fiorini --- .../platforms/wayland/qwaylanddatadevice.cpp | 8 ++++++-- .../platforms/wayland/qwaylandinputdevice.cpp | 13 ++++++++++++- src/plugins/platforms/wayland/qwaylandqtkey.cpp | 6 +++++- src/plugins/platforms/wayland/qwaylandscreen.cpp | 5 +++-- src/tools/qtwaylandscanner/qtwaylandscanner.cpp | 11 +++++++++++ 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylanddatadevice.cpp b/src/plugins/platforms/wayland/qwaylanddatadevice.cpp index 11984f9d394..6b2a408eb19 100644 --- a/src/plugins/platforms/wayland/qwaylanddatadevice.cpp +++ b/src/plugins/platforms/wayland/qwaylanddatadevice.cpp @@ -154,9 +154,13 @@ void QWaylandDataDevice::data_device_drop() void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) { - m_enterSerial = serial; - m_dragWindow = QWaylandWindow::fromWlSurface(surface)->window(); + auto *dragWaylandWindow = QWaylandWindow::fromWlSurface(surface); + if (!dragWaylandWindow) + return; // Ignore foreign surfaces + + m_dragWindow = dragWaylandWindow->window(); m_dragPoint = calculateDragPosition(x, y, m_dragWindow); + m_enterSerial = serial; QMimeData *dragData = nullptr; Qt::DropActions supportedActions; diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 90e138a3dcc..495f258ded9 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -451,6 +451,9 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf return; QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; // Ignore foreign surfaces + mFocus = window; mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy)); mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint()); @@ -477,6 +480,10 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac if (!surface) return; + auto *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; // Ignore foreign surfaces + if (!QWaylandWindow::mouseGrab()) { QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); window->handleMouseLeave(mParent); @@ -852,9 +859,13 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial, if (!surface) return; + auto *window = QWaylandWindow::fromWlSurface(surface); + if (!window) + return; // Ignore foreign surfaces + mParent->mTime = time; mParent->mSerial = serial; - mFocus = QWaylandWindow::fromWlSurface(surface); + mFocus = window; mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus); mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed); } diff --git a/src/plugins/platforms/wayland/qwaylandqtkey.cpp b/src/plugins/platforms/wayland/qwaylandqtkey.cpp index a60185bd641..19261973853 100644 --- a/src/plugins/platforms/wayland/qwaylandqtkey.cpp +++ b/src/plugins/platforms/wayland/qwaylandqtkey.cpp @@ -70,7 +70,11 @@ void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface, } QWaylandInputDevice *dev = inputDevices.first(); - QWaylandWindow *win = surface ? QWaylandWindow::fromWlSurface(surface) : dev->keyboardFocus(); + + auto *win = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr; + + if (!win) + win = dev->keyboardFocus(); if (!win || !win->window()) { qWarning("qt_key_extension: handle_qtkey: No keyboard focus"); diff --git a/src/plugins/platforms/wayland/qwaylandscreen.cpp b/src/plugins/platforms/wayland/qwaylandscreen.cpp index 38d61f88c35..1fe0125e64b 100644 --- a/src/plugins/platforms/wayland/qwaylandscreen.cpp +++ b/src/plugins/platforms/wayland/qwaylandscreen.cpp @@ -199,8 +199,9 @@ QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window) QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output) { - auto wlOutput = static_cast(wl_output_get_user_data(output)); - return static_cast(wlOutput); + if (auto *o = QtWayland::wl_output::fromObject(output)) + return static_cast(o); + return nullptr; } void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh) diff --git a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp index 6f4a33b5f6b..c617369aee2 100644 --- a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp +++ b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp @@ -982,6 +982,7 @@ bool Scanner::process() printf("\n"); printf(" struct ::%s *object() { return m_%s; }\n", interfaceName, interfaceName); printf(" const struct ::%s *object() const { return m_%s; }\n", interfaceName, interfaceName); + printf(" static %s *fromObject(struct ::%s *object);\n", interfaceName, interfaceName); printf("\n"); printf(" bool isInitialized() const;\n"); printf("\n"); @@ -1110,6 +1111,16 @@ bool Scanner::process() printf(" }\n"); printf("\n"); + printf(" %s *%s::fromObject(struct ::%s *object)\n", interfaceName, interfaceName, interfaceName); + printf(" {\n"); + if (hasEvents) { + printf(" if (wl_proxy_get_listener((struct ::wl_proxy *)object) != (void *)&m_%s_listener)\n", interfaceName); + printf(" return nullptr;\n"); + } + printf(" return static_cast<%s *>(%s_get_user_data(object));\n", interfaceName, interfaceName); + printf(" }\n"); + printf("\n"); + printf(" bool %s::isInitialized() const\n", interfaceName); printf(" {\n"); printf(" return m_%s != nullptr;\n", interfaceName); From 7163c286e4b451c8436a95b94dc32b7f62b19ee2 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 16 Aug 2019 12:54:56 +0200 Subject: [PATCH 02/25] Fix incorrect conversion to straight alpha pixel formats Previously, QWaylandSharedMemoryFormatHelper::fromWaylandShmFormat(WL_SHM_FORMAT_ARGB8888) would return Format_ARGB32, i.e. the non-premultiplied version, while, according to the wayland-devel mailing list (https://lists.freedesktop.org/archives/wayland-devel/2017-August/034791.html), all Wayland RGB-based pixel formats with alpha should have premultiplied alpha. This patch makes sure we return the premultiplied variants for ARGB8888, as well as for ABGR8888. Using a switch instead of the array also allows us more freedom to choose the preferred format in other cases where multiple QImage formats map to the same wl_shm format. While being wrapped and exported as QtWaylandClient::QWaylandShm::fromFormat (private API), this conversion function doesn't seem to be used anywhere, so this patch shouldn't cause any changes in behavior for projects that only use public API. Change-Id: Ie09f9a339b4540dd0383a72b3c951eb8c93e3ab4 Reviewed-by: Pier Luigi Fiorini --- .../qwaylandsharedmemoryformathelper_p.h | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h b/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h index 85905c02fa3..72cc8401c26 100644 --- a/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h +++ b/src/plugins/platforms/wayland/shared/qwaylandsharedmemoryformathelper_p.h @@ -51,7 +51,26 @@ class QWaylandSharedMemoryFormatHelper { public: static inline wl_shm_format fromQImageFormat(QImage::Format format); - static inline QImage::Format fromWaylandShmFormat(wl_shm_format format); + static inline QImage::Format fromWaylandShmFormat(wl_shm_format format) + { + switch (format) { + case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32; + case WL_SHM_FORMAT_ARGB8888: return QImage::Format_ARGB32_Premultiplied; + case WL_SHM_FORMAT_RGB565: return QImage::Format_RGB16; + case WL_SHM_FORMAT_XRGB1555: return QImage::Format_RGB555; + case WL_SHM_FORMAT_RGB888: return QImage::Format_RGB888; + case WL_SHM_FORMAT_XRGB4444: return QImage::Format_RGB444; + case WL_SHM_FORMAT_ARGB4444: return QImage::Format_ARGB4444_Premultiplied; + case WL_SHM_FORMAT_XBGR8888: return QImage::Format_RGBX8888; + case WL_SHM_FORMAT_ABGR8888: return QImage::Format_RGBA8888_Premultiplied; + case WL_SHM_FORMAT_XBGR2101010: return QImage::Format_BGR30; + case WL_SHM_FORMAT_ABGR2101010: return QImage::Format_A2BGR30_Premultiplied; + case WL_SHM_FORMAT_XRGB2101010: return QImage::Format_RGB30; + case WL_SHM_FORMAT_ARGB2101010: return QImage::Format_A2RGB30_Premultiplied; + case WL_SHM_FORMAT_C8: return QImage::Format_Alpha8; + default: return QImage::Format_Invalid; + } + } static inline QVector supportedWaylandFormats(); private: @@ -108,16 +127,6 @@ wl_shm_format QWaylandSharedMemoryFormatHelper::fromQImageFormat(QImage::Format return array.data[format]; } -QImage::Format QWaylandSharedMemoryFormatHelper::fromWaylandShmFormat(wl_shm_format format) -{ - Array array = getData(); - for (size_t i = 0; i < array.size; i++) { - if (array.data[i] == format) - return QImage::Format(i); - } - return QImage::Format_Invalid; -} - QVector QWaylandSharedMemoryFormatHelper::supportedWaylandFormats() { QVector retFormats; From cb68cead894f058315fe7a0736d6f9da24d4fcd1 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Tue, 23 Jul 2019 01:40:36 +0200 Subject: [PATCH 03/25] Client: Fix large clipboard pasting With the non-blocking file opening, when no data was available in the pipe, read could return 0 even though we were not at the end of the file. This resulted in truncated data when pasting the clipboard. This patch uses select to block until a file is available, removing the polling sleep. This means the file can be safely opened in blocking mode whilst keeping the timeout feature allowing read to work as intended. Change-Id: I936aa85e9f25197e7abe6fb3fa729b618c00924d Reviewed-by: David Faure Reviewed-by: Johan Helsing --- .../platforms/wayland/qwaylanddataoffer.cpp | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylanddataoffer.cpp b/src/plugins/platforms/wayland/qwaylanddataoffer.cpp index 0c732c02078..3da16ed0041 100644 --- a/src/plugins/platforms/wayland/qwaylanddataoffer.cpp +++ b/src/plugins/platforms/wayland/qwaylanddataoffer.cpp @@ -135,7 +135,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T } int pipefd[2]; - if (qt_safe_pipe(pipefd, O_NONBLOCK) == -1) { + if (qt_safe_pipe(pipefd) == -1) { qWarning("QWaylandMimeData: pipe2() failed"); return QVariant(); } @@ -158,23 +158,32 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T int QWaylandMimeData::readData(int fd, QByteArray &data) const { - char buf[4096]; - int retryCount = 0; - int n; - while (true) { - n = QT_READ(fd, buf, sizeof buf); - if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && ++retryCount < 1000) - usleep(1000); - else - break; - } - if (retryCount >= 1000) + fd_set readset; + FD_ZERO(&readset); + FD_SET(fd, &readset); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout); + if (ready < 0) { + qWarning() << "QWaylandDataOffer: select() failed"; + return -1; + } else if (ready == 0) { qWarning("QWaylandDataOffer: timeout reading from pipe"); - if (n > 0) { - data.append(buf, n); - n = readData(fd, data); + return -1; + } else { + char buf[4096]; + int n = QT_READ(fd, buf, sizeof buf); + + if (n > 0) { + data.append(buf, n); + n = readData(fd, data); + } else if (n < 0) { + qWarning("QWaylandDataOffer: read() failed"); + } + return n; } - return n; } } From ec510cb38c00231406f77dfea771e3c96874412b Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 26 Jun 2019 09:47:03 +0200 Subject: [PATCH 04/25] Client: Emit wl_surface lifetime signals on QWaylandWindow Recent changes in QtBase means QtWayland will have to follow the convention of the rest of the QPA plugins and have QPlatformSurfaceEvent::SurfaceCreated and SurfaceAboutToBeDestroyed follow the QPlatformWindow (QWaylandWindow) lifetime and not the lifetime of wl_surface. Some users were depending on those events to get notified about wl_surface changes and used QPlatformNativeInterface in order to get the window's underlying wl_surfaces in responses to the events. The good news is that QPlatformNativeInterface is private (QPA) API, so we can provide an alternative by exposing new private API, which is what this patch does. The QWaylandWindow::wlSurfaceDestroyed signal already exists in the dev branch (introduced in 04be9cf380), so this is a backport of that signal as well as an addition of a new QWaylandWindow::wlSurfaceCreated signal. Task-number: QTBUG-76324 Change-Id: Ibc5748474cd52f5b9461fd1ad6cef973491174b1 Reviewed-by: Pier Luigi Fiorini --- src/plugins/platforms/wayland/qwaylandwindow.cpp | 6 +++++- src/plugins/platforms/wayland/qwaylandwindow_p.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 76d7715a852..abc54f58438 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -210,7 +210,9 @@ void QWaylandWindow::initWindow() void QWaylandWindow::initializeWlSurface() { + Q_ASSERT(!isInitialized()); init(mDisplay->createSurface(static_cast(this))); + emit wlSurfaceCreated(); } bool QWaylandWindow::shouldCreateShellSurface() const @@ -245,8 +247,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mShellSurface = nullptr; delete mSubSurfaceWindow; mSubSurfaceWindow = nullptr; - if (isInitialized()) + if (isInitialized()) { + emit wlSurfaceDestroyed(); destroy(); + } mScreens.clear(); if (mFrameCallback) { diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 23432e39873..ec9bd7d0288 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -199,6 +199,10 @@ public: public slots: void applyConfigure(); +signals: + void wlSurfaceCreated(); + void wlSurfaceDestroyed(); + protected: void surface_enter(struct ::wl_output *output) override; void surface_leave(struct ::wl_output *output) override; From 8b50d2c6cc99e6da91ac50477ae8191e29b35f8c Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Mon, 8 Jul 2019 10:22:03 +0200 Subject: [PATCH 05/25] Fix race condition for client buffer integration initialization This race happened because QWaylandIntegration::clientBufferIntegration (which lazily initializes the integration) was called from numerous places including code that may run on different threads without any kind of syncrhonization. An example of this is Qt3D, which indirectly and simultaneously calls createPlatformWindow (from the GUI thread) and createPlatformOpenGLContext (from its render thread), both of which needs to use the client buffer integration. In this patch, we fix it by first checking if the integration is initialized. In that case, it's safe to use it. Otherwise we lock a mutex, re-check if initialization has happened on another thread in the meantime, and then finally we initialize it. This way we should avoid the expense of mutex locking after initialization is complete, while still staying race free during initialization. Fixes: QTBUG-76504 Change-Id: I327840ebf41e014882cb659bc3e4fafb7bdb7a98 Reviewed-by: David Edmundson Reviewed-by: Paul Olav Tvete --- .../platforms/wayland/qwaylandintegration.cpp | 31 ++++++++++++------- .../platforms/wayland/qwaylandintegration_p.h | 2 ++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 97e0203cdc9..46bef2944fe 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -301,11 +301,14 @@ QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) co return GenericWaylandTheme::createUnixTheme(name); } +// May be called from non-GUI threads QWaylandClientBufferIntegration *QWaylandIntegration::clientBufferIntegration() const { - if (!mClientBufferIntegrationInitialized) + // Do an inexpensive check first to avoid locking whenever possible + if (Q_UNLIKELY(!mClientBufferIntegrationInitialized)) const_cast(this)->initializeClientBufferIntegration(); + Q_ASSERT(mClientBufferIntegrationInitialized); return mClientBufferIntegration && mClientBufferIntegration->isValid() ? mClientBufferIntegration.data() : nullptr; } @@ -325,9 +328,12 @@ QWaylandShellIntegration *QWaylandIntegration::shellIntegration() const return mShellIntegration.data(); } +// May be called from non-GUI threads void QWaylandIntegration::initializeClientBufferIntegration() { - mClientBufferIntegrationInitialized = true; + QMutexLocker lock(&mClientBufferInitLock); + if (mClientBufferIntegrationInitialized) + return; QString targetKey; bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION"); @@ -345,17 +351,20 @@ void QWaylandIntegration::initializeClientBufferIntegration() if (targetKey.isEmpty()) { qWarning("Failed to determine what client buffer integration to use"); - return; + } else { + QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); + if (keys.contains(targetKey)) { + mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList())); + } + if (mClientBufferIntegration) + mClientBufferIntegration->initialize(mDisplay.data()); + else + qWarning("Failed to load client buffer integration: %s\n", qPrintable(targetKey)); } - QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); - if (keys.contains(targetKey)) { - mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList())); - } - if (mClientBufferIntegration) - mClientBufferIntegration->initialize(mDisplay.data()); - else - qWarning("Failed to load client buffer integration: %s\n", qPrintable(targetKey)); + // This must be set last to make sure other threads don't use the + // integration before initialization is complete. + mClientBufferIntegrationInitialized = true; } void QWaylandIntegration::initializeServerBufferIntegration() diff --git a/src/plugins/platforms/wayland/qwaylandintegration_p.h b/src/plugins/platforms/wayland/qwaylandintegration_p.h index a5a3d7b6956..7c1cb978abe 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration_p.h +++ b/src/plugins/platforms/wayland/qwaylandintegration_p.h @@ -54,6 +54,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -148,6 +149,7 @@ private: QScopedPointer mAccessibility; #endif bool mFailed = false; + QMutex mClientBufferInitLock; bool mClientBufferIntegrationInitialized = false; bool mServerBufferIntegrationInitialized = false; bool mShellIntegrationInitialized = false; From f168c11529709a4fc3c3ec180bbc5c2e47960b34 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 9 Jul 2019 13:15:20 +0200 Subject: [PATCH 06/25] Handle Key_Return explicitly instead of depending on the text When Key_Return is sent from Qt VirtualKeyboard it will send it as \n and not \r which means it will be interpreted as an unknown key. So since we know it will be able to map it in this case, we explicitly account for it so it can be mapped to the right key. Change-Id: Id5d8d9653e78975203f80790b7a3d332f0e011fa Reviewed-by: Johan Helsing --- src/plugins/platforms/wayland/shared/qwaylandxkb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/wayland/shared/qwaylandxkb.cpp b/src/plugins/platforms/wayland/shared/qwaylandxkb.cpp index 3cfc4b074e5..2dff8a5b16d 100644 --- a/src/plugins/platforms/wayland/shared/qwaylandxkb.cpp +++ b/src/plugins/platforms/wayland/shared/qwaylandxkb.cpp @@ -376,7 +376,7 @@ QVector QWaylandXkb::toKeysym(QKeyEvent *event) keysyms.append(XKB_KEY_KP_0 + (event->key() - Qt::Key_0)); else keysyms.append(toKeysymFromTable(event->key())); - } else if (!event->text().isEmpty()) { + } else if (!event->text().isEmpty() && event->key() != Qt::Key_Return) { // From libxkbcommon keysym-utf.c: // "We allow to represent any UCS character in the range U-00000000 to // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." From b4ee20efff96534895060dab6a3e7ced5d41f9c2 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Sun, 23 Jun 2019 14:48:30 +0200 Subject: [PATCH 07/25] Client: Make handleUpdate aware of exposure changes The wl_surface can be destroyed whilst a render is happening. Calling wl_surface::frame after the window is reset can crash as wl_surface is null. Fixes: QTBUG-77747 Change-Id: I139a9b234cb6acba81d6c1d5fa58629904a25053 Reviewed-by: Paul Olav Tvete --- src/plugins/platforms/wayland/qwaylandwindow.cpp | 9 ++++++++- src/plugins/platforms/wayland/qwaylandwindow_p.h | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 76d7715a852..1d4315a395f 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -210,6 +210,8 @@ void QWaylandWindow::initWindow() void QWaylandWindow::initializeWlSurface() { + Q_ASSERT(!isInitialized()); + QWriteLocker lock(&mSurfaceLock); init(mDisplay->createSurface(static_cast(this))); } @@ -245,8 +247,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mShellSurface = nullptr; delete mSubSurfaceWindow; mSubSurfaceWindow = nullptr; - if (isInitialized()) + if (isInitialized()) { + QWriteLocker lock(&mSurfaceLock); destroy(); + } mScreens.clear(); if (mFrameCallback) { @@ -1147,6 +1151,9 @@ void QWaylandWindow::requestUpdate() void QWaylandWindow::handleUpdate() { // TODO: Should sync subsurfaces avoid requesting frame callbacks? + QReadLocker lock(&mSurfaceLock); + if (!isInitialized()) + return; if (mFrameCallback) { wl_callback_destroy(mFrameCallback); diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 23432e39873..ed4061f0eae 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -53,6 +53,8 @@ #include #include +#include + #include #include #include @@ -272,6 +274,8 @@ private: static QMutex mFrameSyncMutex; static QWaylandWindow *mMouseGrab; + QReadWriteLock mSurfaceLock; + friend class QWaylandSubSurface; }; From d87b6d823153d855fe4b052ccd503eeb6cdbcd8d Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 20 Aug 2019 13:38:12 +0200 Subject: [PATCH 08/25] Client: Crash instead of exit when there's a wayland error Qt applications should not call exit. Task-number: QTBUG-75779 Change-Id: I91190b10f8c8e111996cd73283061e6ceaa6b1f6 Reviewed-by: Pier Luigi Fiorini --- .../platforms/wayland/qwaylanddisplay.cpp | 22 +++++-------------- .../platforms/wayland/qwaylanddisplay_p.h | 1 - 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index 82003a308c6..47b107e807f 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -172,9 +172,9 @@ void QWaylandDisplay::checkError() const int ecode = wl_display_get_error(mDisplay); if ((ecode == EPIPE || ecode == ECONNRESET)) { // special case this to provide a nicer error - qWarning("The Wayland connection broke. Did the Wayland compositor die?"); + qFatal("The Wayland connection broke. Did the Wayland compositor die?"); } else { - qErrnoWarning(ecode, "The Wayland connection experienced a fatal error"); + qFatal("The Wayland connection experienced a fatal error: %s", strerror(ecode)); } } @@ -184,25 +184,16 @@ void QWaylandDisplay::flushRequests() wl_display_read_events(mDisplay); } - if (wl_display_dispatch_pending(mDisplay) < 0) { + if (wl_display_dispatch_pending(mDisplay) < 0) checkError(); - exitWithError(); - } wl_display_flush(mDisplay); } void QWaylandDisplay::blockingReadEvents() { - if (wl_display_dispatch(mDisplay) < 0) { + if (wl_display_dispatch(mDisplay) < 0) checkError(); - exitWithError(); - } -} - -void QWaylandDisplay::exitWithError() -{ - ::exit(1); } wl_event_queue *QWaylandDisplay::createEventQueue() @@ -231,10 +222,9 @@ void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function Date: Wed, 28 Aug 2019 11:13:55 +0200 Subject: [PATCH 09/25] Client tests: Set XDG_CURRENT_DESKTOP to avoid platform themes If the client tests are run from within a desktop environment, the XDG_CURRENT_DESKTOP variable may be set, causing the client to try to load a platform theme for the user's desktop. The tests, however, are running against a mock Wayland compositor, so launching a platform theme that expects to connect to a certain compositor implementation is probably not a good idea. And furthermore, if the gtk3 platform theme is used, it will start binding to interfaces and create wayland objects, confusing our existing test code. Setting XDG_CURRENT_DESKTOP to qtwaylandtests will prevent detection of the (outer) desktop environment, making tests more predictable. Change-Id: I57fa76e51cecdd0cbe8be6bd075ce67e9d2892bc Reviewed-by: David Edmundson Reviewed-by: Paul Olav Tvete --- tests/auto/wayland/shared/mockcompositor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/wayland/shared/mockcompositor.h b/tests/auto/wayland/shared/mockcompositor.h index 75ef1eaea1d..05bf32c8d50 100644 --- a/tests/auto/wayland/shared/mockcompositor.h +++ b/tests/auto/wayland/shared/mockcompositor.h @@ -78,6 +78,7 @@ public: int main(int argc, char **argv) \ { \ setenv("XDG_RUNTIME_DIR", ".", 1); \ + setenv("XDG_CURRENT_DESKTOP", "qtwaylandtests", 1); \ setenv("QT_QPA_PLATFORM", "wayland", 1); \ test tc; \ QGuiApplication app(argc, argv); \ From 59486fdf3ba485524a16578a5b76051f2f8c0026 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 21 Aug 2019 13:44:05 +0200 Subject: [PATCH 10/25] Client: Don't freeze in QDrag::exec if there was no drag focus 002fade6 fixed a crash when starting a drag without a valid focus, but there is still a problem, because QDrag::exec will never return because it's waiting indefinitely in an event loop. - QWaylandDataDevice::startDrag can now fail by returning false. - When starting a drag fails, we cancel the drag through QWaylandDrag::cancelDrag wrapped in invokeMethod. - Also, don't unnecessarily create a data_source if we cannot start a drag. [ChangeLog][QPA plugin] Fixed a freeze that happened when starting a drag-and-drop operation without a valid source surface. Change-Id: Iea19b0c92c196a44d1274a966bee4ff519632d34 Reviewed-by: Paul Olav Tvete --- .../platforms/wayland/qwaylanddatadevice.cpp | 17 ++++++++++------- .../platforms/wayland/qwaylanddatadevice_p.h | 2 +- src/plugins/platforms/wayland/qwaylanddnd.cpp | 9 +++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylanddatadevice.cpp b/src/plugins/platforms/wayland/qwaylanddatadevice.cpp index 6b2a408eb19..990f92ba9fc 100644 --- a/src/plugins/platforms/wayland/qwaylanddatadevice.cpp +++ b/src/plugins/platforms/wayland/qwaylanddatadevice.cpp @@ -102,19 +102,22 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const return m_dragOffer.data(); } -void QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) +bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) { - m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); - connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); - QWaylandWindow *origin = m_display->currentInputDevice()->pointerFocus(); if (!origin) origin = m_display->currentInputDevice()->touchFocus(); - if (origin) - start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial()); - else + if (!origin) { qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found."; + return false; + } + + m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); + connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); + + start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial()); + return true; } void QWaylandDataDevice::cancelDrag() diff --git a/src/plugins/platforms/wayland/qwaylanddatadevice_p.h b/src/plugins/platforms/wayland/qwaylanddatadevice_p.h index 0a7f42538da..16c3ad28e44 100644 --- a/src/plugins/platforms/wayland/qwaylanddatadevice_p.h +++ b/src/plugins/platforms/wayland/qwaylanddatadevice_p.h @@ -89,7 +89,7 @@ public: #if QT_CONFIG(draganddrop) QWaylandDataOffer *dragOffer() const; - void startDrag(QMimeData *mimeData, QWaylandWindow *icon); + bool startDrag(QMimeData *mimeData, QWaylandWindow *icon); void cancelDrag(); #endif diff --git a/src/plugins/platforms/wayland/qwaylanddnd.cpp b/src/plugins/platforms/wayland/qwaylanddnd.cpp index b01a9db3614..6535aa16be1 100644 --- a/src/plugins/platforms/wayland/qwaylanddnd.cpp +++ b/src/plugins/platforms/wayland/qwaylanddnd.cpp @@ -66,8 +66,13 @@ void QWaylandDrag::startDrag() { QBasicDrag::startDrag(); QWaylandWindow *icon = static_cast(shapedPixmapWindow()->handle()); - m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon); - icon->addAttachOffset(-drag()->hotSpot()); + if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) { + icon->addAttachOffset(-drag()->hotSpot()); + } else { + // Cancelling immediately does not work, since the event loop for QDrag::exec is started + // after this function returns. + QMetaObject::invokeMethod(this, [this](){ cancelDrag(); }, Qt::QueuedConnection); + } } void QWaylandDrag::cancel() From 84acc4a4e9da1b7a691fd95af2872cda2ccdc648 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Mon, 19 Aug 2019 13:46:08 +0200 Subject: [PATCH 11/25] Client: Refactor touch handling and fix various bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename mTouchPoints to mPendingTouchPoints, to clarify that they're the accumulated state so far, which will be applied with the wl_touch.frame event. QWaylandInputDevice::Touch::mPrevTouchPoints is no longer needed and has been removed. Fixes the following issues with the old approach: - touchPointsReleased() only checked mTouchPoints, which was cleared on touch_frame and populated again on touch_motion and touch_down, which meant that it could return true even though there were still touch points left. Leading to the workaround for missing wl_touch.frame events on Weston being triggered to often. - Touch focus was cleared on any wl_touch.up event, not just the last one. - The order of the touch events was not stable and relied on the order of the events (QTBUG-77014). Fixes: QTBUG-77014 Change-Id: Ic3ecdc87e77b0e0276afefd127ad2b965142cbd4 Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- .../platforms/wayland/qwaylandinputdevice.cpp | 102 ++++++++---------- .../platforms/wayland/qwaylandinputdevice_p.h | 3 +- 2 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 495f258ded9..6016589a6ef 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -874,15 +874,20 @@ void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_ { Q_UNUSED(serial); Q_UNUSED(time); - mFocus = nullptr; mParent->handleTouchPoint(id, 0, 0, Qt::TouchPointReleased); - // As of Weston 1.5.90 there is no touch_frame after the last touch_up - // (i.e. when the last finger is released). To accommodate for this, issue a - // touch_frame. This cannot hurt since it is safe to call the touch_frame - // handler multiple times when there are no points left. - if (allTouchPointsReleased()) + if (allTouchPointsReleased()) { + mFocus = nullptr; + + // As of Weston 7.0.0 there is no touch_frame after the last touch_up + // (i.e. when the last finger is released). To accommodate for this, issue a + // touch_frame. This cannot hurt since it is safe to call the touch_frame + // handler multiple times when there are no points left. + // See: https://gitlab.freedesktop.org/wayland/weston/issues/44 + // TODO: change logging category to lcQpaWaylandInput in newer versions. + qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug"); touch_frame(); + } } void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) @@ -893,8 +898,7 @@ void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixe void QWaylandInputDevice::Touch::touch_cancel() { - mPrevTouchPoints.clear(); - mTouchPoints.clear(); + mPendingTouchPoints.clear(); QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension(); if (touchExt) @@ -905,19 +909,16 @@ void QWaylandInputDevice::Touch::touch_cancel() void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::TouchPointState state) { - QWindowSystemInterface::TouchPoint tp; + auto end = mTouch->mPendingTouchPoints.end(); + auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](auto tp){ return tp.id == id; }); + if (it == end) { + it = mTouch->mPendingTouchPoints.insert(end, QWindowSystemInterface::TouchPoint()); + it->id = id; + } + QWindowSystemInterface::TouchPoint &tp = *it; - // Find out the coordinates for Released events. - bool coordsOk = false; - if (state == Qt::TouchPointReleased) - for (int i = 0; i < mTouch->mPrevTouchPoints.count(); ++i) - if (mTouch->mPrevTouchPoints.at(i).id == id) { - tp.area = mTouch->mPrevTouchPoints.at(i).area; - coordsOk = true; - break; - } - - if (!coordsOk) { + // Only moved and pressed needs to update/set position + if (state == Qt::TouchPointMoved || state == Qt::TouchPointPressed) { // x and y are surface relative. // We need a global (screen) position. QWaylandWindow *win = mTouch->mFocus; @@ -936,59 +937,37 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch } tp.state = state; - tp.id = id; tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1; - mTouch->mTouchPoints.append(tp); } bool QWaylandInputDevice::Touch::allTouchPointsReleased() { - for (int i = 0; i < mTouchPoints.count(); ++i) - if (mTouchPoints.at(i).state != Qt::TouchPointReleased) + for (const auto &tp : qAsConst(mPendingTouchPoints)) { + if (tp.state != Qt::TouchPointReleased) return false; - + } return true; } void QWaylandInputDevice::Touch::releasePoints() { - Q_FOREACH (const QWindowSystemInterface::TouchPoint &previousPoint, mPrevTouchPoints) { - QWindowSystemInterface::TouchPoint tp = previousPoint; + if (mPendingTouchPoints.empty()) + return; + + for (QWindowSystemInterface::TouchPoint &tp : mPendingTouchPoints) tp.state = Qt::TouchPointReleased; - mTouchPoints.append(tp); - } + touch_frame(); } void QWaylandInputDevice::Touch::touch_frame() { - // Copy all points, that are in the previous but not in the current list, as stationary. - for (int i = 0; i < mPrevTouchPoints.count(); ++i) { - const QWindowSystemInterface::TouchPoint &prevPoint(mPrevTouchPoints.at(i)); - if (prevPoint.state == Qt::TouchPointReleased) - continue; - bool found = false; - for (int j = 0; j < mTouchPoints.count(); ++j) - if (mTouchPoints.at(j).id == prevPoint.id) { - found = true; - break; - } - if (!found) { - QWindowSystemInterface::TouchPoint p = prevPoint; - p.state = Qt::TouchPointStationary; - mTouchPoints.append(p); - } - } - - if (mTouchPoints.isEmpty()) { - mPrevTouchPoints.clear(); - return; - } + // TODO: early return if no events? QWindow *window = mFocus ? mFocus->window() : nullptr; if (mFocus) { - const QWindowSystemInterface::TouchPoint &tp = mTouchPoints.last(); + const QWindowSystemInterface::TouchPoint &tp = mPendingTouchPoints.last(); // When the touch event is received, the global pos is calculated with the margins // in mind. Now we need to adjust again to get the correct local pos back. QMargins margins = window->frameMargins(); @@ -997,14 +976,21 @@ void QWaylandInputDevice::Touch::touch_frame() if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers())) return; } - QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mTouchPoints); - if (allTouchPointsReleased()) - mPrevTouchPoints.clear(); - else - mPrevTouchPoints = mTouchPoints; + QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mPendingTouchPoints); + + // Prepare state for next frame + const auto prevTouchPoints = mPendingTouchPoints; + mPendingTouchPoints.clear(); + for (const auto &prevPoint: prevTouchPoints) { + // All non-released touch points should be part of the next touch event + if (prevPoint.state != Qt::TouchPointReleased) { + QWindowSystemInterface::TouchPoint tp = prevPoint; + tp.state = Qt::TouchPointStationary; // ... as stationary (unless proven otherwise) + mPendingTouchPoints.append(tp); + } + } - mTouchPoints.clear(); } } diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h index 7aa86539b49..d9bae9836c7 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h +++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h @@ -304,8 +304,7 @@ public: QWaylandInputDevice *mParent = nullptr; QPointer mFocus; - QList mTouchPoints; - QList mPrevTouchPoints; + QList mPendingTouchPoints; }; class QWaylandPointerEvent From f199375e5cee3e115aa90f3bdf4105d94cc25c13 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Mon, 2 Sep 2019 10:45:20 +0200 Subject: [PATCH 12/25] Client: Fix crash on wl_pointer.up after destroying a window When wl_pointer version 5 was implemented, we added a raw QWaylandWindow pointer in QWaylandPointerEvent. This is a problem, because the events are stored, and the window may be deleted in the meantime. This manifested itself as flakiness in tst_xdgshell::popup() which is now fixed. Fixes: QTBUG-77976 Change-Id: If34eee0286d5a63734535d67503378516d5768c3 Reviewed-by: Paul Olav Tvete --- src/plugins/platforms/wayland/qwaylandinputdevice.cpp | 11 +++++++++-- src/plugins/platforms/wayland/qwaylandinputdevice_p.h | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 8aa0239d079..f2dee75e63c 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -1045,8 +1045,15 @@ void QWaylandInputDevice::Pointer::flushScrollEvent() void QWaylandInputDevice::Pointer::flushFrameEvent() { - if (mFrameData.event) { - mFrameData.event->surface->handleMouse(mParent, *mFrameData.event); + if (auto *event = mFrameData.event) { + if (auto window = event->surface) { + window->handleMouse(mParent, *event); + } else if (mFrameData.event->type == QWaylandPointerEvent::Type::Release) { + // If the window has been destroyed, we still need to report an up event, but it can't + // be handled by the destroyed window (obviously), so send the event here instead. + QWindowSystemInterface::handleMouseEvent(nullptr, event->timestamp, event->local, + event->global, event->buttons, event->modifiers); + } delete mFrameData.event; mFrameData.event = nullptr; } diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h index 4ac1dca3525..7fbb5667f70 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h +++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h @@ -446,7 +446,7 @@ public: QPoint pixelDelta; QPoint angleDelta; Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; - QWaylandWindow *surface = nullptr; + QPointer surface; }; } From 47340e8c6f4b441f11ac6ec9496b64bc956d03be Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 24 May 2019 14:20:18 +0200 Subject: [PATCH 13/25] Add client test for xdg-decoration-unstable-v1 And verify that we don't create decorations prematurely. Change-Id: I621631f0c355529e5afb6a615f909f18c2a4b509 Reviewed-by: David Edmundson Reviewed-by: Paul Olav Tvete --- tests/auto/wayland/client.pro | 1 + .../xdgdecorationv1/tst_xdgdecorationv1.cpp | 186 ++++++++++++++++++ .../xdgdecorationv1/xdgdecorationv1.pro | 7 + 3 files changed, 194 insertions(+) create mode 100644 tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp create mode 100644 tests/auto/wayland/xdgdecorationv1/xdgdecorationv1.pro diff --git a/tests/auto/wayland/client.pro b/tests/auto/wayland/client.pro index 051cb4e3d13..06c1cb8772a 100644 --- a/tests/auto/wayland/client.pro +++ b/tests/auto/wayland/client.pro @@ -9,6 +9,7 @@ SUBDIRS += \ seatv4 \ surface \ wl_connect \ + xdgdecorationv1 \ xdgoutput \ xdgshell \ xdgshellv6 diff --git a/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp b/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp new file mode 100644 index 00000000000..386713cf567 --- /dev/null +++ b/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" + +#include + +#include +#include +#include +#include + +#include + +using namespace MockCompositor; + +constexpr int xdgDecorationVersion = 1; // protocol VERSION, not the name suffix (_v1) + +class XdgDecorationManagerV1; +class XdgToplevelDecorationV1 : public QObject, public QtWaylandServer::zxdg_toplevel_decoration_v1 +{ + Q_OBJECT +public: + explicit XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager, XdgToplevel *toplevel, int id, int version) + : zxdg_toplevel_decoration_v1(toplevel->resource()->client(), id, version) + , m_manager(manager) + , m_toplevel(toplevel) + { + } + void sendConfigure(mode mode) + { + if (!m_configureSent) { + // Attaching buffers before the configure is a protocol error + QVERIFY(!m_toplevel->surface()->m_pending.buffer); + QVERIFY(!m_toplevel->surface()->m_committed.buffer); + } + send_configure(mode); + m_configureSent = true; + } + void zxdg_toplevel_decoration_v1_destroy(Resource *resource) override + { + wl_resource_destroy(resource->handle); + } + void zxdg_toplevel_decoration_v1_destroy_resource(Resource *resource) override; + void zxdg_toplevel_decoration_v1_set_mode(Resource *resource, uint32_t mode) override + { + Q_UNUSED(resource); + m_unsetModeRequested = false; + m_requestedMode = XdgToplevelDecorationV1::mode(mode); + } + void zxdg_toplevel_decoration_v1_unset_mode(Resource *resource) override + { + Q_UNUSED(resource); + m_unsetModeRequested = true; + m_requestedMode = mode(0); + } + XdgDecorationManagerV1 *m_manager = nullptr; + XdgToplevel *m_toplevel = nullptr; + mode m_requestedMode = mode(0); + bool m_unsetModeRequested = false; + bool m_configureSent = false; +}; + +class XdgDecorationManagerV1 : public Global, public QtWaylandServer::zxdg_decoration_manager_v1 +{ + Q_OBJECT +public: + explicit XdgDecorationManagerV1(CoreCompositor *compositor, int version = 1) + : QtWaylandServer::zxdg_decoration_manager_v1(compositor->m_display, version) + , m_version(version) + {} + bool isClean() override { return m_decorations.empty(); } + XdgToplevelDecorationV1 *decorationFor(XdgToplevel *toplevel) + { + return m_decorations.value(toplevel, nullptr); + } + + int m_version = 1; // TODO: Remove on libwayland upgrade + QMap m_decorations; + +protected: + void zxdg_decoration_manager_v1_destroy(Resource *resource) override + { + //TODO: Should the decorations be destroyed at this point? + wl_resource_destroy(resource->handle); + } + + void zxdg_decoration_manager_v1_get_toplevel_decoration(Resource *resource, uint32_t id, ::wl_resource *toplevelResource) override + { + auto *toplevel = fromResource(toplevelResource); + QVERIFY(toplevel); + QVERIFY(!decorationFor(toplevel)); + + // Attaching buffers before the configure is a protocol error + QVERIFY(!toplevel->surface()->m_pending.buffer); + QVERIFY(!toplevel->surface()->m_committed.buffer); + + m_decorations[toplevel] = new XdgToplevelDecorationV1(this, toplevel, id, resource->version()); + } +}; + +void XdgToplevelDecorationV1::zxdg_toplevel_decoration_v1_destroy_resource(QtWaylandServer::zxdg_toplevel_decoration_v1::Resource *resource) +{ + Q_UNUSED(resource); + int removed = m_manager->m_decorations.remove(m_toplevel); + Q_ASSERT(removed == 1); + delete this; +} + +class XdgDecorationCompositor : public DefaultCompositor { +public: + explicit XdgDecorationCompositor() + { + exec([this] { + m_config.autoConfigure = true; + add(xdgDecorationVersion); + }); + } + XdgToplevelDecorationV1 *toplevelDecoration(int i = 0) { + return get()->decorationFor(xdgToplevel(i)); + } +}; + +class tst_xdgdecorationv1 : public QObject, private XdgDecorationCompositor +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } + void clientSidePreferredByCompositor(); +}; + +void tst_xdgdecorationv1::initTestCase() +{ + if (qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_WINDOWDECORATION")) + QSKIP("This test doesn't make sense when QT_WAYLAND_DISABLE_WINDOWDECORATION is set in the environment"); +} + +void tst_xdgdecorationv1::clientSidePreferredByCompositor() +{ + QRasterWindow window; + window.show(); + QCOMPOSITOR_TRY_COMPARE(get()->resourceMap().size(), 1); + QCOMPOSITOR_TRY_COMPARE(get()->resourceMap().first()->version(), xdgDecorationVersion); + QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()); // The client creates a toplevel object + + // Check that we don't assume decorations before the server has configured them + QVERIFY(window.frameMargins().isNull()); + + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_unsetModeRequested); + QVERIFY(window.frameMargins().isNull()); // We're still waiting for a configure + exec([=] { + toplevelDecoration()->sendConfigure(XdgToplevelDecorationV1::mode_client_side); + xdgToplevel()->sendCompleteConfigure(); + }); + QTRY_VERIFY(!window.frameMargins().isNull()); +} + +QCOMPOSITOR_TEST_MAIN(tst_xdgdecorationv1) +#include "tst_xdgdecorationv1.moc" diff --git a/tests/auto/wayland/xdgdecorationv1/xdgdecorationv1.pro b/tests/auto/wayland/xdgdecorationv1/xdgdecorationv1.pro new file mode 100644 index 00000000000..0b553720582 --- /dev/null +++ b/tests/auto/wayland/xdgdecorationv1/xdgdecorationv1.pro @@ -0,0 +1,7 @@ +include (../shared/shared.pri) + +WAYLANDSERVERSOURCES += \ + $$PWD/../../../../src/3rdparty/protocol/xdg-decoration-unstable-v1.xml + +TARGET = tst_xdgdecorationv1 +SOURCES += tst_xdgdecorationv1.cpp From c357505f3a0bc1ac29ad30107075facd68dc1364 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 3 Sep 2019 07:50:26 +0200 Subject: [PATCH 14/25] Client: Adjust for window border when setting text-input cursor rect set_cursor_rectangle takes wl_surface coordinates, but we sent window coordinates. Fixes: QTBUG-77987 Change-Id: Ia0bf98f9749723128bec27c3c607d1ccde2d5fd3 Reviewed-by: Gatis Paeglis --- src/plugins/platforms/wayland/qwaylandinputcontext.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandinputcontext.cpp b/src/plugins/platforms/wayland/qwaylandinputcontext.cpp index c6f287ddaac..0f27f551d31 100644 --- a/src/plugins/platforms/wayland/qwaylandinputcontext.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputcontext.cpp @@ -119,7 +119,8 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) return; - struct ::wl_surface *surface = static_cast(QGuiApplication::focusWindow()->handle())->object(); + auto *window = static_cast(QGuiApplication::focusWindow()->handle()); + auto *surface = window->object(); if (!surface || (surface != m_surface)) return; @@ -157,8 +158,10 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla if (queries & Qt::ImCursorRectangle) { const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); - const QRect &tRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); - set_cursor_rectangle(tRect.x(), tRect.y(), tRect.width(), tRect.height()); + const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); + const QMargins margins = window->frameMargins(); + const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top()); + set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height()); } if (queries & Qt::ImPreferredLanguage) { From 183018747164f94048205093538fdd4a88c9ff33 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 3 Sep 2019 08:22:44 +0200 Subject: [PATCH 15/25] Client: Don't spam stderr about ignored window states Since it is not supported by the protocol, neither we, nor the application developer can do anything about it. Change it from warning to debug so it only shows up when enabled. Task-number: QTBUG-76061 Change-Id: I81420e0c72a9e9652f6592d65c70c7df1e5725b9 Reviewed-by: Paul Olav Tvete Reviewed-by: Pier Luigi Fiorini --- .../shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp | 2 +- .../plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp index 980e4a601a4..99b478d98ba 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp @@ -342,7 +342,7 @@ void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states) if (m_toplevel) m_toplevel->requestWindowStates(states); else - qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states"; + qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel."; } void QWaylandXdgSurfaceV6::setToplevel() diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index f55298134fe..026bb56a0e7 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -377,7 +377,7 @@ void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states) if (m_toplevel) m_toplevel->requestWindowStates(states); else - qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states"; + qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel zxdg_surface_v6."; } void QWaylandXdgSurface::setToplevel() From c3960e59979f3a71d6f1c1bef0bfcb4c0dc2153f Mon Sep 17 00:00:00 2001 From: Pavel Tumakaev Date: Wed, 31 Jul 2019 13:26:51 +0300 Subject: [PATCH 16/25] Fix deadlock in QWaylandWindow::waitForFrameSync Calling the QOpenGLContext::swapBuffers from QGuiApplicationPrivate::processExposeEvent in some cases leads to recursive calls of QWaylandWindow::waitForFrameSync. Since the mWaitingForFrameCallback check in WaylandWindow::waitForFrameSync is performed after the mutex is locked, the QMutexLocker tries to lock the mFrameSyncMutex mutex in every recursive call, that leads to a deadlock. This patch moves the performing of the mWaitingForFrameCallback check before locking the mutex. Change-Id: Ia2d834b7dd03fcd91bbe29a3a897b4db2d155527 Reviewed-by: Johan Helsing --- src/plugins/platforms/wayland/qwaylandwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index abc54f58438..95358232316 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -657,10 +657,11 @@ QMutex QWaylandWindow::mFrameSyncMutex; bool QWaylandWindow::waitForFrameSync(int timeout) { - QMutexLocker locker(&mFrameSyncMutex); if (!mWaitingForFrameCallback) return true; + QMutexLocker locker(&mFrameSyncMutex); + wl_proxy_set_queue(reinterpret_cast(mFrameCallback), mFrameQueue); mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout); From dfe1739bda0d6e8031beba15cd4600d96bd3f045 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Sat, 7 Sep 2019 11:41:49 +0200 Subject: [PATCH 17/25] Client: Disable key repeating when rate is set to 0 From the docs of repeat_info: "A rate of zero will disable any repeating (regardless of the value of delay)." Avoiding starting the initial timer effectively disables everything Change-Id: I7489f61b2bc0e000efddb4255f1968072eeff2b8 Reviewed-by: Johan Helsing --- src/plugins/platforms/wayland/qwaylandinputdevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index b5c18a07481..9ceb222d9d9 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -896,7 +896,7 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease; handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code)) { + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code) && mRepeatRate > 0) { mRepeatKey.key = qtkey; mRepeatKey.code = code; mRepeatKey.time = time; From 3b34e5f86d4c5a56ac1411c86fb0129d46d74464 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 14 Jun 2019 08:45:29 +0200 Subject: [PATCH 18/25] Client: Add test for starting a drag operation without input focus This used to crash the client. Task-number: QTBUG-76368 Change-Id: I855f3bda15b4b2bccbdb2aa8239e26c0eecf7cb3 Reviewed-by: Paul Olav Tvete --- .../wayland/datadevicev1/tst_datadevicev1.cpp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp b/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp index fe68d520d59..7368829d13b 100644 --- a/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp +++ b/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp @@ -30,9 +30,8 @@ #include #include - -//TODO: move? #include +#include using namespace MockCompositor; @@ -60,6 +59,7 @@ private slots: void pasteUtf8(); void destroysPreviousSelection(); void destroysSelectionWithSurface(); + void dragWithoutFocus(); }; void tst_datadevicev1::initTestCase() @@ -209,5 +209,22 @@ void tst_datadevicev1::destroysSelectionWithSurface() QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0); } +// The application should not crash if it attempts to start a drag operation +// when it doesn't have input focus (QTBUG-76368) +void tst_datadevicev1::dragWithoutFocus() +{ + QRasterWindow window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + auto *mimeData = new QMimeData; + const QByteArray data("testData"); + mimeData->setData("text/plain", data); + QDrag drag(&window); + drag.setMimeData(mimeData); + drag.exec(); +} + QCOMPOSITOR_TEST_MAIN(tst_datadevicev1) #include "tst_datadevicev1.moc" From c9994d46fe1fca08a66a70893ca0c7d53a90ab6c Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 4 Sep 2019 11:47:13 +0200 Subject: [PATCH 19/25] Client: Fix touch rounding errors Touch now accounts for fractional input in the same way as for pointer input. Task-number: QTBUG-77457 Change-Id: I18e633bf7c7033187a641f757b8b24f52479971a Reviewed-by: Paul Olav Tvete --- .../platforms/wayland/qwaylandinputdevice.cpp | 18 ++++++++++++------ .../platforms/wayland/qwaylandinputdevice_p.h | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 731285eab81..8f3df8e4dda 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -987,14 +987,15 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial, mParent->mSerial = serial; mFocus = window; mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus); - mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed); + QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y)); + mParent->handleTouchPoint(id, Qt::TouchPointPressed, position); } void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id) { Q_UNUSED(serial); Q_UNUSED(time); - mParent->handleTouchPoint(id, 0, 0, Qt::TouchPointReleased); + mParent->handleTouchPoint(id, Qt::TouchPointReleased); if (allTouchPointsReleased()) { mFocus = nullptr; @@ -1013,7 +1014,8 @@ void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_ void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(time); - mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointMoved); + QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y)); + mParent->handleTouchPoint(id, Qt::TouchPointMoved, position); } void QWaylandInputDevice::Touch::touch_cancel() @@ -1027,7 +1029,7 @@ void QWaylandInputDevice::Touch::touch_cancel() QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); } -void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::TouchPointState state) +void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition) { auto end = mTouch->mPendingTouchPoints.end(); auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](auto tp){ return tp.id == id; }); @@ -1039,7 +1041,6 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch // Only moved and pressed needs to update/set position if (state == Qt::TouchPointMoved || state == Qt::TouchPointPressed) { - // x and y are surface relative. // We need a global (screen) position. QWaylandWindow *win = mTouch->mFocus; @@ -1053,7 +1054,12 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch tp.area = QRectF(0, 0, 8, 8); QMargins margins = win->frameMargins(); - tp.area.moveCenter(win->window()->mapToGlobal(QPoint(x - margins.left(), y - margins.top()))); + QPointF localPosition = surfacePosition - QPointF(margins.left(), margins.top()); + // TODO: This doesn't account for high dpi scaling for the delta, but at least it matches + // what we have for mouse input. + QPointF delta = localPosition - localPosition.toPoint(); + QPointF globalPosition = win->window()->mapToGlobal(localPosition.toPoint()) + delta; + tp.area.moveCenter(globalPosition); } tp.state = state; diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h index b0b02216196..143e1122099 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h +++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h @@ -167,7 +167,7 @@ private: uint32_t mSerial = 0; void seat_capabilities(uint32_t caps) override; - void handleTouchPoint(int id, double x, double y, Qt::TouchPointState state); + void handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition = QPoint()); QTouchDevice *mTouchDevice = nullptr; From fea2383cf191763e74a2c79d0fb14c3cdda20091 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 4 Sep 2019 10:57:42 +0200 Subject: [PATCH 20/25] Add client test for floating point mouse press Change-Id: Ia7cfb1bc86945e08a2ff2c794afb405110e819f9 Reviewed-by: Paul Olav Tvete --- tests/auto/wayland/seatv4/tst_seatv4.cpp | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/auto/wayland/seatv4/tst_seatv4.cpp b/tests/auto/wayland/seatv4/tst_seatv4.cpp index 7dc2e727a6a..1d6fb6b9c36 100644 --- a/tests/auto/wayland/seatv4/tst_seatv4.cpp +++ b/tests/auto/wayland/seatv4/tst_seatv4.cpp @@ -72,6 +72,7 @@ private slots: void usesEnterSerial(); void focusDestruction(); void mousePress(); + void mousePressFloat(); void simpleAxis_data(); void simpleAxis(); void invalidPointerEvents(); @@ -207,6 +208,30 @@ void tst_seatv4::mousePress() QTRY_VERIFY(window.m_pressed); } +void tst_seatv4::mousePressFloat() +{ + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *e) override { m_position = e->localPos(); } + QPointF m_position; + }; + + Window window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([&] { + auto *surface = xdgSurface()->m_surface; + pointer()->sendEnter(surface, {32.75, 32.25}); + pointer()->sendButton(client(), BTN_LEFT, 1); + pointer()->sendButton(client(), BTN_LEFT, 0); + }); + QMargins m = window.frameMargins(); + QPointF pressedPosition(32.75 -m.left(), 32.25 - m.top()); + QTRY_COMPARE(window.m_position, pressedPosition); +} + void tst_seatv4::simpleAxis_data() { QTest::addColumn("axis"); From fe5f437f31745fc83555b5b5fb95181c0e2526f9 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 11 Sep 2019 13:49:09 +0200 Subject: [PATCH 21/25] If egl_x11 is not available then don't turn on xcomposite-egl Since xcomposite-egl depends on the egl_x11 feature in qtbase, then this should be explicit. Change-Id: Ic1dce9526fb50f6f56e18abd58b69bcaed6d204e Reviewed-by: Paul Olav Tvete --- src/plugins/platforms/wayland/configure.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/wayland/configure.json b/src/plugins/platforms/wayland/configure.json index 7feeee4bfc2..91024c9d377 100644 --- a/src/plugins/platforms/wayland/configure.json +++ b/src/plugins/platforms/wayland/configure.json @@ -135,7 +135,7 @@ }, "xcomposite-egl": { "label": "XComposite EGL", - "condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite", + "condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite && features.egl_x11", "output": [ "privateFeature" ] }, "xcomposite-glx": { From 527a890cd3db3e1b313e83d094e54ab36089d5f5 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 4 Sep 2019 11:13:46 +0200 Subject: [PATCH 22/25] Add client test for floating point touch Task-number: QTBUG-77457 Change-Id: I49df80f3e00d349fed273363cd2625b841c085c5 Reviewed-by: Paul Olav Tvete --- tests/auto/wayland/seatv5/tst_seatv5.cpp | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/auto/wayland/seatv5/tst_seatv5.cpp b/tests/auto/wayland/seatv5/tst_seatv5.cpp index ca8de31acb1..ae7c3db2f90 100644 --- a/tests/auto/wayland/seatv5/tst_seatv5.cpp +++ b/tests/auto/wayland/seatv5/tst_seatv5.cpp @@ -68,6 +68,7 @@ private slots: // Touch tests void createsTouch(); void singleTap(); + void singleTapFloat(); }; void tst_seatv5::bindsToSeat() @@ -431,5 +432,36 @@ void tst_seatv5::singleTap() } } +void tst_seatv5::singleTapFloat() +{ + TouchWindow window; + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { + auto *t = touch(); + auto *c = client(); + t->sendDown(xdgToplevel()->surface(), {32.75, 32.25}, 1); + t->sendFrame(c); + t->sendUp(c, 1); + t->sendFrame(c); + }); + + QTRY_VERIFY(!window.m_events.empty()); + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchBegin); + QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed); + QCOMPARE(e.touchPoints.length(), 1); + QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top())); + } + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchEnd); + QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased); + QCOMPARE(e.touchPoints.length(), 1); + QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top())); + } +} + QCOMPOSITOR_TEST_MAIN(tst_seatv5) #include "tst_seatv5.moc" From 00a2ff93f6c649983857194190aee36ba8f4f309 Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Wed, 31 Jul 2019 07:58:21 +0200 Subject: [PATCH 23/25] Use QGenericUnixTheme to provide themes This makes wayland platform plugin to use same platform themes as xcb plugin, which also can load platform theme plugin for apps running in Gnome. Change-Id: I9f4e3504d153e26c5580d9e33443d8a11201cd52 Reviewed-by: Johan Helsing --- .../platforms/wayland/qwaylandintegration.cpp | 36 ++----------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index f6a80e18fde..fbbcedbee91 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -98,38 +98,6 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -class GenericWaylandTheme: public QGenericUnixTheme -{ -public: - static QStringList themeNames() - { - QStringList result; - - if (QGuiApplication::desktopSettingsAware()) { - const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment(); - - if (desktopEnvironment == QByteArrayLiteral("KDE")) { -#if QT_CONFIG(settings) - result.push_back(QStringLiteral("kde")); -#endif - } else if (!desktopEnvironment.isEmpty() && - desktopEnvironment != QByteArrayLiteral("UNKNOWN") && - desktopEnvironment != QByteArrayLiteral("GNOME") && - desktopEnvironment != QByteArrayLiteral("UNITY") && - desktopEnvironment != QByteArrayLiteral("MATE") && - desktopEnvironment != QByteArrayLiteral("XFCE") && - desktopEnvironment != QByteArrayLiteral("LXDE")) - // Ignore X11 desktop environments - result.push_back(QString::fromLocal8Bit(desktopEnvironment.toLower())); - } - - if (result.isEmpty()) - result.push_back(QLatin1String(QGenericUnixTheme::name)); - - return result; - } -}; - QWaylandIntegration::QWaylandIntegration() #if defined(Q_OS_MACOS) : mFontDb(new QCoreTextFontDatabaseEngineFactory) @@ -302,12 +270,12 @@ QList QWaylandIntegration::possibleKeys(const QKeyEvent *event) const QStringList QWaylandIntegration::themeNames() const { - return GenericWaylandTheme::themeNames(); + return QGenericUnixTheme::themeNames(); } QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) const { - return GenericWaylandTheme::createUnixTheme(name); + return QGenericUnixTheme::createUnixTheme(name); } // May be called from non-GUI threads From 9b228df7850c7c46cc91945ab2849d8dc97aa0c3 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 18 Sep 2019 09:19:39 +0200 Subject: [PATCH 24/25] Convert most config tests to configure.json inline format And remove the now unused entries in config.tests. Now only the wayland-scanner test remains, as that uses qmake features that do not easily translate to the inline format. This makes it much easier to port QtWayland to cmake. This also removes some config.tests that weren't used. Task-number: QTBUG-78177 Change-Id: Ia21a0025f9fac45b9f4815ee0f196e9e5759cdc6 Reviewed-by: Paul Olav Tvete --- src/plugins/platforms/wayland/configure.json | 59 ++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/plugins/platforms/wayland/configure.json b/src/plugins/platforms/wayland/configure.json index e9e16324b60..a25b61f5517 100644 --- a/src/plugins/platforms/wayland/configure.json +++ b/src/plugins/platforms/wayland/configure.json @@ -61,25 +61,76 @@ "drm-egl-server": { "label": "DRM EGL Server", "type": "compile", - "test": "drm_egl_server", + "test": { + "include": [ + "EGL/egl.h", + "EGL/eglext.h" + ], + "main": [ + "#ifdef EGL_MESA_drm_image", + "return 0;", + "#else", + "#error Requires EGL_MESA_drm_image to be defined", + "return 1;", + "#endif" + ] + }, "use": "egl" }, "libhybris-egl-server": { "label": "libhybris EGL Server", "type": "compile", - "test": "libhybris_egl_server", + "test": { + "include": [ + "EGL/egl.h", + "EGL/eglext.h", + "hybris/eglplatformcommon/hybris_nativebufferext.h" + ], + "main": [ + "#ifdef EGL_HYBRIS_native_buffer", + "return 0;", + "#else", + "#error Requires EGL_HYBRIS_native_buffer to be defined", + "return 1;", + "#endif" + ] + }, "use": "egl" }, "dmabuf-server-buffer": { "label": "Linux dma-buf Buffer Sharing", "type": "compile", - "test": "dmabuf_server_buffer", + "test": { + "include": [ + "EGL/egl.h", + "EGL/eglext.h", + "drm_fourcc.h" + ], + "main": [ + "#ifdef EGL_LINUX_DMA_BUF_EXT", + "return 0;", + "#else", + "#error Requires EGL_LINUX_DMA_BUF_EXT", + "return 1;", + "#endif" + ] + }, "use": "egl" }, "vulkan-server-buffer": { "label": "Vulkan Buffer Sharing", "type": "compile", - "test": "vulkan_server_buffer" + "test": { + "include": [ + "vulkan/vulkan.h" + ], + "main": [ + "VkExportMemoryAllocateInfoKHR exportAllocInfo = {};", + "exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;", + "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", + "return 0;" + ] + } } }, From 79196d387754f2b270ca848c0eb685dec278c85c Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 20 Sep 2019 15:55:29 +0200 Subject: [PATCH 25/25] Use new ifdef style for accessibility_atspi_bridge Change-Id: I9537017198ef3be862b22b2ed893f9438dc92b65 Reviewed-by: Paul Olav Tvete --- src/plugins/platforms/wayland/qwaylandintegration.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index fbbcedbee91..ce72c334068 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -86,7 +86,7 @@ #include "qwaylandinputdeviceintegration_p.h" #include "qwaylandinputdeviceintegrationfactory_p.h" -#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE +#if QT_CONFIG(accessibility_atspi_bridge) #include #endif @@ -239,7 +239,7 @@ QVariant QWaylandIntegration::styleHint(StyleHint hint) const QPlatformAccessibility *QWaylandIntegration::accessibility() const { if (!mAccessibility) { -#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE +#if QT_CONFIG(accessibility_atspi_bridge) Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QWaylandIntegration", "Initializing accessibility without event-dispatcher!"); mAccessibility.reset(new QSpiAccessibleBridge());