Commit 9d40118f authored by Martin Flöser's avatar Martin Flöser
Browse files

[server] Add support for pointer input transformation

So far we only supported mapping global to surface-local coordinates
using a 2D-offset. With this change it's possible to register a
QMatrix4x4 to describe the transformation for going from global to
surface-local coordinates in a full 3D space.

The existing 2D-offset is transformed to use the new matrix based
variant describing a translation.

REVIEW: 126271
parent d96319b2
......@@ -59,6 +59,8 @@ private Q_SLOTS:
void testCapabilities_data();
void testCapabilities();
void testPointer();
void testPointerTransformation_data();
void testPointerTransformation();
void testPointerButton_data();
void testPointerButton();
void testCursor();
......@@ -426,6 +428,111 @@ void TestWaylandSeat::testPointer()
QVERIFY(!m_seatInterface->focusedPointerSurface());
}
void TestWaylandSeat::testPointerTransformation_data()
{
QTest::addColumn<QMatrix4x4>("enterTransformation");
// global position at 20/18
QTest::addColumn<QPointF>("expectedEnterPoint");
// global position at 10/16
QTest::addColumn<QPointF>("expectedMovePoint");
QMatrix4x4 tm;
tm.translate(-10, -15);
QTest::newRow("translation") << tm << QPointF(10, 3) << QPointF(0, 1);
QMatrix4x4 sm;
sm.scale(2, 2);
QTest::newRow("scale") << sm << QPointF(40, 36) << QPointF(20, 32);
QMatrix4x4 rotate;
rotate.rotate(90, 0, 0, 1);
QTest::newRow("rotate") << rotate << QPointF(-18, 20) << QPointF(-16, 10);
}
void TestWaylandSeat::testPointerTransformation()
{
using namespace KWayland::Client;
using namespace KWayland::Server;
QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged);
QVERIFY(pointerSpy.isValid());
m_seatInterface->setHasPointer(true);
QVERIFY(pointerSpy.wait());
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
QVERIFY(surfaceCreatedSpy.isValid());
Surface *s = m_compositor->createSurface(m_compositor);
QVERIFY(surfaceCreatedSpy.wait());
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWayland::Server::SurfaceInterface*>();
QVERIFY(serverSurface);
m_seatInterface->setPointerPos(QPoint(20, 18));
QFETCH(QMatrix4x4, enterTransformation);
m_seatInterface->setFocusedPointerSurface(serverSurface, enterTransformation);
QCOMPARE(m_seatInterface->focusedPointerSurfaceTransformation(), enterTransformation);
// no pointer yet
QVERIFY(m_seatInterface->focusedPointerSurface());
QVERIFY(!m_seatInterface->focusedPointer());
Pointer *p = m_seat->createPointer(m_seat);
const Pointer &cp = *p;
QVERIFY(p->isValid());
QSignalSpy pointerCreatedSpy(m_seatInterface, &SeatInterface::pointerCreated);
QVERIFY(pointerCreatedSpy.isValid());
// once the pointer is created it should be set as the focused pointer
QVERIFY(pointerCreatedSpy.wait());
QVERIFY(m_seatInterface->focusedPointer());
QCOMPARE(pointerCreatedSpy.first().first().value<PointerInterface*>(), m_seatInterface->focusedPointer());
m_seatInterface->setFocusedPointerSurface(nullptr);
serverSurface->client()->flush();
QTest::qWait(100);
QSignalSpy enteredSpy(p, &Pointer::entered);
QVERIFY(enteredSpy.isValid());
QSignalSpy leftSpy(p, &Pointer::left);
QVERIFY(leftSpy.isValid());
QSignalSpy motionSpy(p, &Pointer::motion);
QVERIFY(motionSpy.isValid());
QVERIFY(!p->enteredSurface());
QVERIFY(!cp.enteredSurface());
m_seatInterface->setFocusedPointerSurface(serverSurface, enterTransformation);
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
QVERIFY(enteredSpy.wait());
QCOMPARE(enteredSpy.first().first().value<quint32>(), m_display->serial());
QTEST(enteredSpy.first().last().toPointF(), "expectedEnterPoint");
PointerInterface *serverPointer = m_seatInterface->focusedPointer();
QVERIFY(serverPointer);
QCOMPARE(p->enteredSurface(), s);
QCOMPARE(cp.enteredSurface(), s);
// test motion
m_seatInterface->setTimestamp(1);
m_seatInterface->setPointerPos(QPoint(10, 16));
QVERIFY(motionSpy.wait());
QTEST(motionSpy.first().first().toPointF(), "expectedMovePoint");
QCOMPARE(motionSpy.first().last().value<quint32>(), quint32(1));
// leave the surface
m_seatInterface->setFocusedPointerSurface(nullptr);
QVERIFY(leftSpy.wait());
QCOMPARE(leftSpy.first().first().value<quint32>(), m_display->serial());
QVERIFY(!p->enteredSurface());
QVERIFY(!cp.enteredSurface());
// enter it again
m_seatInterface->setFocusedPointerSurface(serverSurface);
QVERIFY(enteredSpy.wait());
QCOMPARE(p->enteredSurface(), s);
QCOMPARE(cp.enteredSurface(), s);
delete s;
wl_display_flush(m_connection->display());
QTest::qWait(100);
QVERIFY(!m_seatInterface->focusedPointerSurface());
}
Q_DECLARE_METATYPE(Qt::MouseButton)
void TestWaylandSeat::testPointerButton_data()
......
......@@ -104,7 +104,7 @@ PointerInterface::PointerInterface(SeatInterface *parent, wl_resource *parentRes
connect(parent, &SeatInterface::pointerPosChanged, this, [this] {
Q_D();
if (d->focusedSurface && d->resource) {
const QPointF pos = d->seat->pointerPos() - d->seat->focusedPointerSurfacePosition();
const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos());
wl_pointer_send_motion(d->resource, d->seat->timestamp(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
}
......@@ -134,7 +134,7 @@ void PointerInterface::setFocusedSurface(SurfaceInterface *surface, quint32 seri
}
);
const QPointF pos = d->seat->pointerPos() - d->seat->focusedPointerSurfacePosition();
const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos());
wl_pointer_send_enter(d->resource, serial,
d->focusedSurface->resource(),
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
......
......@@ -482,6 +482,17 @@ SurfaceInterface *SeatInterface::focusedPointerSurface() const
}
void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QPointF &surfacePosition)
{
QMatrix4x4 m;
m.translate(-surfacePosition.x(), -surfacePosition.y());
setFocusedPointerSurface(surface, m);
Q_D();
if (d->globalPointer.focus.surface) {
d->globalPointer.focus.offset = surfacePosition;
}
}
void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QMatrix4x4 &transformation)
{
Q_D();
const quint32 serial = d->display->nextSerial();
......@@ -505,7 +516,8 @@ void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QP
d->globalPointer.focus = Private::Pointer::Focus();
}
);
d->globalPointer.focus.offset = surfacePosition;
d->globalPointer.focus.offset = QPointF();
d->globalPointer.focus.transformation = transformation;
d->globalPointer.focus.serial = serial;
}
if (!p) {
......@@ -525,6 +537,8 @@ void SeatInterface::setFocusedPointerSurfacePosition(const QPointF &surfacePosit
Q_D();
if (d->globalPointer.focus.surface) {
d->globalPointer.focus.offset = surfacePosition;
d->globalPointer.focus.transformation = QMatrix4x4();
d->globalPointer.focus.transformation.translate(-surfacePosition.x(), -surfacePosition.y());
}
}
......@@ -534,6 +548,20 @@ QPointF SeatInterface::focusedPointerSurfacePosition() const
return d->globalPointer.focus.offset;
}
void SeatInterface::setFocusedPointerSurfaceTransformation(const QMatrix4x4 &transformation)
{
Q_D();
if (d->globalPointer.focus.surface) {
d->globalPointer.focus.transformation = transformation;
}
}
QMatrix4x4 SeatInterface::focusedPointerSurfaceTransformation() const
{
Q_D();
return d->globalPointer.focus.transformation;
}
namespace {
static quint32 qtToWaylandButton(Qt::MouseButton button)
{
......
......@@ -22,6 +22,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include <QObject>
#include <QPoint>
#include <QMatrix4x4>
#include <KWayland/Server/kwaylandserver_export.h>
#include "global.h"
......@@ -157,6 +158,9 @@ public:
* Pointer motion events are adjusted to the local position based on the @p surfacePosition.
* If the surface changes it's position in the global coordinate system
* use setFocusedPointerSurfacePosition to update.
* The surface position is used to create the base transformation matrix to go from global
* to surface local coordinates. The default generated matrix is a translation with
* negative @p surfacePosition.
*
* @param surface The surface which should become the new focused pointer surface.
* @param surfacePosition The position of the surface in the global coordinate system
......@@ -166,8 +170,35 @@ public:
* @see focusedPointer
* @see setFocusedPointerSurfacePosition
* @see focusedPointerSurfacePosition
* @see setFocusedPointerSurfaceTransformation
* @see focusedPointerSurfaceTransformation
**/
void setFocusedPointerSurface(SurfaceInterface *surface, const QPointF &surfacePosition = QPoint());
/**
* Sets the focused pointer @p surface.
* All pointer events will be sent to the @p surface till a new focused pointer surface gets
* installed. When the focus pointer surface changes a leave event is sent to the previous
* focused surface.
*
* To unset the focused pointer surface pass @c nullptr as @p surface.
*
* Pointer motion events are adjusted to the local position based on the @p transformation.
* If the surface changes it's position in the global coordinate system
* use setFocusedPointerSurfaceTransformation to update.
*
* @param surface The surface which should become the new focused pointer surface.
* @param transformation The transformation to transform global into local coordinates
*
* @see setPointerPos
* @see focucedPointerSurface
* @see focusedPointer
* @see setFocusedPointerSurfacePosition
* @see focusedPointerSurfacePosition
* @see setFocusedPointerSurfaceTransformation
* @see focusedPointerSurfaceTransformation
* @since 5.6
**/
void setFocusedPointerSurface(SurfaceInterface *surface, const QMatrix4x4 &transformation);
/**
* @returns The currently focused pointer surface, that is the surface receiving pointer events.
* @see setFocusedPointerSurface
......@@ -179,19 +210,45 @@ public:
**/
PointerInterface *focusedPointer() const;
/**
* Updates the global position of the currently focused pointer surface
* Updates the global position of the currently focused pointer surface.
*
* Updating the focused surface position also generates a new transformation matrix.
* The default generated matrix is a translation with negative @p surfacePosition.
* If a different transformation is required a dedicated call to
* @link setFocusedPointerSurfaceTransformation is required.
*
* @param surfacePosition The new global position of the focused pointer surface
* @see focusedPointerSurface
* @see setFocusedPointerSurface
* @see focusedPointerSurfaceTransformation
* @see setFocusedPointerSurfaceTransformation
**/
void setFocusedPointerSurfacePosition(const QPointF &surfacePosition);
/**
* @returns The position of the focused pointer surface in global coordinates.
* @see setFocusedPointerSurfacePosition
* @see setFocusedPointerSurface
* @see focusedPointerSurfaceTransformation
**/
QPointF focusedPointerSurfacePosition() const;
/**
* Sets the @p transformation for going from global to local coordinates.
*
* The default transformation gets generated from the surface position and reset whenever
* the surface position changes.
*
* @see focusedPointerSurfaceTransformation
* @see focusedPointerSurfacePosition
* @see setFocusedPointerSurfacePosition
* @since 5.6
**/
void setFocusedPointerSurfaceTransformation(const QMatrix4x4 &transformation);
/**
* @returns The transformation applied to pointer position to go from global to local coordinates.
* @see setFocusedPointerSurfaceTransformation
* @since 5.6
**/
QMatrix4x4 focusedPointerSurfaceTransformation() const;
/**
* Marks the @p button as pressed.
*
......
......@@ -74,6 +74,7 @@ public:
PointerInterface *pointer = nullptr;
QMetaObject::Connection destroyConnection;
QPointF offset = QPointF();
QMatrix4x4 transformation;
quint32 serial = 0;
};
Focus focus;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment