Merge remote-tracking branch 'origin/5.14' into 5.15

Change-Id: I1fdf822938737aebe0c13a2eba9e478ba8f8ba14
This commit is contained in:
Qt Forward Merge Bot 2019-09-17 03:07:15 +02:00
commit 0d39c32532
17 changed files with 373 additions and 91 deletions

View File

@ -140,7 +140,7 @@
}, },
"xcomposite-egl": { "xcomposite-egl": {
"label": "XComposite EGL", "label": "XComposite EGL",
"condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite", "condition": "features.wayland-client && features.opengl && features.egl && features.xlib && libs.xcomposite && features.egl_x11",
"output": [ "privateFeature" ] "output": [ "privateFeature" ]
}, },
"xcomposite-glx": { "xcomposite-glx": {

View File

@ -342,7 +342,7 @@ void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states)
if (m_toplevel) if (m_toplevel)
m_toplevel->requestWindowStates(states); m_toplevel->requestWindowStates(states);
else else
qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states"; qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel.";
} }
void QWaylandXdgSurfaceV6::setToplevel() void QWaylandXdgSurfaceV6::setToplevel()

View File

@ -377,7 +377,7 @@ void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states)
if (m_toplevel) if (m_toplevel)
m_toplevel->requestWindowStates(states); m_toplevel->requestWindowStates(states);
else else
qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states"; qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel zxdg_surface_v6.";
} }
void QWaylandXdgSurface::setToplevel() void QWaylandXdgSurface::setToplevel()

View File

@ -103,20 +103,23 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
return m_dragOffer.data(); return m_dragOffer.data();
} }
void QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
{ {
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
auto *seat = m_display->currentInputDevice(); auto *seat = m_display->currentInputDevice();
auto *origin = seat->pointerFocus(); auto *origin = seat->pointerFocus();
if (!origin) if (!origin)
origin = seat->touchFocus(); origin = seat->touchFocus();
if (origin) if (!origin) {
start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
else
qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found."; qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
return false;
}
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial());
return true;
} }
void QWaylandDataDevice::cancelDrag() void QWaylandDataDevice::cancelDrag()

View File

@ -89,7 +89,7 @@ public:
#if QT_CONFIG(draganddrop) #if QT_CONFIG(draganddrop)
QWaylandDataOffer *dragOffer() const; QWaylandDataOffer *dragOffer() const;
void startDrag(QMimeData *mimeData, QWaylandWindow *icon); bool startDrag(QMimeData *mimeData, QWaylandWindow *icon);
void cancelDrag(); void cancelDrag();
#endif #endif

View File

@ -66,8 +66,13 @@ void QWaylandDrag::startDrag()
{ {
QBasicDrag::startDrag(); QBasicDrag::startDrag();
QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle()); QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle());
m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon); if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) {
icon->addAttachOffset(-drag()->hotSpot()); icon->addAttachOffset(-drag()->hotSpot());
} else {
// Cancelling immediately does not work, since the event loop for QDrag::exec is started
// after this function returns.
QMetaObject::invokeMethod(this, [this](){ cancelDrag(); }, Qt::QueuedConnection);
}
} }
void QWaylandDrag::cancel() void QWaylandDrag::cancel()

View File

@ -119,7 +119,8 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
return; return;
auto *surface = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle())->wlSurface(); auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
auto *surface = window->wlSurface();
if (!surface || (surface != m_surface)) if (!surface || (surface != m_surface))
return; return;
@ -157,8 +158,10 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
if (queries & Qt::ImCursorRectangle) { if (queries & Qt::ImCursorRectangle) {
const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
const QRect &tRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
set_cursor_rectangle(tRect.x(), tRect.y(), tRect.width(), tRect.height()); const QMargins margins = window->frameMargins();
const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top());
set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
} }
if (queries & Qt::ImPreferredLanguage) { if (queries & Qt::ImPreferredLanguage) {

View File

@ -1200,7 +1200,7 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time,
QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease; QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text); handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code)) { if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code) && mRepeatRate > 0) {
mRepeatKey.key = qtkey; mRepeatKey.key = qtkey;
mRepeatKey.code = code; mRepeatKey.code = code;
mRepeatKey.time = time; mRepeatKey.time = time;
@ -1295,34 +1295,40 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
mParent->mSerial = serial; mParent->mSerial = serial;
mFocus = window; mFocus = window;
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus); mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed); QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
mParent->handleTouchPoint(id, Qt::TouchPointPressed, position);
} }
void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id) void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id)
{ {
Q_UNUSED(serial); Q_UNUSED(serial);
Q_UNUSED(time); Q_UNUSED(time);
mFocus = nullptr; mParent->handleTouchPoint(id, Qt::TouchPointReleased);
mParent->handleTouchPoint(id, 0, 0, Qt::TouchPointReleased);
// As of Weston 1.5.90 there is no touch_frame after the last touch_up if (allTouchPointsReleased()) {
// (i.e. when the last finger is released). To accommodate for this, issue a mFocus = nullptr;
// touch_frame. This cannot hurt since it is safe to call the touch_frame
// handler multiple times when there are no points left. // As of Weston 7.0.0 there is no touch_frame after the last touch_up
if (allTouchPointsReleased()) // (i.e. when the last finger is released). To accommodate for this, issue a
// touch_frame. This cannot hurt since it is safe to call the touch_frame
// handler multiple times when there are no points left.
// See: https://gitlab.freedesktop.org/wayland/weston/issues/44
// TODO: change logging category to lcQpaWaylandInput in newer versions.
qCDebug(lcQpaWayland, "Generating fake frame event to work around Weston bug");
touch_frame(); touch_frame();
}
} }
void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
{ {
Q_UNUSED(time); Q_UNUSED(time);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointMoved); QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y));
mParent->handleTouchPoint(id, Qt::TouchPointMoved, position);
} }
void QWaylandInputDevice::Touch::touch_cancel() void QWaylandInputDevice::Touch::touch_cancel()
{ {
mPrevTouchPoints.clear(); mPendingTouchPoints.clear();
mTouchPoints.clear();
QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension(); QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension();
if (touchExt) if (touchExt)
@ -1331,22 +1337,18 @@ void QWaylandInputDevice::Touch::touch_cancel()
QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice);
} }
void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::TouchPointState state) void QWaylandInputDevice::handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition)
{ {
QWindowSystemInterface::TouchPoint tp; auto end = mTouch->mPendingTouchPoints.end();
auto it = std::find_if(mTouch->mPendingTouchPoints.begin(), end, [id](auto tp){ return tp.id == id; });
if (it == end) {
it = mTouch->mPendingTouchPoints.insert(end, QWindowSystemInterface::TouchPoint());
it->id = id;
}
QWindowSystemInterface::TouchPoint &tp = *it;
// Find out the coordinates for Released events. // Only moved and pressed needs to update/set position
bool coordsOk = false; if (state == Qt::TouchPointMoved || state == Qt::TouchPointPressed) {
if (state == Qt::TouchPointReleased)
for (int i = 0; i < mTouch->mPrevTouchPoints.count(); ++i)
if (mTouch->mPrevTouchPoints.at(i).id == id) {
tp.area = mTouch->mPrevTouchPoints.at(i).area;
coordsOk = true;
break;
}
if (!coordsOk) {
// x and y are surface relative.
// We need a global (screen) position. // We need a global (screen) position.
QWaylandWindow *win = mTouch->mFocus; QWaylandWindow *win = mTouch->mFocus;
@ -1360,63 +1362,46 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch
tp.area = QRectF(0, 0, 8, 8); tp.area = QRectF(0, 0, 8, 8);
QMargins margins = win->frameMargins(); QMargins margins = win->frameMargins();
tp.area.moveCenter(win->window()->mapToGlobal(QPoint(x - margins.left(), y - margins.top()))); QPointF localPosition = surfacePosition - QPointF(margins.left(), margins.top());
// TODO: This doesn't account for high dpi scaling for the delta, but at least it matches
// what we have for mouse input.
QPointF delta = localPosition - localPosition.toPoint();
QPointF globalPosition = win->window()->mapToGlobal(localPosition.toPoint()) + delta;
tp.area.moveCenter(globalPosition);
} }
tp.state = state; tp.state = state;
tp.id = id;
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1; tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
mTouch->mTouchPoints.append(tp);
} }
bool QWaylandInputDevice::Touch::allTouchPointsReleased() bool QWaylandInputDevice::Touch::allTouchPointsReleased()
{ {
for (int i = 0; i < mTouchPoints.count(); ++i) for (const auto &tp : qAsConst(mPendingTouchPoints)) {
if (mTouchPoints.at(i).state != Qt::TouchPointReleased) if (tp.state != Qt::TouchPointReleased)
return false; return false;
}
return true; return true;
} }
void QWaylandInputDevice::Touch::releasePoints() void QWaylandInputDevice::Touch::releasePoints()
{ {
for (const QWindowSystemInterface::TouchPoint &previousPoint : qAsConst(mPrevTouchPoints)) { if (mPendingTouchPoints.empty())
QWindowSystemInterface::TouchPoint tp = previousPoint; return;
for (QWindowSystemInterface::TouchPoint &tp : mPendingTouchPoints)
tp.state = Qt::TouchPointReleased; tp.state = Qt::TouchPointReleased;
mTouchPoints.append(tp);
}
touch_frame(); touch_frame();
} }
void QWaylandInputDevice::Touch::touch_frame() void QWaylandInputDevice::Touch::touch_frame()
{ {
// Copy all points, that are in the previous but not in the current list, as stationary. // TODO: early return if no events?
for (int i = 0; i < mPrevTouchPoints.count(); ++i) {
const QWindowSystemInterface::TouchPoint &prevPoint(mPrevTouchPoints.at(i));
if (prevPoint.state == Qt::TouchPointReleased)
continue;
bool found = false;
for (int j = 0; j < mTouchPoints.count(); ++j)
if (mTouchPoints.at(j).id == prevPoint.id) {
found = true;
break;
}
if (!found) {
QWindowSystemInterface::TouchPoint p = prevPoint;
p.state = Qt::TouchPointStationary;
mTouchPoints.append(p);
}
}
if (mTouchPoints.isEmpty()) {
mPrevTouchPoints.clear();
return;
}
QWindow *window = mFocus ? mFocus->window() : nullptr; QWindow *window = mFocus ? mFocus->window() : nullptr;
if (mFocus) { if (mFocus) {
const QWindowSystemInterface::TouchPoint &tp = mTouchPoints.last(); const QWindowSystemInterface::TouchPoint &tp = mPendingTouchPoints.last();
// When the touch event is received, the global pos is calculated with the margins // When the touch event is received, the global pos is calculated with the margins
// in mind. Now we need to adjust again to get the correct local pos back. // in mind. Now we need to adjust again to get the correct local pos back.
QMargins margins = window->frameMargins(); QMargins margins = window->frameMargins();
@ -1425,14 +1410,21 @@ void QWaylandInputDevice::Touch::touch_frame()
if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers())) if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers()))
return; return;
} }
QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mTouchPoints);
if (allTouchPointsReleased()) QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mPendingTouchPoints);
mPrevTouchPoints.clear();
else // Prepare state for next frame
mPrevTouchPoints = mTouchPoints; const auto prevTouchPoints = mPendingTouchPoints;
mPendingTouchPoints.clear();
for (const auto &prevPoint: prevTouchPoints) {
// All non-released touch points should be part of the next touch event
if (prevPoint.state != Qt::TouchPointReleased) {
QWindowSystemInterface::TouchPoint tp = prevPoint;
tp.state = Qt::TouchPointStationary; // ... as stationary (unless proven otherwise)
mPendingTouchPoints.append(tp);
}
}
mTouchPoints.clear();
} }
} }

View File

@ -188,7 +188,7 @@ private:
uint32_t mSerial = 0; uint32_t mSerial = 0;
void seat_capabilities(uint32_t caps) override; void seat_capabilities(uint32_t caps) override;
void handleTouchPoint(int id, double x, double y, Qt::TouchPointState state); void handleTouchPoint(int id, Qt::TouchPointState state, const QPointF &surfacePosition = QPoint());
QTouchDevice *mTouchDevice = nullptr; QTouchDevice *mTouchDevice = nullptr;
@ -389,8 +389,7 @@ public:
QWaylandInputDevice *mParent = nullptr; QWaylandInputDevice *mParent = nullptr;
QPointer<QWaylandWindow> mFocus; QPointer<QWaylandWindow> mFocus;
QList<QWindowSystemInterface::TouchPoint> mTouchPoints; QList<QWindowSystemInterface::TouchPoint> mPendingTouchPoints;
QList<QWindowSystemInterface::TouchPoint> mPrevTouchPoints;
}; };
class QWaylandPointerEvent class QWaylandPointerEvent

View File

@ -201,10 +201,13 @@ void QWaylandWindow::initWindow()
void QWaylandWindow::initializeWlSurface() void QWaylandWindow::initializeWlSurface()
{ {
Q_ASSERT(!mSurface); Q_ASSERT(!mSurface);
mSurface.reset(new QWaylandSurface(mDisplay)); {
connect(mSurface.data(), &QWaylandSurface::screensChanged, QWriteLocker lock(&mSurfaceLock);
this, &QWaylandWindow::handleScreensChanged); mSurface.reset(new QWaylandSurface(mDisplay));
mSurface->m_window = this; connect(mSurface.data(), &QWaylandSurface::screensChanged,
this, &QWaylandWindow::handleScreensChanged);
mSurface->m_window = this;
}
emit wlSurfaceCreated(); emit wlSurfaceCreated();
} }
@ -242,6 +245,7 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
mSubSurfaceWindow = nullptr; mSubSurfaceWindow = nullptr;
if (mSurface) { if (mSurface) {
emit wlSurfaceDestroyed(); emit wlSurfaceDestroyed();
QWriteLocker lock(&mSurfaceLock);
mSurface.reset(); mSurface.reset();
} }
@ -617,10 +621,11 @@ QMutex QWaylandWindow::mFrameSyncMutex;
bool QWaylandWindow::waitForFrameSync(int timeout) bool QWaylandWindow::waitForFrameSync(int timeout)
{ {
QMutexLocker locker(&mFrameSyncMutex);
if (!mWaitingForFrameCallback) if (!mWaitingForFrameCallback)
return true; return true;
QMutexLocker locker(&mFrameSyncMutex);
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue); wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue);
mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout); mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout);
@ -1135,6 +1140,9 @@ void QWaylandWindow::requestUpdate()
void QWaylandWindow::handleUpdate() void QWaylandWindow::handleUpdate()
{ {
// TODO: Should sync subsurfaces avoid requesting frame callbacks? // TODO: Should sync subsurfaces avoid requesting frame callbacks?
QReadLocker lock(&mSurfaceLock);
if (!mSurface)
return;
if (mFrameCallback) { if (mFrameCallback) {
wl_callback_destroy(mFrameCallback); wl_callback_destroy(mFrameCallback);

View File

@ -53,6 +53,8 @@
#include <QtCore/QWaitCondition> #include <QtCore/QWaitCondition>
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <QtCore/QReadWriteLock>
#include <QtGui/QIcon> #include <QtGui/QIcon>
#include <QtCore/QVariant> #include <QtCore/QVariant>
#include <QtCore/QLoggingCategory> #include <QtCore/QLoggingCategory>
@ -274,6 +276,8 @@ private:
static QMutex mFrameSyncMutex; static QMutex mFrameSyncMutex;
static QWaylandWindow *mMouseGrab; static QWaylandWindow *mMouseGrab;
QReadWriteLock mSurfaceLock;
friend class QWaylandSubSurface; friend class QWaylandSubSurface;
}; };

View File

@ -11,6 +11,7 @@ SUBDIRS += \
seatv5 \ seatv5 \
surface \ surface \
wl_connect \ wl_connect \
xdgdecorationv1 \
xdgoutput \ xdgoutput \
xdgshell \ xdgshell \
xdgshellv6 xdgshellv6

View File

@ -30,9 +30,8 @@
#include <QtGui/QRasterWindow> #include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow> #include <QtGui/QOpenGLWindow>
//TODO: move?
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtGui/QDrag>
using namespace MockCompositor; using namespace MockCompositor;
@ -60,6 +59,7 @@ private slots:
void pasteUtf8(); void pasteUtf8();
void destroysPreviousSelection(); void destroysPreviousSelection();
void destroysSelectionWithSurface(); void destroysSelectionWithSurface();
void dragWithoutFocus();
}; };
void tst_datadevicev1::initTestCase() void tst_datadevicev1::initTestCase()
@ -215,5 +215,22 @@ void tst_datadevicev1::destroysSelectionWithSurface()
QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0); QCOMPOSITOR_TRY_COMPARE(dataDevice()->m_sentSelectionOffers.size(), 0);
} }
// The application should not crash if it attempts to start a drag operation
// when it doesn't have input focus (QTBUG-76368)
void tst_datadevicev1::dragWithoutFocus()
{
QRasterWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
auto *mimeData = new QMimeData;
const QByteArray data("testData");
mimeData->setData("text/plain", data);
QDrag drag(&window);
drag.setMimeData(mimeData);
drag.exec();
}
QCOMPOSITOR_TEST_MAIN(tst_datadevicev1) QCOMPOSITOR_TEST_MAIN(tst_datadevicev1)
#include "tst_datadevicev1.moc" #include "tst_datadevicev1.moc"

View File

@ -72,6 +72,7 @@ private slots:
void usesEnterSerial(); void usesEnterSerial();
void focusDestruction(); void focusDestruction();
void mousePress(); void mousePress();
void mousePressFloat();
void simpleAxis_data(); void simpleAxis_data();
void simpleAxis(); void simpleAxis();
void invalidPointerEvents(); void invalidPointerEvents();
@ -208,6 +209,30 @@ void tst_seatv4::mousePress()
QTRY_VERIFY(window.m_pressed); QTRY_VERIFY(window.m_pressed);
} }
void tst_seatv4::mousePressFloat()
{
class Window : public QRasterWindow {
public:
void mousePressEvent(QMouseEvent *e) override { m_position = e->localPos(); }
QPointF m_position;
};
Window window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *surface = xdgSurface()->m_surface;
pointer()->sendEnter(surface, {32.75, 32.25});
pointer()->sendButton(client(), BTN_LEFT, 1);
pointer()->sendButton(client(), BTN_LEFT, 0);
});
QMargins m = window.frameMargins();
QPointF pressedPosition(32.75 -m.left(), 32.25 - m.top());
QTRY_COMPARE(window.m_position, pressedPosition);
}
void tst_seatv4::simpleAxis_data() void tst_seatv4::simpleAxis_data()
{ {
QTest::addColumn<uint>("axis"); QTest::addColumn<uint>("axis");

View File

@ -68,6 +68,7 @@ private slots:
// Touch tests // Touch tests
void createsTouch(); void createsTouch();
void singleTap(); void singleTap();
void singleTapFloat();
}; };
void tst_seatv5::bindsToSeat() void tst_seatv5::bindsToSeat()
@ -431,5 +432,36 @@ void tst_seatv5::singleTap()
} }
} }
void tst_seatv5::singleTapFloat()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([=] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32.75, 32.25}, 1);
t->sendFrame(c);
t->sendUp(c, 1);
t->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchBegin);
QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed);
QCOMPARE(e.touchPoints.length(), 1);
QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top()));
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchEnd);
QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased);
QCOMPARE(e.touchPoints.length(), 1);
QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top()));
}
}
QCOMPOSITOR_TEST_MAIN(tst_seatv5) QCOMPOSITOR_TEST_MAIN(tst_seatv5)
#include "tst_seatv5.moc" #include "tst_seatv5.moc"

View File

@ -0,0 +1,186 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "mockcompositor.h"
#include <qwayland-server-xdg-decoration-unstable-v1.h>
#include <QtGui/QRasterWindow>
#include <QtGui/QOpenGLWindow>
#include <QtGui/QClipboard>
#include <QtCore/private/qcore_unix_p.h>
#include <fcntl.h>
using namespace MockCompositor;
constexpr int xdgDecorationVersion = 1; // protocol VERSION, not the name suffix (_v1)
class XdgDecorationManagerV1;
class XdgToplevelDecorationV1 : public QObject, public QtWaylandServer::zxdg_toplevel_decoration_v1
{
Q_OBJECT
public:
explicit XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager, XdgToplevel *toplevel, int id, int version)
: zxdg_toplevel_decoration_v1(toplevel->resource()->client(), id, version)
, m_manager(manager)
, m_toplevel(toplevel)
{
}
void sendConfigure(mode mode)
{
if (!m_configureSent) {
// Attaching buffers before the configure is a protocol error
QVERIFY(!m_toplevel->surface()->m_pending.buffer);
QVERIFY(!m_toplevel->surface()->m_committed.buffer);
}
send_configure(mode);
m_configureSent = true;
}
void zxdg_toplevel_decoration_v1_destroy(Resource *resource) override
{
wl_resource_destroy(resource->handle);
}
void zxdg_toplevel_decoration_v1_destroy_resource(Resource *resource) override;
void zxdg_toplevel_decoration_v1_set_mode(Resource *resource, uint32_t mode) override
{
Q_UNUSED(resource);
m_unsetModeRequested = false;
m_requestedMode = XdgToplevelDecorationV1::mode(mode);
}
void zxdg_toplevel_decoration_v1_unset_mode(Resource *resource) override
{
Q_UNUSED(resource);
m_unsetModeRequested = true;
m_requestedMode = mode(0);
}
XdgDecorationManagerV1 *m_manager = nullptr;
XdgToplevel *m_toplevel = nullptr;
mode m_requestedMode = mode(0);
bool m_unsetModeRequested = false;
bool m_configureSent = false;
};
class XdgDecorationManagerV1 : public Global, public QtWaylandServer::zxdg_decoration_manager_v1
{
Q_OBJECT
public:
explicit XdgDecorationManagerV1(CoreCompositor *compositor, int version = 1)
: QtWaylandServer::zxdg_decoration_manager_v1(compositor->m_display, version)
, m_version(version)
{}
bool isClean() override { return m_decorations.empty(); }
XdgToplevelDecorationV1 *decorationFor(XdgToplevel *toplevel)
{
return m_decorations.value(toplevel, nullptr);
}
int m_version = 1; // TODO: Remove on libwayland upgrade
QMap<XdgToplevel *, XdgToplevelDecorationV1 *> m_decorations;
protected:
void zxdg_decoration_manager_v1_destroy(Resource *resource) override
{
//TODO: Should the decorations be destroyed at this point?
wl_resource_destroy(resource->handle);
}
void zxdg_decoration_manager_v1_get_toplevel_decoration(Resource *resource, uint32_t id, ::wl_resource *toplevelResource) override
{
auto *toplevel = fromResource<XdgToplevel>(toplevelResource);
QVERIFY(toplevel);
QVERIFY(!decorationFor(toplevel));
// Attaching buffers before the configure is a protocol error
QVERIFY(!toplevel->surface()->m_pending.buffer);
QVERIFY(!toplevel->surface()->m_committed.buffer);
m_decorations[toplevel] = new XdgToplevelDecorationV1(this, toplevel, id, resource->version());
}
};
void XdgToplevelDecorationV1::zxdg_toplevel_decoration_v1_destroy_resource(QtWaylandServer::zxdg_toplevel_decoration_v1::Resource *resource)
{
Q_UNUSED(resource);
int removed = m_manager->m_decorations.remove(m_toplevel);
Q_ASSERT(removed == 1);
delete this;
}
class XdgDecorationCompositor : public DefaultCompositor {
public:
explicit XdgDecorationCompositor()
{
exec([this] {
m_config.autoConfigure = true;
add<XdgDecorationManagerV1>(xdgDecorationVersion);
});
}
XdgToplevelDecorationV1 *toplevelDecoration(int i = 0) {
return get<XdgDecorationManagerV1>()->decorationFor(xdgToplevel(i));
}
};
class tst_xdgdecorationv1 : public QObject, private XdgDecorationCompositor
{
Q_OBJECT
private slots:
void initTestCase();
void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
void clientSidePreferredByCompositor();
};
void tst_xdgdecorationv1::initTestCase()
{
if (qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_WINDOWDECORATION"))
QSKIP("This test doesn't make sense when QT_WAYLAND_DISABLE_WINDOWDECORATION is set in the environment");
}
void tst_xdgdecorationv1::clientSidePreferredByCompositor()
{
QRasterWindow window;
window.show();
QCOMPOSITOR_TRY_COMPARE(get<XdgDecorationManagerV1>()->resourceMap().size(), 1);
QCOMPOSITOR_TRY_COMPARE(get<XdgDecorationManagerV1>()->resourceMap().first()->version(), xdgDecorationVersion);
QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()); // The client creates a toplevel object
// Check that we don't assume decorations before the server has configured them
QVERIFY(window.frameMargins().isNull());
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_unsetModeRequested);
QVERIFY(window.frameMargins().isNull()); // We're still waiting for a configure
exec([=] {
toplevelDecoration()->sendConfigure(XdgToplevelDecorationV1::mode_client_side);
xdgToplevel()->sendCompleteConfigure();
});
QTRY_VERIFY(!window.frameMargins().isNull());
}
QCOMPOSITOR_TEST_MAIN(tst_xdgdecorationv1)
#include "tst_xdgdecorationv1.moc"

View File

@ -0,0 +1,7 @@
include (../shared/shared.pri)
WAYLANDSERVERSOURCES += \
$$PWD/../../../../src/3rdparty/protocol/xdg-decoration-unstable-v1.xml
TARGET = tst_xdgdecorationv1
SOURCES += tst_xdgdecorationv1.cpp