Merge "Merge remote-tracking branch 'origin/dev' into wip/qt6"

This commit is contained in:
Simon Hausmann 2019-09-16 23:10:27 +02:00
commit 95ce296968
24 changed files with 536 additions and 244 deletions

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle_inhibit_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zwp_idle_inhibit_manager_v1" version="1">
<description summary="control behavior when display idles">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the idle inhibitor object">
Destroy the inhibit manager.
</description>
</request>
<request name="create_inhibitor">
<description summary="create a new inhibitor object">
Create a new inhibitor object associated with the given surface.
</description>
<arg name="id" type="new_id" interface="zwp_idle_inhibitor_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface that inhibits the idle behavior"/>
</request>
</interface>
<interface name="zwp_idle_inhibitor_v1" version="1">
<description summary="context object for inhibiting idle behavior">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the idle inhibitor object">
Remove the inhibitor effect from the associated wl_surface.
</description>
</request>
</interface>
</protocol>

View File

@ -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",

View File

@ -20,7 +20,7 @@ qtConfig(xkbcommon) {
}
qtHaveModule(linuxaccessibility_support_private): \
QT += linuxaccessibility_support_private
QT_PRIVATE += linuxaccessibility_support_private
QMAKE_USE += wayland-client

View File

@ -48,6 +48,8 @@
#include <wayland-cursor.h>
#include <algorithm>
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 QMultiMap<WaylandCursor, QByteArray>cursorNamesMap {
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<QByteArray> 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;
}

View File

@ -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;
}

View File

@ -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<QWaylandWindow> mFocus;
QList<QWindowSystemInterface::TouchPoint> mTouchPoints;

View File

@ -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

View File

@ -47,6 +47,7 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandwindowmanagerintegration_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandinputdevice_p.h"
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/QScreen>
#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h>
@ -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;
}

View File

@ -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)

View File

@ -57,7 +57,8 @@
#include <QtGui/QImage>
#include <qpa/qplatformwindow.h>
#include <QMutex>
#include <QLinkedList>
#include <list>
QT_BEGIN_NAMESPACE
@ -116,7 +117,7 @@ private:
QWaylandShmBuffer *getBuffer(const QSize &size);
QWaylandDisplay *mDisplay = nullptr;
QLinkedList<QWaylandShmBuffer *> mBuffers;
std::list<QWaylandShmBuffer *> mBuffers;
QWaylandShmBuffer *mFrontBuffer = nullptr;
QWaylandShmBuffer *mBackBuffer = nullptr;
bool mPainting = false;

View File

@ -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<QWaylandWindow*>(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);

View File

@ -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;

View File

@ -40,7 +40,8 @@
#include <QCoreApplication>
#include <QFile>
#include <QXmlStreamReader>
#include <QtCore/QList>
#include <vector>
class Scanner
{
@ -63,7 +64,7 @@ private:
struct WaylandEnum {
QByteArray name;
QList<WaylandEnumEntry> entries;
std::vector<WaylandEnumEntry> entries;
};
struct WaylandArgument {
@ -78,16 +79,16 @@ private:
bool request;
QByteArray name;
QByteArray type;
QList<WaylandArgument> arguments;
std::vector<WaylandArgument> arguments;
};
struct WaylandInterface {
QByteArray name;
int version;
QList<WaylandEnum> enums;
QList<WaylandEvent> events;
QList<WaylandEvent> requests;
std::vector<WaylandEnum> enums;
std::vector<WaylandEvent> events;
std::vector<WaylandEvent> 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<WaylandArgument> &arguments);
const Scanner::WaylandArgument *newIdArgument(const std::vector<WaylandArgument> &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<WaylandEnum> &enums);
void printEnums(const std::vector<WaylandEnum> &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<WaylandArgument> &arguments)
const Scanner::WaylandArgument *Scanner::newIdArgument(const std::vector<WaylandArgument> &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<WaylandEnum> &enums)
void Scanner::printEnums(const std::vector<WaylandEnum> &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<WaylandInterface> interfaces;
std::vector<WaylandInterface> 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");

View File

@ -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: 😂");
}

View File

@ -1,6 +1,4 @@
include (../shared/shared.pri)
QT += waylandcompositor
TARGET = tst_inputcontext
SOURCES += tst_inputcontext.cpp

View File

@ -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

View File

@ -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));

View File

@ -212,22 +212,20 @@ void tst_seatv4::simpleAxis_data()
{
QTest::addColumn<uint>("axis");
QTest::addColumn<qreal>("value");
QTest::addColumn<Qt::Orientation>("orientation");
QTest::addColumn<QPoint>("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<Event> 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()

View File

@ -41,18 +41,11 @@ public:
removeAll<Seat>();
uint capabilities = MockCompositor::Seat::capability_pointer;
uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch;
int version = 5;
add<Seat>(capabilities, version);
});
}
Pointer *pointer()
{
auto *seat = get<Seat>();
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<Event> m_events;
@ -178,22 +164,20 @@ void tst_seatv5::simpleAxis_data()
{
QTest::addColumn<uint>("axis");
QTest::addColumn<qreal>("value");
QTest::addColumn<Qt::Orientation>("orientation");
QTest::addColumn<QPoint>("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<QTouchEvent::TouchPoint> touchPoints;
};
QVector<Event> 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"

View File

@ -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();

View File

@ -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<Pointer *> m_oldPointers;
Touch* m_touch = nullptr;
QVector<Touch *> m_oldTouchs;
Keyboard* m_keyboard = nullptr;
QVector<Keyboard *> 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

View File

@ -41,7 +41,7 @@ DefaultCompositor::DefaultCompositor()
add<SubCompositor>();
auto *output = add<Output>();
output->m_data.physicalSize = output->m_data.mode.physicalSizeForDpi(96);
add<Seat>(Seat::capability_pointer | Seat::capability_keyboard);
add<Seat>(Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch);
add<XdgWmBase>();
add<Shm>();
// TODO: other shells, viewporter, xdgoutput etc

View File

@ -60,6 +60,7 @@ public:
XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); }
XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(i); }
Pointer *pointer() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_pointer; }
Touch *touch() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_touch; }
Surface *cursorSurface() { auto *p = pointer(); return p ? p->cursorSurface() : nullptr; }
Keyboard *keyboard() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_keyboard; }
uint sendXdgShellPing();

View File

@ -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()