From fa7a1f77226795ece274b3505655e522881a0e24 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Thu, 28 Sep 2017 16:21:21 +0200 Subject: Fix crash after destroying view with mouse focus Don't emit QWaylandSeat::mouseFocusChanged with a destroyed QWaylandView. QWaylandPointer has been refactored to make it easier to follow enter and leave logic. A missing emit for buttonPressedChanged has been fixed as well. This also adds a test for pointer events to verify that setting mouse focus works and that the crash has been fixed. Task-number: QTBUG-63208 Change-Id: Id0c174a7b609dfd0152f3ae446dd51fd8befd554 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Paul Olav Tvete --- src/compositor/compositor_api/qwaylandpointer.cpp | 155 +++++++++++---------- src/compositor/compositor_api/qwaylandpointer.h | 2 +- src/compositor/compositor_api/qwaylandpointer_p.h | 8 +- src/compositor/compositor_api/qwaylandseat.cpp | 17 +++ src/compositor/compositor_api/qwaylandseat.h | 3 + src/compositor/compositor_api/qwaylandview.cpp | 3 - tests/auto/compositor/compositor/compositor.pro | 6 +- tests/auto/compositor/compositor/mockpointer.cpp | 97 +++++++++++++ tests/auto/compositor/compositor/mockpointer.h | 47 +++++++ tests/auto/compositor/compositor/mockseat.cpp | 1 + tests/auto/compositor/compositor/mockseat.h | 7 + .../auto/compositor/compositor/tst_compositor.cpp | 56 ++++++++ 12 files changed, 324 insertions(+), 78 deletions(-) create mode 100644 tests/auto/compositor/compositor/mockpointer.cpp create mode 100644 tests/auto/compositor/compositor/mockpointer.h diff --git a/src/compositor/compositor_api/qwaylandpointer.cpp b/src/compositor/compositor_api/qwaylandpointer.cpp index d1ca174db..bf4bf547d 100644 --- a/src/compositor/compositor_api/qwaylandpointer.cpp +++ b/src/compositor/compositor_api/qwaylandpointer.cpp @@ -51,31 +51,77 @@ QWaylandPointerPrivate::QWaylandPointerPrivate(QWaylandPointer *pointer, QWaylan , wl_pointer() , seat(seat) , output() - , hasSentEnter(false) , enterSerial(0) , buttonCount() { Q_UNUSED(pointer); } -const QList QWaylandPointerPrivate::pointerResourcesForFocusedSurface() const -{ - if (!seat->mouseFocus()) - return {}; - - return resourceMap().values(seat->mouseFocus()->surfaceResource()->client); -} - uint QWaylandPointerPrivate::sendButton(Qt::MouseButton button, uint32_t state) { Q_Q(QWaylandPointer); + if (!q->mouseFocus() || !q->mouseFocus()->surface()) + return 0; + + wl_client *client = q->mouseFocus()->surface()->waylandClient(); uint32_t time = compositor()->currentTimeMsecs(); uint32_t serial = compositor()->nextSerial(); - for (auto resource : pointerResourcesForFocusedSurface()) + for (auto resource : resourceMap().values(client)) send_button(resource->handle, serial, time, q->toWaylandButton(button), state); return serial; } +void QWaylandPointerPrivate::sendMotion() +{ + Q_ASSERT(enteredSurface); + uint32_t time = compositor()->currentTimeMsecs(); + wl_fixed_t x = wl_fixed_from_double(localPosition.x()); + wl_fixed_t y = wl_fixed_from_double(localPosition.y()); + for (auto resource : resourceMap().values(enteredSurface->waylandClient())) + wl_pointer_send_motion(resource->handle, time, x, y); +} + +void QWaylandPointerPrivate::sendEnter(QWaylandSurface *surface) +{ + Q_ASSERT(surface && !enteredSurface); + enterSerial = compositor()->nextSerial(); + + QWaylandKeyboard *keyboard = seat->keyboard(); + if (keyboard) + keyboard->sendKeyModifiers(surface->client(), enterSerial); + + wl_fixed_t x = wl_fixed_from_double(localPosition.x()); + wl_fixed_t y = wl_fixed_from_double(localPosition.y()); + for (auto resource : resourceMap().values(surface->waylandClient())) + send_enter(resource->handle, enterSerial, surface->resource(), x, y); + + enteredSurface = surface; + enteredSurfaceDestroyListener.listenForDestruction(surface->resource()); +} + +void QWaylandPointerPrivate::sendLeave() +{ + Q_ASSERT(enteredSurface); + uint32_t serial = compositor()->nextSerial(); + for (auto resource : resourceMap().values(enteredSurface->waylandClient())) + send_leave(resource->handle, serial, enteredSurface->resource()); + enteredSurface = nullptr; + localPosition = QPointF(); + enteredSurfaceDestroyListener.reset(); +} + +void QWaylandPointerPrivate::ensureEntered(QWaylandSurface *surface) +{ + if (enteredSurface == surface) + return; + + if (enteredSurface) + sendLeave(); + + if (surface) + sendEnter(surface); +} + void QWaylandPointerPrivate::pointer_release(wl_pointer::Resource *resource) { wl_resource_destroy(resource->handle); @@ -122,7 +168,7 @@ void QWaylandPointerPrivate::pointer_set_cursor(wl_pointer::Resource *resource, QWaylandPointer::QWaylandPointer(QWaylandSeat *seat, QObject *parent) : QWaylandObject(* new QWaylandPointerPrivate(this, seat), parent) { - connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired, this, &QWaylandPointer::focusDestroyed); + connect(&d_func()->enteredSurfaceDestroyListener, &QWaylandDestroyListener::fired, this, &QWaylandPointer::enteredSurfaceDestroyed); connect(seat, &QWaylandSeat::mouseFocusChanged, this, &QWaylandPointer::pointerFocusChanged); } @@ -173,16 +219,11 @@ uint QWaylandPointer::sendMousePressEvent(Qt::MouseButton button) { Q_D(QWaylandPointer); d->buttonCount++; - uint serial = 0; - - if (d->seat->mouseFocus()) - serial = d->sendButton(button, WL_POINTER_BUTTON_STATE_PRESSED); - if (d->buttonCount == 1) { + if (d->buttonCount == 1) emit buttonPressedChanged(); - } - return serial; + return d->sendButton(button, WL_POINTER_BUTTON_STATE_PRESSED); } /*! @@ -194,15 +235,11 @@ uint QWaylandPointer::sendMouseReleaseEvent(Qt::MouseButton button) { Q_D(QWaylandPointer); d->buttonCount--; - uint serial = 0; - - if (d->seat->mouseFocus()) - serial = d->sendButton(button, WL_POINTER_BUTTON_STATE_RELEASED); if (d->buttonCount == 0) emit buttonPressedChanged(); - return serial; + return d->sendButton(button, WL_POINTER_BUTTON_STATE_RELEASED); } /*! @@ -218,41 +255,20 @@ void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &loca d->localPosition = localPos; d->spacePosition = outputSpacePos; - //we adjust if the mouse position is on the edge - //to work around Qt's event propagation - if (view && view->surface()) { + if (view) { + // We adjust if the mouse position is on the edge + // to work around Qt's event propagation QSizeF size(view->surface()->size()); - if (d->localPosition.x() == size.width()) + if (d->localPosition.x() == size.width()) d->localPosition.rx() -= 0.01; - if (d->localPosition.y() == size.height()) d->localPosition.ry() -= 0.01; - } - - if (!d->hasSentEnter) { - d->enterSerial = d->compositor()->nextSerial(); - QWaylandKeyboard *keyboard = d->seat->keyboard(); - if (keyboard) - keyboard->sendKeyModifiers(view->surface()->client(), d->enterSerial); - for (auto resource : d->pointerResourcesForFocusedSurface()) { - d->send_enter(resource->handle, d->enterSerial, view->surface()->resource(), - wl_fixed_from_double(d->localPosition.x()), - wl_fixed_from_double(d->localPosition.y())); - } - d->focusDestroyListener.listenForDestruction(view->surface()->resource()); - d->hasSentEnter = true; - } - if (view && view->output()) - setOutput(view->output()); - - uint32_t time = d->compositor()->currentTimeMsecs(); + d->ensureEntered(view->surface()); + d->sendMotion(); - if (d->seat->mouseFocus()) { - wl_fixed_t x = wl_fixed_from_double(currentLocalPosition().x()); - wl_fixed_t y = wl_fixed_from_double(currentLocalPosition().y()); - for (auto resource : d->pointerResourcesForFocusedSurface()) - wl_pointer_send_motion(resource->handle, time, x, y); + if (view->output()) + setOutput(view->output()); } } @@ -262,14 +278,14 @@ void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &loca void QWaylandPointer::sendMouseWheelEvent(Qt::Orientation orientation, int delta) { Q_D(QWaylandPointer); - if (!d->seat->mouseFocus()) + if (!d->enteredSurface) return; uint32_t time = d->compositor()->currentTimeMsecs(); uint32_t axis = orientation == Qt::Horizontal ? WL_POINTER_AXIS_HORIZONTAL_SCROLL : WL_POINTER_AXIS_VERTICAL_SCROLL; - for (auto resource : d->pointerResourcesForFocusedSurface()) + for (auto resource : d->resourceMap().values(d->enteredSurface->waylandClient())) d->send_axis(resource->handle, time, axis, wl_fixed_from_int(-delta / 12)); } @@ -316,9 +332,8 @@ void QWaylandPointer::addClient(QWaylandClient *client, uint32_t id, uint32_t ve { Q_D(QWaylandPointer); wl_resource *resource = d->add(client->client(), id, qMin(QtWaylandServer::wl_pointer::interfaceVersion(), version))->handle; - QWaylandView *focus = d->seat->mouseFocus(); - if (focus && client == focus->surface()->client()) { - d->send_enter(resource, d->enterSerial, focus->surfaceResource(), + if (d->enteredSurface && client == d->enteredSurface->client()) { + d->send_enter(resource, d->enterSerial, d->enteredSurface->resource(), wl_fixed_from_double(d->localPosition.x()), wl_fixed_from_double(d->localPosition.y())); } @@ -389,14 +404,19 @@ uint32_t QWaylandPointer::toWaylandButton(Qt::MouseButton button) /*! * \internal */ -void QWaylandPointer::focusDestroyed(void *data) +void QWaylandPointer::enteredSurfaceDestroyed(void *data) { Q_D(QWaylandPointer); Q_UNUSED(data) - d->focusDestroyListener.reset(); + d->enteredSurfaceDestroyListener.reset(); + d->enteredSurface = nullptr; + + d->seat->setMouseFocus(nullptr); - d->seat->setMouseFocus(Q_NULLPTR); - d->buttonCount = 0; + if (d->buttonCount != 0) { + d->buttonCount = 0; + emit buttonPressedChanged(); + } } /*! @@ -404,16 +424,11 @@ void QWaylandPointer::focusDestroyed(void *data) */ void QWaylandPointer::pointerFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus) { - Q_UNUSED(newFocus); Q_D(QWaylandPointer); - d->localPosition = QPointF(); - d->hasSentEnter = false; - if (oldFocus) { - uint32_t serial = d->compositor()->nextSerial(); - for (auto resource : d->resourceMap().values(oldFocus->surfaceResource()->client)) - d->send_leave(resource->handle, serial, oldFocus->surfaceResource()); - d->focusDestroyListener.reset(); - } + Q_UNUSED(oldFocus); + bool wasSameSurface = newFocus && newFocus->surface() == d->enteredSurface; + if (d->enteredSurface && !wasSameSurface) + d->sendLeave(); } QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandpointer.h b/src/compositor/compositor_api/qwaylandpointer.h index bcba1d6a3..409bcc83a 100644 --- a/src/compositor/compositor_api/qwaylandpointer.h +++ b/src/compositor/compositor_api/qwaylandpointer.h @@ -89,7 +89,7 @@ Q_SIGNALS: void buttonPressedChanged(); private: - void focusDestroyed(void *data); + void enteredSurfaceDestroyed(void *data); void pointerFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus); }; diff --git a/src/compositor/compositor_api/qwaylandpointer_p.h b/src/compositor/compositor_api/qwaylandpointer_p.h index 0a9ef2329..11076f34b 100644 --- a/src/compositor/compositor_api/qwaylandpointer_p.h +++ b/src/compositor/compositor_api/qwaylandpointer_p.h @@ -86,11 +86,15 @@ protected: void pointer_release(Resource *resource) override; private: - const QList pointerResourcesForFocusedSurface() const; uint sendButton(Qt::MouseButton button, uint32_t state); + void sendMotion(); + void sendEnter(QWaylandSurface *surface); + void sendLeave(); + void ensureEntered(QWaylandSurface *surface); QWaylandSeat *seat; QWaylandOutput *output; + QPointer enteredSurface; QPointF localPosition; QPointF spacePosition; @@ -100,7 +104,7 @@ private: int buttonCount; - QWaylandDestroyListener focusDestroyListener; + QWaylandDestroyListener enteredSurfaceDestroyListener; static QWaylandSurfaceRole s_role; }; diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp index 80b75d617..b4b45392c 100644 --- a/src/compositor/compositor_api/qwaylandseat.cpp +++ b/src/compositor/compositor_api/qwaylandseat.cpp @@ -456,6 +456,12 @@ void QWaylandSeat::setMouseFocus(QWaylandView *view) QWaylandView *oldFocus = d->mouseFocus; d->mouseFocus = view; + + if (oldFocus) + disconnect(oldFocus, &QObject::destroyed, this, &QWaylandSeat::handleMouseFocusDestroyed); + if (d->mouseFocus) + connect(d->mouseFocus, &QObject::destroyed, this, &QWaylandSeat::handleMouseFocusDestroyed); + emit mouseFocusChanged(d->mouseFocus, oldFocus); } @@ -512,4 +518,15 @@ QWaylandSeat *QWaylandSeat::fromSeatResource(struct ::wl_resource *resource) * This signal is emitted when the mouse focus has changed from \a oldFocus to \a newFocus. */ +void QWaylandSeat::handleMouseFocusDestroyed() +{ + // This is triggered when the QWaylandView is destroyed, NOT the surface. + // ... so this is for the rare case when the view that currently holds the mouse focus is + // destroyed before its surface + Q_D(QWaylandSeat); + d->mouseFocus = nullptr; + QWaylandView *oldFocus = nullptr; // we have to send nullptr because the old focus is already destroyed at this point + emit mouseFocusChanged(d->mouseFocus, oldFocus); +} + QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandseat.h b/src/compositor/compositor_api/qwaylandseat.h index 86cf250a6..e5ef46dc6 100644 --- a/src/compositor/compositor_api/qwaylandseat.h +++ b/src/compositor/compositor_api/qwaylandseat.h @@ -133,6 +133,9 @@ Q_SIGNALS: void mouseFocusChanged(QWaylandView *newFocus, QWaylandView *oldFocus); void keyboardFocusChanged(QWaylandSurface *newFocus, QWaylandSurface *oldFocus); void cursorSurfaceRequest(QWaylandSurface *surface, int hotspotX, int hotspotY); + +private: + void handleMouseFocusDestroyed(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandSeat::CapabilityFlags) diff --git a/src/compositor/compositor_api/qwaylandview.cpp b/src/compositor/compositor_api/qwaylandview.cpp index 47996a5a0..9e8297574 100644 --- a/src/compositor/compositor_api/qwaylandview.cpp +++ b/src/compositor/compositor_api/qwaylandview.cpp @@ -99,9 +99,6 @@ QWaylandView::~QWaylandView() if (d->surface) { if (d->output) QWaylandOutputPrivate::get(d->output)->removeView(this, d->surface); - QWaylandSeat *i = d->surface->compositor()->defaultSeat(); - if (i->mouseFocus() == this) - i->setMouseFocus(Q_NULLPTR); QWaylandSurfacePrivate::get(d->surface)->derefView(this); } diff --git a/tests/auto/compositor/compositor/compositor.pro b/tests/auto/compositor/compositor/compositor.pro index f0cdaf32c..112e2e74f 100644 --- a/tests/auto/compositor/compositor/compositor.pro +++ b/tests/auto/compositor/compositor/compositor.pro @@ -20,11 +20,13 @@ SOURCES += \ testkeyboardgrabber.cpp \ mockclient.cpp \ mockseat.cpp \ - testseat.cpp + testseat.cpp \ + mockpointer.cpp HEADERS += \ testcompositor.h \ testkeyboardgrabber.h \ mockclient.h \ mockseat.h \ - testseat.h + testseat.h \ + mockpointer.h diff --git a/tests/auto/compositor/compositor/mockpointer.cpp b/tests/auto/compositor/compositor/mockpointer.cpp new file mode 100644 index 000000000..6c51d8bd1 --- /dev/null +++ b/tests/auto/compositor/compositor/mockpointer.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "mockpointer.h" + +static void pointerEnter(void *pointer, struct wl_pointer *wlPointer, uint serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) +{ + Q_UNUSED(wlPointer); + Q_UNUSED(serial); + Q_UNUSED(x); + Q_UNUSED(y); + + static_cast(pointer)->m_enteredSurface = surface; +} + +static void pointerLeave(void *pointer, struct wl_pointer *wlPointer, uint32_t serial, struct wl_surface *surface) +{ + Q_UNUSED(pointer); + Q_UNUSED(wlPointer); + Q_UNUSED(serial); + + Q_ASSERT(surface); + + static_cast(pointer)->m_enteredSurface = nullptr; +} + +static void pointerMotion(void *pointer, struct wl_pointer *wlPointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) +{ + Q_UNUSED(pointer); + Q_UNUSED(wlPointer); + Q_UNUSED(time); + Q_UNUSED(x); + Q_UNUSED(y); +} + +static void pointerButton(void *pointer, struct wl_pointer *wlPointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + Q_UNUSED(pointer); + Q_UNUSED(wlPointer); + Q_UNUSED(serial); + Q_UNUSED(time); + Q_UNUSED(button); + Q_UNUSED(state); +} + +static void pointerAxis(void *pointer, struct wl_pointer *wlPointer, uint32_t time, uint32_t axis, wl_fixed_t value) +{ + Q_UNUSED(pointer); + Q_UNUSED(wlPointer); + Q_UNUSED(time); + Q_UNUSED(axis); + Q_UNUSED(value); +} + +static const struct wl_pointer_listener pointerListener = { + pointerEnter, + pointerLeave, + pointerMotion, + pointerButton, + pointerAxis, +}; + +MockPointer::MockPointer(wl_seat *seat) + : m_pointer(wl_seat_get_pointer(seat)) +{ + wl_pointer_add_listener(m_pointer, &pointerListener, this); +} + +MockPointer::~MockPointer() +{ + wl_pointer_destroy(m_pointer); +} diff --git a/tests/auto/compositor/compositor/mockpointer.h b/tests/auto/compositor/compositor/mockpointer.h new file mode 100644 index 000000000..353861415 --- /dev/null +++ b/tests/auto/compositor/compositor/mockpointer.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef MOCKPOINTER_H +#define MOCKPOINTER_H + +#include +#include + +class MockPointer : public QObject +{ + Q_OBJECT + +public: + MockPointer(wl_seat *seat); + ~MockPointer(); + + wl_pointer *m_pointer = nullptr; + wl_surface *m_enteredSurface = nullptr; +}; + +#endif // MOCKPOINTER_H diff --git a/tests/auto/compositor/compositor/mockseat.cpp b/tests/auto/compositor/compositor/mockseat.cpp index 29c4a4e13..052c2f90b 100644 --- a/tests/auto/compositor/compositor/mockseat.cpp +++ b/tests/auto/compositor/compositor/mockseat.cpp @@ -30,6 +30,7 @@ MockSeat::MockSeat(wl_seat *seat) : m_seat(seat) + , m_pointer(new MockPointer(seat)) { // Bind to the keyboard interface so that the compositor has // the right resource associations diff --git a/tests/auto/compositor/compositor/mockseat.h b/tests/auto/compositor/compositor/mockseat.h index 1c6d324c2..e6d5e0b1c 100644 --- a/tests/auto/compositor/compositor/mockseat.h +++ b/tests/auto/compositor/compositor/mockseat.h @@ -28,6 +28,8 @@ #ifndef MOCKSEAT #define MOCKSEAT +#include "mockpointer.h" + #include #include @@ -38,8 +40,13 @@ class MockSeat : public QObject public: MockSeat(wl_seat *seat); ~MockSeat(); + MockPointer *pointer() const { return m_pointer.data(); } wl_seat *m_seat; wl_keyboard *m_keyboard; + +private: + QScopedPointer m_pointer; }; + #endif diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index 3d7d2dab9..6f00b6e61 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -27,6 +27,8 @@ ****************************************************************************/ #include "mockclient.h" +#include "mockseat.h" +#include "mockpointer.h" #include "testcompositor.h" #include "testkeyboardgrabber.h" #include "testseat.h" @@ -56,6 +58,7 @@ private slots: void keyboardGrab(); void seatCreation(); void seatKeyboardFocus(); + void seatMouseFocus(); void singleClient(); void multipleClients(); void geometry(); @@ -438,6 +441,59 @@ void tst_WaylandCompositor::seatKeyboardFocus() QTRY_VERIFY(!compositor.defaultSeat()->keyboardFocus()); } +void tst_WaylandCompositor::seatMouseFocus() +{ + TestCompositor compositor(true); + compositor.create(); + + // Create client after all the seats have been set up as the mock client + // does not dynamically listen to new seats + MockClient client; + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + auto view = new QWaylandView; + view->setSurface(waylandSurface); + + QWaylandSeat* seat = compositor.defaultSeat(); + seat->setMouseFocus(view); + seat->sendMouseMoveEvent(view, QPointF(10, 10), QPointF(100, 100)); + + compositor.flushClients(); + + QTRY_VERIFY(seat->mouseFocus()); + QTRY_VERIFY(seat->pointer()); + QTRY_COMPARE(seat->mouseFocus()->surface(), waylandSurface); + + QTRY_COMPARE(client.m_seats.size(), 1); + MockPointer *mockPointer = client.m_seats.first()->pointer(); + QVERIFY(mockPointer); + QTRY_COMPARE(mockPointer->m_enteredSurface, surface); + + delete view; + + compositor.flushClients(); + + QTRY_COMPARE(mockPointer->m_enteredSurface, nullptr); + QTRY_VERIFY(!compositor.defaultSeat()->mouseFocus()); + + view = new QWaylandView; + view->setSurface(waylandSurface); + seat->sendMouseMoveEvent(view, QPointF(10, 10), QPointF(100, 100)); + QTRY_COMPARE(compositor.defaultSeat()->mouseFocus(), view); + + compositor.flushClients(); + + QTRY_COMPARE(mockPointer->m_enteredSurface, surface); + + wl_surface_destroy(surface); + QTRY_VERIFY(compositor.surfaces.size() == 0); + QTRY_VERIFY(!compositor.defaultSeat()->mouseFocus()); + + delete view; +} + class XdgTestCompositor: public TestCompositor { Q_OBJECT public: -- cgit v1.2.3