From b4ee20efff96534895060dab6a3e7ced5d41f9c2 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Sun, 23 Jun 2019 14:48:30 +0200 Subject: [PATCH 01/13] 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 59486fdf3ba485524a16578a5b76051f2f8c0026 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 21 Aug 2019 13:44:05 +0200 Subject: [PATCH 02/13] 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 03/13] 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 47340e8c6f4b441f11ac6ec9496b64bc956d03be Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 24 May 2019 14:20:18 +0200 Subject: [PATCH 04/13] 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 05/13] 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 06/13] 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 07/13] 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 08/13] 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 09/13] 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 10/13] 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 11/13] 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 12/13] 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 13/13] 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"