diff --git a/src/3rdparty/wayland/protocols/idle-inhibit-unstable-v1.xml b/src/3rdparty/wayland/protocols/idle-inhibit-unstable-v1.xml new file mode 100644 index 00000000000..9c06cdcba6c --- /dev/null +++ b/src/3rdparty/wayland/protocols/idle-inhibit-unstable-v1.xml @@ -0,0 +1,83 @@ + + + + + Copyright © 2015 Samsung Electronics Co., Ltd + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This interface permits inhibiting the idle behavior such as screen + blanking, locking, and screensaving. The client binds the idle manager + globally, then creates idle-inhibitor objects for each surface. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + Destroy the inhibit manager. + + + + + + Create a new inhibitor object associated with the given surface. + + + + + + + + + + An idle inhibitor prevents the output that the associated surface is + visible on from being set to a state where it is not visually usable due + to lack of user interaction (e.g. blanked, dimmed, locked, set to power + save, etc.) Any screensaver processes are also blocked from displaying. + + If the surface is destroyed, unmapped, becomes occluded, loses + visibility, or otherwise becomes not visually relevant for the user, the + idle inhibitor will not be honored by the compositor; if the surface + subsequently regains visibility the inhibitor takes effect once again. + Likewise, the inhibitor isn't honored if the system was already idled at + the time the inhibitor was established, although if the system later + de-idles and re-idles the inhibitor will take effect. + + + + + Remove the inhibitor effect from the associated wl_surface. + + + + + diff --git a/src/3rdparty/wayland/protocols/qt_attribution.json b/src/3rdparty/wayland/protocols/qt_attribution.json index e6f90698b89..c49ead4f33d 100644 --- a/src/3rdparty/wayland/protocols/qt_attribution.json +++ b/src/3rdparty/wayland/protocols/qt_attribution.json @@ -127,8 +127,9 @@ Copyright (c) 2013 BMW Car IT GmbH" "Id": "wayland-xdg-output-protocol", "Name": "Wayland XDG Output Protocol", "QDocModule": "qtwaylandcompositor", - "QtUsage": "Used in the Qt Wayland platform plugin.", + "QtUsage": "Used in the Qt Wayland Compositor API, and the Qt Wayland platform plugin.", "Files": "xdg-output-unstable-v1.xml", + "Description": "The XDG Output protocol is an extended way to describe output regions under Wayland", "Homepage": "https://wayland.freedesktop.org", "Version": "unstable v1, version 2", diff --git a/src/plugins/platforms/wayland/client.pro b/src/plugins/platforms/wayland/client.pro index 4f4c58328d6..d0ae9009e94 100644 --- a/src/plugins/platforms/wayland/client.pro +++ b/src/plugins/platforms/wayland/client.pro @@ -20,7 +20,7 @@ qtConfig(xkbcommon) { } qtHaveModule(linuxaccessibility_support_private): \ - QT += linuxaccessibility_support_private + QT_PRIVATE += linuxaccessibility_support_private QMAKE_USE += wayland-client diff --git a/src/plugins/platforms/wayland/qwaylandcursor.cpp b/src/plugins/platforms/wayland/qwaylandcursor.cpp index 165df776295..4356b23a0a8 100644 --- a/src/plugins/platforms/wayland/qwaylandcursor.cpp +++ b/src/plugins/platforms/wayland/qwaylandcursor.cpp @@ -48,6 +48,8 @@ #include +#include + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -75,7 +77,10 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) if (struct wl_cursor *cursor = m_cursors.value(shape, nullptr)) return cursor; - static const QMultiMapcursorNamesMap { + static Q_CONSTEXPR struct ShapeAndName { + WaylandCursor shape; + const char name[33]; + } cursorNamesMap[] = { {ArrowCursor, "left_ptr"}, {ArrowCursor, "default"}, {ArrowCursor, "top_left_arrow"}, @@ -193,9 +198,14 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) {ResizeSouthWestCursor, "bottom_left_corner"}, }; - QList cursorNames = cursorNamesMap.values(shape); - for (auto &name : qAsConst(cursorNames)) { - if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, name.constData())) { + const auto byShape = [](ShapeAndName lhs, ShapeAndName rhs) { + return lhs.shape < rhs.shape; + }; + Q_ASSERT(std::is_sorted(std::begin(cursorNamesMap), std::end(cursorNamesMap), byShape)); + const auto p = std::equal_range(std::begin(cursorNamesMap), std::end(cursorNamesMap), + ShapeAndName{shape, ""}, byShape); + for (auto it = p.first; it != p.second; ++it) { + if (wl_cursor *cursor = wl_cursor_theme_get_cursor(m_theme, it->name)) { m_cursors.insert(shape, cursor); return cursor; } diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp index 19e36ccba63..8aa0239d079 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -442,6 +442,21 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice return new Touch(device); } +QWaylandInputDevice::Keyboard *QWaylandInputDevice::keyboard() const +{ + return mKeyboard; +} + +QWaylandInputDevice::Pointer *QWaylandInputDevice::pointer() const +{ + return mPointer; +} + +QWaylandInputDevice::Touch *QWaylandInputDevice::touch() const +{ + return mTouch; +} + void QWaylandInputDevice::handleEndDrag() { if (mTouch) @@ -602,7 +617,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf invalidateFocus(); } mFocus = window->waylandSurface(); - connect(mFocus, &QObject::destroyed, this, &Pointer::handleFocusDestroyed); + connect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed); mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy)); mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint()); @@ -774,8 +789,10 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time void QWaylandInputDevice::Pointer::invalidateFocus() { - disconnect(mFocus, &QObject::destroyed, this, &Pointer::handleFocusDestroyed); - mFocus = nullptr; + if (mFocus) { + disconnect(mFocus.data(), &QObject::destroyed, this, &Pointer::handleFocusDestroyed); + mFocus = nullptr; + } mEnterSerial = 0; } diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h index 06ba5d5664e..4ac1dca3525 100644 --- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h +++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h @@ -148,6 +148,10 @@ public: virtual Pointer *createPointer(QWaylandInputDevice *device); virtual Touch *createTouch(QWaylandInputDevice *device); + Keyboard *keyboard() const; + Pointer *pointer() const; + Touch *touch() const; + private: QWaylandDisplay *mQDisplay = nullptr; struct wl_display *mDisplay = nullptr; @@ -248,6 +252,8 @@ public: Qt::KeyboardModifiers modifiers() const; + struct ::wl_keyboard *wl_keyboard() { return QtWayland::wl_keyboard::object(); } + private slots: void handleFocusDestroyed(); void handleFocusLost(); @@ -284,6 +290,8 @@ public: #endif QWaylandInputDevice *seat() const { return mParent; } + struct ::wl_pointer *wl_pointer() { return QtWayland::wl_pointer::object(); } + protected: void pointer_enter(uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) override; @@ -377,6 +385,8 @@ public: bool allTouchPointsReleased(); void releasePoints(); + struct ::wl_touch *wl_touch() { return QtWayland::wl_touch::object(); } + QWaylandInputDevice *mParent = nullptr; QPointer mFocus; QList mTouchPoints; diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 078e5be196d..7f79d6b9863 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -272,7 +272,7 @@ QPlatformAccessibility *QWaylandIntegration::accessibility() const { if (!mAccessibility) { #ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE - Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration", + Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QWaylandIntegration", "Initializing accessibility without event-dispatcher!"); mAccessibility.reset(new QSpiAccessibleBridge()); #else diff --git a/src/plugins/platforms/wayland/qwaylandnativeinterface.cpp b/src/plugins/platforms/wayland/qwaylandnativeinterface.cpp index cf227d489a5..b4ecc009098 100644 --- a/src/plugins/platforms/wayland/qwaylandnativeinterface.cpp +++ b/src/plugins/platforms/wayland/qwaylandnativeinterface.cpp @@ -47,6 +47,7 @@ #include "qwaylanddisplay_p.h" #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandscreen_p.h" +#include "qwaylandinputdevice_p.h" #include #include #include @@ -76,6 +77,27 @@ void *QWaylandNativeInterface::nativeResourceForIntegration(const QByteArray &re if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration()) return m_integration->clientBufferIntegration()->nativeResource(QWaylandClientBufferIntegration::EglDisplay); + if (lowerCaseResource == "wl_seat") + return m_integration->display()->defaultInputDevice()->wl_seat(); + if (lowerCaseResource == "wl_keyboard") { + auto *keyboard = m_integration->display()->defaultInputDevice()->keyboard(); + if (keyboard) + return keyboard->wl_keyboard(); + return nullptr; + } + if (lowerCaseResource == "wl_pointer") { + auto *pointer = m_integration->display()->defaultInputDevice()->pointer(); + if (pointer) + return pointer->wl_pointer(); + return nullptr; + } + if (lowerCaseResource == "wl_touch") { + auto *touch = m_integration->display()->defaultInputDevice()->touch(); + if (touch) + return touch->wl_touch(); + return nullptr; + } + return nullptr; } diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp index d768e7fc236..9b5971a21e9 100644 --- a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp +++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp @@ -249,7 +249,7 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) if (b->size() == size) { return b; } else { - mBuffers.removeOne(b); + mBuffers.remove(b); if (mBackBuffer == b) mBackBuffer = nullptr; delete b; @@ -257,11 +257,11 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) } } - static const int MAX_BUFFERS = 5; - if (mBuffers.count() < MAX_BUFFERS) { + static const size_t MAX_BUFFERS = 5; + if (mBuffers.size() < MAX_BUFFERS) { QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format(); QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale()); - mBuffers.prepend(b); + mBuffers.push_front(b); return b; } return nullptr; @@ -300,9 +300,9 @@ void QWaylandShmBackingStore::resize(const QSize &size) // ensure the new buffer is at the beginning of the list so next time getBuffer() will pick // it if possible - if (mBuffers.first() != buffer) { - mBuffers.removeOne(buffer); - mBuffers.prepend(buffer); + if (mBuffers.front() != buffer) { + mBuffers.remove(buffer); + mBuffers.push_front(buffer); } if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes) diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h index 88ecfc5ece1..8a85cd7f34a 100644 --- a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h +++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h @@ -57,7 +57,8 @@ #include #include #include -#include + +#include QT_BEGIN_NAMESPACE @@ -116,7 +117,7 @@ private: QWaylandShmBuffer *getBuffer(const QSize &size); QWaylandDisplay *mDisplay = nullptr; - QLinkedList mBuffers; + std::list mBuffers; QWaylandShmBuffer *mFrontBuffer = nullptr; QWaylandShmBuffer *mBackBuffer = nullptr; bool mPainting = false; diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 9a58ec39a97..ce58003cc67 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -249,6 +249,13 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mFrameCallback = nullptr; } + int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); + if (timerId != -1) { + killTimer(timerId); + } + mWaitingForFrameCallback = false; + mFrameCallbackTimedOut = false; + mMask = QRegion(); mQueuedBuffer = nullptr; } @@ -343,7 +350,7 @@ void QWaylandWindow::setGeometry(const QRect &rect) mSentInitialResize = true; } QRect exposeGeometry(QPoint(), geometry().size()); - if (exposeGeometry != mLastExposeGeometry) + if (isExposed() && !mInResizeFromApplyConfigure && exposeGeometry != mLastExposeGeometry) sendExposeEvent(exposeGeometry); if (mShellSurface) @@ -358,7 +365,9 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins)); mOffset += offset; + mInResizeFromApplyConfigure = true; setGeometry(geometry); + mInResizeFromApplyConfigure = false; } void QWaylandWindow::sendExposeEvent(const QRect &rect) @@ -573,29 +582,34 @@ const wl_callback_listener QWaylandWindow::callbackListener = { Q_UNUSED(callback); Q_UNUSED(time); auto *window = static_cast(data); - if (window->thread() != QThread::currentThread()) - QMetaObject::invokeMethod(window, [=] { window->handleFrameCallback(); }, Qt::QueuedConnection); - else - window->handleFrameCallback(); + window->handleFrameCallback(); } }; void QWaylandWindow::handleFrameCallback() { - bool wasExposed = isExposed(); - - if (mFrameCallbackTimerId != -1) { - killTimer(mFrameCallbackTimerId); - mFrameCallbackTimerId = -1; - } - + // Stop the timer and stop waiting immediately + int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); mWaitingForFrameCallback = false; - mFrameCallbackTimedOut = false; - if (!wasExposed && isExposed()) - sendExposeEvent(QRect(QPoint(), geometry().size())); - if (wasExposed && hasPendingUpdateRequest()) - deliverUpdateRequest(); + // The rest can wait until we can run it on the correct thread + auto doHandleExpose = [this, timerId]() { + if (timerId != -1) + killTimer(timerId); + + bool wasExposed = isExposed(); + mFrameCallbackTimedOut = false; + if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? + sendExposeEvent(QRect(QPoint(), geometry().size())); + if (wasExposed && hasPendingUpdateRequest()) + deliverUpdateRequest(); + }; + + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, doHandleExpose); + } else { + doHandleExpose(); + } } QMutex QWaylandWindow::mFrameSyncMutex; @@ -617,11 +631,11 @@ bool QWaylandWindow::waitForFrameSync(int timeout) } // Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread - if (mFrameCallbackTimerId != -1) { - int id = mFrameCallbackTimerId; - mFrameCallbackTimerId = -1; - QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection); - } + // Ordered semantics is needed to avoid stopping the timer twice and not miss it when it's + // started by other writes + int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); + if (fcbId != -1) + QMetaObject::invokeMethod(this, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection); return !mWaitingForFrameCallback; } @@ -1077,9 +1091,9 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) } } - if (event->timerId() == mFrameCallbackTimerId) { - killTimer(mFrameCallbackTimerId); - mFrameCallbackTimerId = -1; + + if (mFrameCallbackTimerId.testAndSetOrdered(event->timerId(), -1)) { + killTimer(event->timerId()); qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; mFrameCallbackTimedOut = true; mWaitingForUpdate = false; @@ -1132,7 +1146,7 @@ void QWaylandWindow::handleUpdate() // ignore it if it times out before it's cleaned up by the invokeMethod call. int id = mFallbackUpdateTimerId; mFallbackUpdateTimerId = -1; - QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, [this, id] { killTimer(id); }, Qt::QueuedConnection); } mFrameCallback = mSurface->frame(); @@ -1141,14 +1155,12 @@ void QWaylandWindow::handleUpdate() mWaitingForUpdate = false; // Stop current frame timer if any, can't use killTimer directly, see comment above. - if (mFrameCallbackTimerId != -1) { - int id = mFrameCallbackTimerId; - mFrameCallbackTimerId = -1; - QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection); - } + int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); + if (fcbId != -1) + QMetaObject::invokeMethod(this, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection); // Start a timer for handling the case when the compositor stops sending frame callbacks. - QMetaObject::invokeMethod(this, [=] { // Again; can't do it directly + QMetaObject::invokeMethod(this, [this] { // Again; can't do it directly if (mWaitingForFrameCallback) mFrameCallbackTimerId = startTimer(100); }, Qt::QueuedConnection); diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 5eadc02c8cc..8d8565d7761 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -219,7 +219,7 @@ protected: WId mWindowId; bool mWaitingForFrameCallback = false; bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out - int mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback + QAtomicInt mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback struct ::wl_callback *mFrameCallback = nullptr; struct ::wl_event_queue *mFrameQueue = nullptr; QWaitCondition mFrameSyncWait; @@ -264,6 +264,7 @@ private: void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleScreensChanged(); + bool mInResizeFromApplyConfigure = false; QRect mLastExposeGeometry; static const wl_callback_listener callbackListener; diff --git a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp index 02bb5701cda..9691b857f27 100644 --- a/src/tools/qtwaylandscanner/qtwaylandscanner.cpp +++ b/src/tools/qtwaylandscanner/qtwaylandscanner.cpp @@ -40,7 +40,8 @@ #include #include #include -#include + +#include class Scanner { @@ -63,7 +64,7 @@ private: struct WaylandEnum { QByteArray name; - QList entries; + std::vector entries; }; struct WaylandArgument { @@ -78,16 +79,16 @@ private: bool request; QByteArray name; QByteArray type; - QList arguments; + std::vector arguments; }; struct WaylandInterface { QByteArray name; int version; - QList enums; - QList events; - QList requests; + std::vector enums; + std::vector events; + std::vector requests; }; bool isServerSide(); @@ -101,11 +102,11 @@ private: Scanner::WaylandInterface readInterface(QXmlStreamReader &xml); QByteArray waylandToCType(const QByteArray &waylandType, const QByteArray &interface); QByteArray waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray); - const Scanner::WaylandArgument *newIdArgument(const QList &arguments); + const Scanner::WaylandArgument *newIdArgument(const std::vector &arguments); void printEvent(const WaylandEvent &e, bool omitNames = false, bool withResource = false); void printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent = true); - void printEnums(const QList &enums); + void printEnums(const std::vector &enums); QByteArray stripInterfaceName(const QByteArray &name); bool ignoreInterface(const QByteArray &name); @@ -189,19 +190,22 @@ bool Scanner::boolValue(const QXmlStreamReader &xml, const char *name) Scanner::WaylandEvent Scanner::readEvent(QXmlStreamReader &xml, bool request) { - WaylandEvent event; - event.request = request; - event.name = byteArrayValue(xml, "name"); - event.type = byteArrayValue(xml, "type"); + WaylandEvent event = { + .request = request, + .name = byteArrayValue(xml, "name"), + .type = byteArrayValue(xml, "type"), + .arguments = {}, + }; while (xml.readNextStartElement()) { if (xml.name() == "arg") { - WaylandArgument argument; - argument.name = byteArrayValue(xml, "name"); - argument.type = byteArrayValue(xml, "type"); - argument.interface = byteArrayValue(xml, "interface"); - argument.summary = byteArrayValue(xml, "summary"); - argument.allowNull = boolValue(xml, "allowNull"); - event.arguments << argument; + WaylandArgument argument = { + .name = byteArrayValue(xml, "name"), + .type = byteArrayValue(xml, "type"), + .interface = byteArrayValue(xml, "interface"), + .summary = byteArrayValue(xml, "summary"), + .allowNull = boolValue(xml, "allowNull"), + }; + event.arguments.push_back(std::move(argument)); } xml.skipCurrentElement(); @@ -211,16 +215,19 @@ Scanner::WaylandEvent Scanner::readEvent(QXmlStreamReader &xml, bool request) Scanner::WaylandEnum Scanner::readEnum(QXmlStreamReader &xml) { - WaylandEnum result; - result.name = byteArrayValue(xml, "name"); + WaylandEnum result = { + .name = byteArrayValue(xml, "name"), + .entries = {}, + }; while (xml.readNextStartElement()) { if (xml.name() == "entry") { - WaylandEnumEntry entry; - entry.name = byteArrayValue(xml, "name"); - entry.value = byteArrayValue(xml, "value"); - entry.summary = byteArrayValue(xml, "summary"); - result.entries << entry; + WaylandEnumEntry entry = { + .name = byteArrayValue(xml, "name"), + .value = byteArrayValue(xml, "value"), + .summary = byteArrayValue(xml, "summary"), + }; + result.entries.push_back(std::move(entry)); } xml.skipCurrentElement(); @@ -231,17 +238,21 @@ Scanner::WaylandEnum Scanner::readEnum(QXmlStreamReader &xml) Scanner::WaylandInterface Scanner::readInterface(QXmlStreamReader &xml) { - WaylandInterface interface; - interface.name = byteArrayValue(xml, "name"); - interface.version = intValue(xml, "version", 1); + WaylandInterface interface = { + .name = byteArrayValue(xml, "name"), + .version = intValue(xml, "version", 1), + .enums = {}, + .events = {}, + .requests = {}, + }; while (xml.readNextStartElement()) { if (xml.name() == "event") - interface.events << readEvent(xml, false); + interface.events.push_back(readEvent(xml, false)); else if (xml.name() == "request") - interface.requests << readEvent(xml, true); + interface.requests.push_back(readEvent(xml, true)); else if (xml.name() == "enum") - interface.enums << readEnum(xml); + interface.enums.push_back(readEnum(xml)); else xml.skipCurrentElement(); } @@ -283,11 +294,11 @@ QByteArray Scanner::waylandToQtType(const QByteArray &waylandType, const QByteAr return waylandToCType(waylandType, interface); } -const Scanner::WaylandArgument *Scanner::newIdArgument(const QList &arguments) +const Scanner::WaylandArgument *Scanner::newIdArgument(const std::vector &arguments) { - for (int i = 0; i < arguments.size(); ++i) { - if (arguments.at(i).type == "new_id") - return &arguments.at(i); + for (const WaylandArgument &a : arguments) { + if (a.type == "new_id") + return &a; } return nullptr; } @@ -305,8 +316,7 @@ void Scanner::printEvent(const WaylandEvent &e, bool omitNames, bool withResourc needsComma = true; } } - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { bool isNewId = a.type == "new_id"; if (isNewId && !isServerSide() && (a.interface.isEmpty() != e.request)) continue; @@ -346,9 +356,8 @@ void Scanner::printEventHandlerSignature(const WaylandEvent &e, const char *inte printf(" %svoid *data,\n", indent); printf(" %sstruct ::%s *object", indent, interfaceName); } - for (int i = 0; i < e.arguments.size(); ++i) { + for (const WaylandArgument &a : e.arguments) { printf(",\n"); - const WaylandArgument &a = e.arguments.at(i); bool isNewId = a.type == "new_id"; if (isServerSide() && isNewId) { printf(" %suint32_t %s", indent, a.name.constData()); @@ -360,17 +369,13 @@ void Scanner::printEventHandlerSignature(const WaylandEvent &e, const char *inte printf(")"); } -void Scanner::printEnums(const QList &enums) +void Scanner::printEnums(const std::vector &enums) { - for (int i = 0; i < enums.size(); ++i) { + for (const WaylandEnum &e : enums) { printf("\n"); - const WaylandEnum &e = enums.at(i); printf(" enum %s {\n", e.name.constData()); - for (int i = 0; i < e.entries.size(); ++i) { - const WaylandEnumEntry &entry = e.entries.at(i); - printf(" %s_%s = %s", e.name.constData(), entry.name.constData(), entry.value.constData()); - if (i < e.entries.size() - 1) - printf(","); + for (const WaylandEnumEntry &entry : e.entries) { + printf(" %s_%s = %s,", e.name.constData(), entry.name.constData(), entry.value.constData()); if (!entry.summary.isNull()) printf(" // %s", entry.summary.constData()); printf("\n"); @@ -424,11 +429,11 @@ bool Scanner::process() //QByteArray preProcessorProtocolName = QByteArray(m_protocolName).replace('-', '_').toUpper(); QByteArray preProcessorProtocolName = QByteArray(m_protocolName).toUpper(); - QList interfaces; + std::vector interfaces; while (m_xml->readNextStartElement()) { if (m_xml->name() == "interface") - interfaces << readInterface(*m_xml); + interfaces.push_back(readInterface(*m_xml)); else m_xml->skipCurrentElement(); } @@ -478,12 +483,16 @@ bool Scanner::process() printf("\n"); printf("namespace QtWaylandServer {\n"); - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + bool needsNewLine = false; + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; + if (needsNewLine) + printf("\n"); + needsNewLine = true; + const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); @@ -538,7 +547,7 @@ bool Scanner::process() printEnums(interface.enums); - bool hasEvents = !interface.events.isEmpty(); + bool hasEvents = !interface.events.empty(); if (hasEvents) { printf("\n"); @@ -559,7 +568,7 @@ bool Scanner::process() printf(" virtual void %s_bind_resource(Resource *resource);\n", interfaceNameStripped); printf(" virtual void %s_destroy_resource(Resource *resource);\n", interfaceNameStripped); - bool hasRequests = !interface.requests.isEmpty(); + bool hasRequests = !interface.requests.empty(); if (hasRequests) { printf("\n"); @@ -584,8 +593,7 @@ bool Scanner::process() printf(" static const struct ::%s_interface m_%s_interface;\n", interfaceName, interfaceName); printf("\n"); - for (int i = 0; i < interface.requests.size(); ++i) { - const WaylandEvent &e = interface.requests.at(i); + for (const WaylandEvent &e : interface.requests) { printf(" static void "); printEventHandlerSignature(e, interfaceName); @@ -603,9 +611,6 @@ bool Scanner::process() printf(" };\n"); printf(" DisplayDestroyedListener m_displayDestroyedListener;\n"); printf(" };\n"); - - if (j < interfaces.size() - 1) - printf("\n"); } printf("}\n"); @@ -629,8 +634,7 @@ bool Scanner::process() printf("namespace QtWaylandServer {\n"); bool needsNewLine = false; - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; @@ -778,7 +782,7 @@ bool Scanner::process() printf(" }\n"); printf("\n"); - bool hasRequests = !interface.requests.isEmpty(); + bool hasRequests = !interface.requests.empty(); QByteArray interfaceMember = hasRequests ? "&m_" + interface.name + "_interface" : QByteArray("nullptr"); @@ -816,11 +820,12 @@ bool Scanner::process() if (hasRequests) { printf("\n"); printf(" const struct ::%s_interface %s::m_%s_interface = {", interfaceName, interfaceName, interfaceName); - for (int i = 0; i < interface.requests.size(); ++i) { - if (i > 0) + bool needsComma = false; + for (const WaylandEvent &e : interface.requests) { + if (needsComma) printf(","); + needsComma = true; printf("\n"); - const WaylandEvent &e = interface.requests.at(i); printf(" %s::handle_%s", interfaceName, e.name.constData()); } printf("\n"); @@ -836,11 +841,10 @@ bool Scanner::process() } printf("\n"); - for (int i = 0; i < interface.requests.size(); ++i) { + for (const WaylandEvent &e : interface.requests) { printf("\n"); printf(" void %s::", interfaceName); - const WaylandEvent &e = interface.requests.at(i); printEventHandlerSignature(e, interfaceName, false); printf("\n"); @@ -849,9 +853,8 @@ bool Scanner::process() printf(" Resource *r = Resource::fromResource(resource);\n"); printf(" static_cast<%s *>(r->%s_object)->%s_%s(\n", interfaceName, interfaceNameStripped, interfaceNameStripped, e.name.constData()); printf(" r"); - for (int i = 0; i < e.arguments.size(); ++i) { + for (const WaylandArgument &a : e.arguments) { printf(",\n"); - const WaylandArgument &a = e.arguments.at(i); QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); const char *argumentName = a.name.constData(); @@ -865,17 +868,15 @@ bool Scanner::process() } } - for (int i = 0; i < interface.events.size(); ++i) { + for (const WaylandEvent &e : interface.events) { printf("\n"); - const WaylandEvent &e = interface.events.at(i); printf(" void %s::send_", interfaceName); printEvent(e); printf("\n"); printf(" {\n"); printf(" send_%s(\n", e.name.constData()); printf(" m_resource->handle"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { printf(",\n"); printf(" %s", a.name.constData()); } @@ -888,8 +889,7 @@ bool Scanner::process() printf("\n"); printf(" {\n"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { if (a.type != "array") continue; QByteArray array = a.name + "_data"; @@ -905,8 +905,7 @@ bool Scanner::process() printf(" %s_send_%s(\n", interfaceName, e.name.constData()); printf(" resource"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { printf(",\n"); QByteArray cType = waylandToCType(a.type, a.interface); QByteArray qtType = waylandToQtType(a.type, a.interface, e.request); @@ -962,12 +961,17 @@ bool Scanner::process() } printf("\n"); printf("namespace QtWayland {\n"); - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + + bool needsNewLine = false; + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; + if (needsNewLine) + printf("\n"); + needsNewLine = true; + const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); @@ -994,7 +998,7 @@ bool Scanner::process() printEnums(interface.enums); - if (!interface.requests.isEmpty()) { + if (!interface.requests.empty()) { printf("\n"); for (const WaylandEvent &e : interface.requests) { const WaylandArgument *new_id = newIdArgument(e.arguments); @@ -1011,7 +1015,7 @@ bool Scanner::process() } } - bool hasEvents = !interface.events.isEmpty(); + bool hasEvents = !interface.events.empty(); if (hasEvents) { printf("\n"); @@ -1028,8 +1032,7 @@ bool Scanner::process() if (hasEvents) { printf(" void init_listener();\n"); printf(" static const struct %s_listener m_%s_listener;\n", interfaceName, interfaceName); - for (int i = 0; i < interface.events.size(); ++i) { - const WaylandEvent &e = interface.events.at(i); + for (const WaylandEvent &e : interface.events) { printf(" static void "); printEventHandlerSignature(e, interfaceName); @@ -1038,9 +1041,6 @@ bool Scanner::process() } printf(" struct ::%s *m_%s;\n", interfaceName, interfaceName); printf(" };\n"); - - if (j < interfaces.size() - 1) - printf("\n"); } printf("}\n"); printf("\n"); @@ -1077,18 +1077,23 @@ bool Scanner::process() printf("#endif\n"); printf("}\n"); printf("\n"); - for (int j = 0; j < interfaces.size(); ++j) { - const WaylandInterface &interface = interfaces.at(j); + + bool needsNewLine = false; + for (const WaylandInterface &interface : interfaces) { if (ignoreInterface(interface.name)) continue; + if (needsNewLine) + printf("\n"); + needsNewLine = true; + const char *interfaceName = interface.name.constData(); QByteArray stripped = stripInterfaceName(interface.name); const char *interfaceNameStripped = stripped.constData(); - bool hasEvents = !interface.events.isEmpty(); + bool hasEvents = !interface.events.empty(); printf(" %s::%s(struct ::wl_registry *registry, int id, int version)\n", interfaceName, interfaceName); printf(" {\n"); @@ -1152,9 +1157,8 @@ bool Scanner::process() printf(" return &::%s_interface;\n", interfaceName); printf(" }\n"); - for (int i = 0; i < interface.requests.size(); ++i) { + for (const WaylandEvent &e : interface.requests) { printf("\n"); - const WaylandEvent &e = interface.requests.at(i); const WaylandArgument *new_id = newIdArgument(e.arguments); QByteArray new_id_str = "void "; if (new_id) { @@ -1167,8 +1171,7 @@ bool Scanner::process() printEvent(e); printf("\n"); printf(" {\n"); - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { if (a.type != "array") continue; QByteArray array = a.name + "_data"; @@ -1180,12 +1183,11 @@ bool Scanner::process() printf(" %s.alloc = 0;\n", arrayName); printf("\n"); } - int actualArgumentCount = new_id ? e.arguments.size() - 1 : e.arguments.size(); + int actualArgumentCount = new_id ? int(e.arguments.size()) - 1 : int(e.arguments.size()); printf(" %s%s_%s(\n", new_id ? "return " : "", interfaceName, e.name.constData()); printf(" m_%s%s", interfaceName, actualArgumentCount > 0 ? "," : ""); bool needsComma = false; - for (int i = 0; i < e.arguments.size(); ++i) { - const WaylandArgument &a = e.arguments.at(i); + for (const WaylandArgument &a : e.arguments) { bool isNewId = a.type == "new_id"; if (isNewId && !a.interface.isEmpty()) continue; @@ -1215,8 +1217,7 @@ bool Scanner::process() if (hasEvents) { printf("\n"); - for (int i = 0; i < interface.events.size(); ++i) { - const WaylandEvent &e = interface.events.at(i); + for (const WaylandEvent &e : interface.events) { printf(" void %s::%s_", interfaceName, interfaceNameStripped); printEvent(e, true); printf("\n"); @@ -1229,17 +1230,17 @@ bool Scanner::process() printf(" {\n"); printf(" Q_UNUSED(object);\n"); printf(" static_cast<%s *>(data)->%s_%s(", interfaceName, interfaceNameStripped, e.name.constData()); - for (int i = 0; i < e.arguments.size(); ++i) { + bool needsComma = false; + for (const WaylandArgument &a : e.arguments) { + if (needsComma) + printf(","); + needsComma = true; printf("\n"); - const WaylandArgument &a = e.arguments.at(i); const char *argumentName = a.name.constData(); if (a.type == "string") printf(" QString::fromUtf8(%s)", argumentName); else printf(" %s", argumentName); - - if (i < e.arguments.size() - 1) - printf(","); } printf(");\n"); @@ -1247,9 +1248,8 @@ bool Scanner::process() printf("\n"); } printf(" const struct %s_listener %s::m_%s_listener = {\n", interfaceName, interfaceName, interfaceName); - for (int i = 0; i < interface.events.size(); ++i) { - const WaylandEvent &e = interface.events.at(i); - printf(" %s::handle_%s%s\n", interfaceName, e.name.constData(), i < interface.events.size() - 1 ? "," : ""); + for (const WaylandEvent &e : interface.events) { + printf(" %s::handle_%s,\n", interfaceName, e.name.constData()); } printf(" };\n"); printf("\n"); @@ -1259,9 +1259,6 @@ bool Scanner::process() printf(" %s_add_listener(m_%s, &m_%s_listener, this);\n", interfaceName, interfaceName, interfaceName); printf(" }\n"); } - - if (j < interfaces.size() - 1) - printf("\n"); } printf("}\n"); printf("\n"); diff --git a/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp b/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp index fe68d520d59..35ac7252866 100644 --- a/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp +++ b/tests/auto/wayland/datadevicev1/tst_datadevicev1.cpp @@ -66,7 +66,7 @@ void tst_datadevicev1::initTestCase() { QCOMPOSITOR_TRY_VERIFY(pointer()); QCOMPOSITOR_TRY_VERIFY(!pointer()->resourceMap().empty()); - QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4); + QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 5); QCOMPOSITOR_TRY_VERIFY(keyboard()); @@ -104,8 +104,11 @@ void tst_datadevicev1::pasteAscii() keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 1); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 0); + pointer()->sendFrame(client); }); QTRY_COMPARE(window.m_text, "normal ascii"); } @@ -139,8 +142,11 @@ void tst_datadevicev1::pasteUtf8() keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 1); + pointer()->sendFrame(client); pointer()->sendButton(client, BTN_LEFT, 0); + pointer()->sendFrame(client); }); QTRY_COMPARE(window.m_text, "face with tears of joy: 😂"); } diff --git a/tests/auto/wayland/inputcontext/inputcontext.pro b/tests/auto/wayland/inputcontext/inputcontext.pro index 4419b3e77ad..1971d455ead 100644 --- a/tests/auto/wayland/inputcontext/inputcontext.pro +++ b/tests/auto/wayland/inputcontext/inputcontext.pro @@ -1,6 +1,4 @@ include (../shared/shared.pri) -QT += waylandcompositor - TARGET = tst_inputcontext SOURCES += tst_inputcontext.cpp diff --git a/tests/auto/wayland/output/tst_output.cpp b/tests/auto/wayland/output/tst_output.cpp index 2d2c8efd682..29c773cf65e 100644 --- a/tests/auto/wayland/output/tst_output.cpp +++ b/tests/auto/wayland/output/tst_output.cpp @@ -196,8 +196,11 @@ void tst_output::removePrimaryScreen() exec([&] { auto *surface = xdgToplevel()->surface(); pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_LEFT, 1); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_LEFT, 0); + pointer()->sendFrame(client()); }); // Wait to make sure mouse events dont't cause a crash now that the screen has changed diff --git a/tests/auto/wayland/primaryselectionv1/tst_primaryselectionv1.cpp b/tests/auto/wayland/primaryselectionv1/tst_primaryselectionv1.cpp index 281e4c5d11a..216db85cd6b 100644 --- a/tests/auto/wayland/primaryselectionv1/tst_primaryselectionv1.cpp +++ b/tests/auto/wayland/primaryselectionv1/tst_primaryselectionv1.cpp @@ -268,7 +268,7 @@ void tst_primaryselectionv1::initTestCase() { QCOMPOSITOR_TRY_VERIFY(pointer()); QCOMPOSITOR_TRY_VERIFY(!pointer()->resourceMap().empty()); - QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4); + QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 5); QCOMPOSITOR_TRY_VERIFY(keyboard()); } @@ -329,8 +329,11 @@ void tst_primaryselectionv1::pasteAscii() device->sendSelection(offer); pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_MIDDLE, 1); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_MIDDLE, 0); + pointer()->sendFrame(client()); }); QTRY_COMPARE(window.m_formats, QStringList{"text/plain"}); QTRY_COMPARE(window.m_text, "normal ascii"); @@ -372,8 +375,11 @@ void tst_primaryselectionv1::pasteUtf8() device->sendSelection(offer); pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_MIDDLE, 1); + pointer()->sendFrame(client()); pointer()->sendButton(client(), BTN_MIDDLE, 0); + pointer()->sendFrame(client()); }); QTRY_COMPARE(window.m_formats, QStringList({"text/plain", "text/plain;charset=utf-8"})); QTRY_COMPARE(window.m_text, "face with tears of joy: 😂"); @@ -428,8 +434,11 @@ void tst_primaryselectionv1::copy() auto *surface = xdgSurface()->m_surface; keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol pointer()->sendEnter(surface, {32, 32}); + pointer()->sendFrame(client()); mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 1); + pointer()->sendFrame(client()); mouseSerials << pointer()->sendButton(client(), BTN_MIDDLE, 0); + pointer()->sendFrame(client()); }); QCOMPOSITOR_TRY_VERIFY(primarySelectionDevice()->m_selectionSource); QCOMPOSITOR_TRY_VERIFY(mouseSerials.contains(primarySelectionDevice()->m_serial)); diff --git a/tests/auto/wayland/seatv4/tst_seatv4.cpp b/tests/auto/wayland/seatv4/tst_seatv4.cpp index 77304deafb3..40f8742a2fb 100644 --- a/tests/auto/wayland/seatv4/tst_seatv4.cpp +++ b/tests/auto/wayland/seatv4/tst_seatv4.cpp @@ -212,22 +212,20 @@ void tst_seatv4::simpleAxis_data() { QTest::addColumn("axis"); QTest::addColumn("value"); - QTest::addColumn("orientation"); QTest::addColumn("angleDelta"); // Directions in regular windows/linux terms (no "natural" scrolling) - QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << Qt::Vertical << QPoint{0, -12}; - QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << Qt::Vertical << QPoint{0, 12}; - QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << Qt::Horizontal << QPoint{-12, 0}; - QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << Qt::Horizontal << QPoint{12, 0}; - QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << Qt::Vertical << QPoint{0, 120}; + QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12}; + QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12}; + QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0}; + QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0}; + QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120}; } void tst_seatv4::simpleAxis() { QFETCH(uint, axis); QFETCH(qreal, value); - QFETCH(Qt::Orientation, orientation); QFETCH(QPoint, angleDelta); class WheelWindow : QRasterWindow { @@ -256,27 +254,18 @@ void tst_seatv4::simpleAxis() // We didn't press any buttons QCOMPARE(event->buttons(), Qt::NoButton); - if (event->orientation() == Qt::Horizontal) - QCOMPARE(event->delta(), event->angleDelta().x()); - else - QCOMPARE(event->delta(), event->angleDelta().y()); - // There has been no information about what created the event. // Documentation says not synthesized is appropriate in such cases QCOMPARE(event->source(), Qt::MouseEventNotSynthesized); - m_events.append(Event(event->pixelDelta(), event->angleDelta(), event->orientation())); + m_events.append(Event{event->pixelDelta(), event->angleDelta()}); } struct Event // Because I didn't find a convenient way to copy it entirely { - // TODO: Constructors can be removed when we start supporting brace-initializers Event() = default; - Event(const QPoint &pixelDelta, const QPoint &angleDelta, Qt::Orientation orientation) - : pixelDelta(pixelDelta), angleDelta(angleDelta), orientation(orientation) - {} + const QPoint pixelDelta; const QPoint angleDelta; // eights of a degree, positive is upwards, left - const Qt::Orientation orientation{}; }; QVector m_events; }; @@ -299,7 +288,6 @@ void tst_seatv4::simpleAxis() QTRY_COMPARE(window.m_events.size(), 1); auto event = window.m_events.takeFirst(); QCOMPARE(event.angleDelta, angleDelta); - QCOMPARE(event.orientation, orientation); } void tst_seatv4::invalidPointerEvents() diff --git a/tests/auto/wayland/seatv5/tst_seatv5.cpp b/tests/auto/wayland/seatv5/tst_seatv5.cpp index 5b9235d9aa3..ca8de31acb1 100644 --- a/tests/auto/wayland/seatv5/tst_seatv5.cpp +++ b/tests/auto/wayland/seatv5/tst_seatv5.cpp @@ -41,18 +41,11 @@ public: removeAll(); - uint capabilities = MockCompositor::Seat::capability_pointer; + uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch; int version = 5; add(capabilities, version); }); } - - Pointer *pointer() - { - auto *seat = get(); - Q_ASSERT(seat); - return seat->m_pointer; - } }; class tst_seatv5 : public QObject, private SeatV5Compositor @@ -61,6 +54,8 @@ class tst_seatv5 : public QObject, private SeatV5Compositor private slots: void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } void bindsToSeat(); + + // Pointer tests void createsPointer(); void setsCursorOnEnter(); void usesEnterSerial(); @@ -69,6 +64,10 @@ private slots: void fingerScroll(); void fingerScrollSlow(); void wheelDiscreteScroll(); + + // Touch tests + void createsTouch(); + void singleTap(); }; void tst_seatv5::bindsToSeat() @@ -128,12 +127,8 @@ public: QRasterWindow::wheelEvent(event); // qDebug() << event << "angleDelta" << event->angleDelta() << "pixelDelta" << event->pixelDelta(); - if (event->phase() == Qt::ScrollUpdate || event->phase() == Qt::NoScrollPhase) { - // Angle delta should always be provided (says docs, but QPA sends compatibility events - // for Qt4 with zero angleDelta, and with a delta) - QVERIFY(!event->angleDelta().isNull() || event->delta()); - } else { - // Shouldn't have deltas in the other phases + if (event->phase() != Qt::ScrollUpdate && event->phase() != Qt::NoScrollPhase) { + // Shouldn't have deltas in the these phases QCOMPARE(event->angleDelta(), QPoint(0, 0)); QCOMPARE(event->pixelDelta(), QPoint(0, 0)); } @@ -145,13 +140,6 @@ public: // We didn't press any buttons QCOMPARE(event->buttons(), Qt::NoButton); - if (!event->angleDelta().isNull()) { - if (event->orientation() == Qt::Horizontal) - QCOMPARE(event->delta(), event->angleDelta().x()); - else - QCOMPARE(event->delta(), event->angleDelta().y()); - } - m_events.append(Event{event}); } struct Event // Because I didn't find a convenient way to copy it entirely @@ -161,14 +149,12 @@ public: : phase(event->phase()) , pixelDelta(event->pixelDelta()) , angleDelta(event->angleDelta()) - , orientation(event->orientation()) , source(event->source()) { } const Qt::ScrollPhase phase{}; const QPoint pixelDelta; const QPoint angleDelta; // eights of a degree, positive is upwards, left - const Qt::Orientation orientation{}; const Qt::MouseEventSource source{}; }; QVector m_events; @@ -178,22 +164,20 @@ void tst_seatv5::simpleAxis_data() { QTest::addColumn("axis"); QTest::addColumn("value"); - QTest::addColumn("orientation"); QTest::addColumn("angleDelta"); // Directions in regular windows/linux terms (no "natural" scrolling) - QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << Qt::Vertical << QPoint{0, -12}; - QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << Qt::Vertical << QPoint{0, 12}; - QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << Qt::Horizontal << QPoint{-12, 0}; - QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << Qt::Horizontal << QPoint{12, 0}; - QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << Qt::Vertical << QPoint{0, 120}; + QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12}; + QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12}; + QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0}; + QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0}; + QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120}; } void tst_seatv5::simpleAxis() { QFETCH(uint, axis); QFETCH(qreal, value); - QFETCH(Qt::Orientation, orientation); QFETCH(QPoint, angleDelta); WheelWindow window; @@ -220,7 +204,6 @@ void tst_seatv5::simpleAxis() // There has been no information about what created the event. // Documentation says not synthesized is appropriate in such cases QCOMPARE(e.source, Qt::MouseEventNotSynthesized); - QCOMPARE(e.orientation, orientation); QCOMPARE(e.angleDelta, angleDelta); } @@ -263,7 +246,7 @@ void tst_seatv5::fingerScroll() { auto e = window.m_events.takeFirst(); QCOMPARE(e.phase, Qt::ScrollUpdate); - QCOMPARE(e.orientation, Qt::Vertical); + QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll // QCOMPARE(e.angleDelta, angleDelta); // TODO: what should this be? QCOMPARE(e.pixelDelta, QPoint(0, 10)); QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel @@ -281,7 +264,7 @@ void tst_seatv5::fingerScroll() { auto e = window.m_events.takeFirst(); QCOMPARE(e.phase, Qt::ScrollUpdate); - QCOMPARE(e.orientation, Qt::Horizontal); + QVERIFY(qAbs(e.angleDelta.x()) > qAbs(e.angleDelta.y())); // Horizontal scroll QCOMPARE(e.pixelDelta, QPoint(10, 0)); QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel } @@ -373,7 +356,7 @@ void tst_seatv5::wheelDiscreteScroll() { auto e = window.m_events.takeFirst(); QCOMPARE(e.phase, Qt::NoScrollPhase); - QCOMPARE(e.orientation, Qt::Vertical); + QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll // According to the docs the angle delta is in eights of a degree and most mice have // 1 click = 15 degrees. The angle delta should therefore be: // 15 degrees / (1/8 eights per degrees) = 120 eights of degrees. @@ -383,5 +366,70 @@ void tst_seatv5::wheelDiscreteScroll() } } +void tst_seatv5::createsTouch() +{ + QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().size(), 1); + QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 5); +} + +class TouchWindow : public QRasterWindow { +public: + TouchWindow() + { + resize(64, 64); + show(); + } + void touchEvent(QTouchEvent *event) override + { + QRasterWindow::touchEvent(event); + m_events.append(Event{event}); + } + struct Event // Because I didn't find a convenient way to copy it entirely + { + explicit Event() = default; + explicit Event(const QTouchEvent *event) + : type(event->type()) + , touchPointStates(event->touchPointStates()) + , touchPoints(event->touchPoints()) + { + } + const QEvent::Type type{}; + const Qt::TouchPointStates touchPointStates{}; + const QList touchPoints; + }; + QVector m_events; +}; + +void tst_seatv5::singleTap() +{ + TouchWindow window; + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { + auto *t = touch(); + auto *c = client(); + t->sendDown(xdgToplevel()->surface(), {32, 32}, 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-window.frameMargins().left(), 32-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-window.frameMargins().left(), 32-window.frameMargins().top())); + } +} + QCOMPOSITOR_TEST_MAIN(tst_seatv5) #include "tst_seatv5.moc" diff --git a/tests/auto/wayland/shared/coreprotocol.cpp b/tests/auto/wayland/shared/coreprotocol.cpp index f9335e7834a..120c256c653 100644 --- a/tests/auto/wayland/shared/coreprotocol.cpp +++ b/tests/auto/wayland/shared/coreprotocol.cpp @@ -191,12 +191,15 @@ Seat::~Seat() { qDeleteAll(m_oldPointers); delete m_pointer; + + qDeleteAll(m_oldTouchs); + delete m_touch; + + qDeleteAll(m_oldKeyboards); + delete m_keyboard; } void Seat::setCapabilities(uint capabilities) { - // TODO: Add support for touch - Q_ASSERT(~capabilities & capability_touch); - m_capabilities = capabilities; if (m_capabilities & capability_pointer) { @@ -207,6 +210,14 @@ void Seat::setCapabilities(uint capabilities) { m_pointer = nullptr; } + if (m_capabilities & capability_touch) { + if (!m_touch) + m_touch = (new Touch(this)); + } else if (m_touch) { + m_oldTouchs << m_touch; + m_touch = nullptr; + } + if (m_capabilities & capability_keyboard) { if (!m_keyboard) m_keyboard = (new Keyboard(this)); @@ -234,9 +245,24 @@ void Seat::seat_get_pointer(Resource *resource, uint32_t id) m_pointer->add(resource->client(), id, resource->version()); } +void Seat::seat_get_touch(QtWaylandServer::wl_seat::Resource *resource, uint32_t id) +{ + if (~m_capabilities & capability_touch) { + qWarning() << "Client requested a wl_touch without the capability being available." + << "This Could be a race condition when hotunplugging," + << "but is most likely a client error"; + Touch *touch = new Touch(this); + touch->add(resource->client(), id, resource->version()); + // TODO: mark as destroyed + m_oldTouchs << touch; + return; + } + m_touch->add(resource->client(), id, resource->version()); +} + void Seat::seat_get_keyboard(QtWaylandServer::wl_seat::Resource *resource, uint32_t id) { - if (~m_capabilities & capability_pointer) { + if (~m_capabilities & capability_keyboard) { qWarning() << "Client requested a wl_keyboard without the capability being available." << "This Could be a race condition when hotunplugging," << "but is most likely a client error"; @@ -371,6 +397,40 @@ void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resourc emit setCursor(serial); } +uint Touch::sendDown(Surface *surface, const QPointF &position, int id) +{ + wl_fixed_t x = wl_fixed_from_double(position.x()); + wl_fixed_t y = wl_fixed_from_double(position.y()); + uint serial = m_seat->m_compositor->nextSerial(); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + wl_client *client = surface->resource()->client(); + + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_down(r->handle, serial, time, surface->resource()->handle, id, x, y); + + return serial; +} + +uint Touch::sendUp(wl_client *client, int id) +{ + uint serial = m_seat->m_compositor->nextSerial(); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_up(r->handle, serial, time, id); + + return serial; +} + +void Touch::sendFrame(wl_client *client) +{ + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + send_frame(r->handle); +} + uint Keyboard::sendEnter(Surface *surface) { auto serial = m_seat->m_compositor->nextSerial(); diff --git a/tests/auto/wayland/shared/coreprotocol.h b/tests/auto/wayland/shared/coreprotocol.h index 264c5f694e6..50812c29658 100644 --- a/tests/auto/wayland/shared/coreprotocol.h +++ b/tests/auto/wayland/shared/coreprotocol.h @@ -38,6 +38,7 @@ namespace MockCompositor { class WlCompositor; class Output; class Pointer; +class Touch; class Keyboard; class CursorRole; class ShmPool; @@ -236,7 +237,7 @@ class Seat : public Global, public QtWaylandServer::wl_seat { Q_OBJECT public: - explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard, int version = 4); + explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch, int version = 5); ~Seat() override; void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead void send_capabilities(uint capabilities) = delete; // Use wrapper instead @@ -247,6 +248,9 @@ public: Pointer* m_pointer = nullptr; QVector m_oldPointers; + Touch* m_touch = nullptr; + QVector m_oldTouchs; + Keyboard* m_keyboard = nullptr; QVector m_oldKeyboards; @@ -259,8 +263,8 @@ protected: } void seat_get_pointer(Resource *resource, uint32_t id) override; + void seat_get_touch(Resource *resource, uint32_t id) override; void seat_get_keyboard(Resource *resource, uint32_t id) override; -// void seat_get_touch(Resource *resource, uint32_t id) override; // void seat_release(Resource *resource) override; }; @@ -307,6 +311,18 @@ public: Surface *m_surface = nullptr; }; +class Touch : public QObject, public QtWaylandServer::wl_touch +{ + Q_OBJECT +public: + explicit Touch(Seat *seat) : m_seat(seat) {} + uint sendDown(Surface *surface, const QPointF &position, int id); + uint sendUp(wl_client *client, int id); + void sendFrame(wl_client *client); + + Seat *m_seat = nullptr; +}; + class Keyboard : public QObject, public QtWaylandServer::wl_keyboard { Q_OBJECT diff --git a/tests/auto/wayland/shared/mockcompositor.cpp b/tests/auto/wayland/shared/mockcompositor.cpp index 6b9af4295b8..5f2d89078e5 100644 --- a/tests/auto/wayland/shared/mockcompositor.cpp +++ b/tests/auto/wayland/shared/mockcompositor.cpp @@ -41,7 +41,7 @@ DefaultCompositor::DefaultCompositor() add(); auto *output = add(); output->m_data.physicalSize = output->m_data.mode.physicalSizeForDpi(96); - add(Seat::capability_pointer | Seat::capability_keyboard); + add(Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch); add(); add(); // TODO: other shells, viewporter, xdgoutput etc diff --git a/tests/auto/wayland/shared/mockcompositor.h b/tests/auto/wayland/shared/mockcompositor.h index aa85a4aeace..3cb9b337c7c 100644 --- a/tests/auto/wayland/shared/mockcompositor.h +++ b/tests/auto/wayland/shared/mockcompositor.h @@ -60,6 +60,7 @@ public: XdgToplevel *xdgToplevel(int i = 0) { return get()->toplevel(i); } XdgPopup *xdgPopup(int i = 0) { return get()->popup(i); } Pointer *pointer() { auto *seat = get(); Q_ASSERT(seat); return seat->m_pointer; } + Touch *touch() { auto *seat = get(); Q_ASSERT(seat); return seat->m_touch; } Surface *cursorSurface() { auto *p = pointer(); return p ? p->cursorSurface() : nullptr; } Keyboard *keyboard() { auto *seat = get(); Q_ASSERT(seat); return seat->m_keyboard; } uint sendXdgShellPing(); diff --git a/tests/auto/wayland/xdgshell/tst_xdgshell.cpp b/tests/auto/wayland/xdgshell/tst_xdgshell.cpp index 038ff6624f7..ac5c24988f8 100644 --- a/tests/auto/wayland/xdgshell/tst_xdgshell.cpp +++ b/tests/auto/wayland/xdgshell/tst_xdgshell.cpp @@ -214,12 +214,13 @@ void tst_xdgshell::popup() uint clickSerial = exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendFrame(c); uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); return serial; -// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendFrame(c); }); QTRY_VERIFY(window.m_popup); @@ -298,13 +299,14 @@ void tst_xdgshell::tooltipOnPopup() exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); //TODO: uncomment when we support seat v5 + p->sendFrame(c); p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); p->sendLeave(surface); -// p->sendFrame(); + p->sendFrame(c); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); @@ -315,11 +317,12 @@ void tst_xdgshell::tooltipOnPopup() exec([=] { auto *surface = xdgPopup()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); + p->sendFrame(c); p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); @@ -380,13 +383,14 @@ void tst_xdgshell::switchPopups() exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); //TODO: uncomment when we support seat v5 - p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); + p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); p->sendLeave(surface); -// p->sendFrame(); + p->sendFrame(c); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); @@ -399,11 +403,12 @@ void tst_xdgshell::switchPopups() exec([=] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); + auto *c = client(); p->sendEnter(surface, {100, 100}); -// p->sendFrame(); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); - p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); -// p->sendFrame(); + p->sendFrame(c); + p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); }); // The client will now hide one popup and then show another @@ -476,10 +481,14 @@ void tst_xdgshell::windowGeometry() exec([=] { xdgToplevel()->sendCompleteConfigure(); }); - QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size())); + QSize marginsSize; + marginsSize.setWidth(window.frameMargins().left() + window.frameMargins().right()); + marginsSize.setHeight(window.frameMargins().top() + window.frameMargins().bottom()); + + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), QSize(400, 320) + marginsSize)); window.resize(800, 600); - QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), window.frameGeometry().size())); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committed.windowGeometry, QRect(QPoint(0, 0), QSize(800, 600) + marginsSize)); } void tst_xdgshell::foreignSurface()