summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2017-09-28 16:21:21 +0200
committerJohan Helsing <johan.helsing@qt.io>2017-10-18 11:39:25 +0000
commitfa7a1f77226795ece274b3505655e522881a0e24 (patch)
tree3e3e730642b9f03dbdbbd385ae1a4900f5b7cb19
parent3876d75305d3a1d2967c887b9591a327f726d91e (diff)
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 <allan.jensen@qt.io> Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
-rw-r--r--src/compositor/compositor_api/qwaylandpointer.cpp155
-rw-r--r--src/compositor/compositor_api/qwaylandpointer.h2
-rw-r--r--src/compositor/compositor_api/qwaylandpointer_p.h8
-rw-r--r--src/compositor/compositor_api/qwaylandseat.cpp17
-rw-r--r--src/compositor/compositor_api/qwaylandseat.h3
-rw-r--r--src/compositor/compositor_api/qwaylandview.cpp3
-rw-r--r--tests/auto/compositor/compositor/compositor.pro6
-rw-r--r--tests/auto/compositor/compositor/mockpointer.cpp97
-rw-r--r--tests/auto/compositor/compositor/mockpointer.h47
-rw-r--r--tests/auto/compositor/compositor/mockseat.cpp1
-rw-r--r--tests/auto/compositor/compositor/mockseat.h7
-rw-r--r--tests/auto/compositor/compositor/tst_compositor.cpp56
12 files changed, 324 insertions, 78 deletions
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<QtWaylandServer::wl_pointer::Resource *> 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<uint32_t>(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<Resource *> 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<QWaylandSurface> 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<MockPointer *>(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<MockPointer *>(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 <QObject>
+#include <wayland-client.h>
+
+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 <QObject>
#include <wayland-client.h>
@@ -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<MockPointer> 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: