Commit ddff2c06 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

Allow compositors to send discrete axis values

Summary:
So far not all v5 features were implemented because most of them are
optional. But given that XWayland needs axis_discrete event maybe it's
time to implement them.

CCBUG: 404152

Reviewers: #kwin, davidedmundson

Reviewed By: #kwin, davidedmundson

Subscribers: davidedmundson, mthw, kde-frameworks-devel

Tags: #frameworks

Differential Revision: https://phabricator.kde.org/D18933
parent 8e3afee4
......@@ -78,6 +78,7 @@ private Q_SLOTS:
void testPointerSwipeGesture();
void testPointerPinchGesture_data();
void testPointerPinchGesture();
void testPointerAxis();
void testKeyboardSubSurfaceTreeFromPointer();
void testCursor();
void testCursorDamage();
......@@ -1151,6 +1152,103 @@ void TestWaylandSeat::testPointerPinchGesture()
QVERIFY(spy->wait());
}
void TestWaylandSeat::testPointerAxis()
{
using namespace KWayland::Client;
using namespace KWayland::Server;
// first create the pointer
QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged);
QVERIFY(hasPointerChangedSpy.isValid());
m_seatInterface->setHasPointer(true);
QVERIFY(hasPointerChangedSpy.wait());
QScopedPointer<Pointer> pointer(m_seat->createPointer());
QVERIFY(pointer);
// now create a surface
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
QVERIFY(surfaceCreatedSpy.isValid());
QScopedPointer<Surface> surface(m_compositor->createSurface());
QVERIFY(surfaceCreatedSpy.wait());
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
QVERIFY(serverSurface);
m_seatInterface->setFocusedPointerSurface(serverSurface);
QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface);
QVERIFY(m_seatInterface->focusedPointer());
QSignalSpy frameSpy(pointer.data(), &Pointer::frame);
QVERIFY(frameSpy.isValid());
QVERIFY(frameSpy.wait());
QCOMPARE(frameSpy.count(), 1);
// let's scroll vertically
QSignalSpy axisSourceSpy(pointer.data(), &Pointer::axisSourceChanged);
QVERIFY(axisSourceSpy.isValid());
QSignalSpy axisSpy(pointer.data(), &Pointer::axisChanged);
QVERIFY(axisSpy.isValid());
QSignalSpy axisDiscreteSpy(pointer.data(), &Pointer::axisDiscreteChanged);
QVERIFY(axisDiscreteSpy.isValid());
QSignalSpy axisStoppedSpy(pointer.data(), &Pointer::axisStopped);
QVERIFY(axisStoppedSpy.isValid());
quint32 timestamp = 1;
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxisV5(Qt::Vertical, 10, 1, PointerAxisSource::Wheel);
QVERIFY(frameSpy.wait());
QCOMPARE(frameSpy.count(), 2);
QCOMPARE(axisSourceSpy.count(), 1);
QCOMPARE(axisSourceSpy.last().at(0).value<Pointer::AxisSource>(), Pointer::AxisSource::Wheel);
QCOMPARE(axisDiscreteSpy.count(), 1);
QCOMPARE(axisDiscreteSpy.last().at(0).value<Pointer::Axis>(), Pointer::Axis::Vertical);
QCOMPARE(axisDiscreteSpy.last().at(1).value<qint32>(), 1);
QCOMPARE(axisSpy.count(), 1);
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(1));
QCOMPARE(axisSpy.last().at(1).value<Pointer::Axis>(), Pointer::Axis::Vertical);
QCOMPARE(axisSpy.last().at(2).value<qreal>(), 10.0);
QCOMPARE(axisStoppedSpy.count(), 0);
// let's scroll using fingers
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxisV5(Qt::Horizontal, 42, 0, PointerAxisSource::Finger);
QVERIFY(frameSpy.wait());
QCOMPARE(frameSpy.count(), 3);
QCOMPARE(axisSourceSpy.count(), 2);
QCOMPARE(axisSourceSpy.last().at(0).value<Pointer::AxisSource>(), Pointer::AxisSource::Finger);
QCOMPARE(axisDiscreteSpy.count(), 1);
QCOMPARE(axisSpy.count(), 2);
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(2));
QCOMPARE(axisSpy.last().at(1).value<Pointer::Axis>(), Pointer::Axis::Horizontal);
QCOMPARE(axisSpy.last().at(2).value<qreal>(), 42.0);
QCOMPARE(axisStoppedSpy.count(), 0);
// lift the fingers off the device
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxisV5(Qt::Horizontal, 0, 0, PointerAxisSource::Finger);
QVERIFY(frameSpy.wait());
QCOMPARE(frameSpy.count(), 4);
QCOMPARE(axisSourceSpy.count(), 3);
QCOMPARE(axisSourceSpy.last().at(0).value<Pointer::AxisSource>(), Pointer::AxisSource::Finger);
QCOMPARE(axisDiscreteSpy.count(), 1);
QCOMPARE(axisSpy.count(), 2);
QCOMPARE(axisStoppedSpy.count(), 1);
QCOMPARE(axisStoppedSpy.last().at(0).value<quint32>(), 3);
QCOMPARE(axisStoppedSpy.last().at(1).value<Pointer::Axis>(), Pointer::Axis::Horizontal);
// if the device is unknown, no axis_source event should be sent
m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->pointerAxisV5(Qt::Horizontal, 42, 1, PointerAxisSource::Unknown);
QVERIFY(frameSpy.wait());
QCOMPARE(frameSpy.count(), 5);
QCOMPARE(axisSourceSpy.count(), 3);
QCOMPARE(axisDiscreteSpy.count(), 2);
QCOMPARE(axisDiscreteSpy.last().at(0).value<Pointer::Axis>(), Pointer::Axis::Horizontal);
QCOMPARE(axisDiscreteSpy.last().at(1).value<qint32>(), 1);
QCOMPARE(axisSpy.count(), 3);
QCOMPARE(axisSpy.last().at(0).value<quint32>(), quint32(4));
QCOMPARE(axisSpy.last().at(1).value<Pointer::Axis>(), Pointer::Axis::Horizontal);
QCOMPARE(axisSpy.last().at(2).value<qreal>(), 42.0);
QCOMPARE(axisStoppedSpy.count(), 1);
}
void TestWaylandSeat::testKeyboardSubSurfaceTreeFromPointer()
{
// this test verifies that when clicking on a sub-surface the keyboard focus passes to it
......
......@@ -31,6 +31,18 @@ namespace KWayland
namespace Client
{
static Pointer::Axis wlAxisToPointerAxis(uint32_t axis)
{
switch (axis) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
return Pointer::Axis::Vertical;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
return Pointer::Axis::Horizontal;
}
Q_UNREACHABLE();
}
class Q_DECL_HIDDEN Pointer::Private
{
public:
......@@ -164,14 +176,7 @@ void Pointer::Private::axisCallback(void *data, wl_pointer *pointer, uint32_t ti
{
auto p = reinterpret_cast<Pointer::Private*>(data);
Q_ASSERT(p->pointer == pointer);
auto toAxis = [axis] {
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
return Axis::Horizontal;
} else {
return Axis::Vertical;
}
};
emit p->q->axisChanged(time, toAxis(), wl_fixed_to_double(value));
emit p->q->axisChanged(time, wlAxisToPointerAxis(axis), wl_fixed_to_double(value));
}
void Pointer::Private::frameCallback(void *data, wl_pointer *pointer)
......@@ -183,28 +188,41 @@ void Pointer::Private::frameCallback(void *data, wl_pointer *pointer)
void Pointer::Private::axisSourceCallback(void *data, wl_pointer *pointer, uint32_t axis_source)
{
Q_UNUSED(data)
Q_UNUSED(pointer)
Q_UNUSED(axis_source)
// TODO: implement
auto p = reinterpret_cast<Pointer::Private*>(data);
Q_ASSERT(p->pointer == pointer);
AxisSource source;
switch (axis_source) {
case WL_POINTER_AXIS_SOURCE_WHEEL:
source = AxisSource::Wheel;
break;
case WL_POINTER_AXIS_SOURCE_FINGER:
source = AxisSource::Finger;
break;
case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
source = AxisSource::Continuous;
break;
case WL_POINTER_AXIS_SOURCE_WHEEL_TILT:
source = AxisSource::WheelTilt;
break;
default:
Q_UNREACHABLE();
break;
}
emit p->q->axisSourceChanged(source);
}
void Pointer::Private::axisStopCallback(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis)
{
Q_UNUSED(data)
Q_UNUSED(pointer)
Q_UNUSED(time)
Q_UNUSED(axis)
// TODO: implement
auto p = reinterpret_cast<Pointer::Private*>(data);
Q_ASSERT(p->pointer == pointer);
emit p->q->axisStopped(time, wlAxisToPointerAxis(axis));
}
void Pointer::Private::axisDiscreteCallback(void *data, wl_pointer *pointer, uint32_t axis, int32_t discrete)
{
Q_UNUSED(data)
Q_UNUSED(pointer)
Q_UNUSED(axis)
Q_UNUSED(discrete)
// TODO: implement
auto p = reinterpret_cast<Pointer::Private*>(data);
Q_ASSERT(p->pointer == pointer);
emit p->q->axisDiscreteChanged(wlAxisToPointerAxis(axis), discrete);
}
void Pointer::setCursor(Surface *surface, const QPoint &hotspot)
......
......@@ -55,6 +55,12 @@ public:
Vertical,
Horizontal
};
enum class AxisSource {
Wheel,
Finger,
Continuous,
WheelTilt
};
explicit Pointer(QObject *parent = nullptr);
virtual ~Pointer();
......@@ -169,6 +175,24 @@ Q_SIGNALS:
* @param delta
**/
void axisChanged(quint32 time, KWayland::Client::Pointer::Axis axis, qreal delta);
/**
* Indicates the source of scroll and other axes.
*
* @since 5.59
**/
void axisSourceChanged(KWayland::Client::Pointer::AxisSource source);
/**
* Discrete step information for scroll and other axes.
*
* @since 5.59
**/
void axisDiscreteChanged(KWayland::Client::Pointer::Axis axis, qint32 discreteDelta);
/**
* Stop notification for scroll and other axes.
*
* @since 5.59
**/
void axisStopped(quint32 time, KWayland::Client::Pointer::Axis axis);
/**
* Indicates the end of a set of events that logically belong together.
......@@ -188,5 +212,6 @@ private:
Q_DECLARE_METATYPE(KWayland::Client::Pointer::ButtonState)
Q_DECLARE_METATYPE(KWayland::Client::Pointer::Axis)
Q_DECLARE_METATYPE(KWayland::Client::Pointer::AxisSource)
#endif
......@@ -322,6 +322,54 @@ void PointerInterface::buttonReleased(quint32 button, quint32 serial)
d->sendFrame();
}
void PointerInterface::axis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source)
{
Q_D();
Q_ASSERT(d->focusedSurface);
if (!d->resource) {
return;
}
const quint32 version = wl_resource_get_version(d->resource);
const auto wlOrientation = (orientation == Qt::Vertical)
? WL_POINTER_AXIS_VERTICAL_SCROLL
: WL_POINTER_AXIS_HORIZONTAL_SCROLL;
if (source != PointerAxisSource::Unknown && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
wl_pointer_axis_source wlSource;
switch (source) {
case PointerAxisSource::Wheel:
wlSource = WL_POINTER_AXIS_SOURCE_WHEEL;
break;
case PointerAxisSource::Finger:
wlSource = WL_POINTER_AXIS_SOURCE_FINGER;
break;
case PointerAxisSource::Continuous:
wlSource = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
break;
case PointerAxisSource::WheelTilt:
wlSource = WL_POINTER_AXIS_SOURCE_WHEEL_TILT;
break;
default:
Q_UNREACHABLE();
break;
}
wl_pointer_send_axis_source(d->resource, wlSource);
}
if (delta != 0.0) {
if (discreteDelta && version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
wl_pointer_send_axis_discrete(d->resource, wlOrientation, discreteDelta);
}
wl_pointer_send_axis(d->resource, d->seat->timestamp(), wlOrientation, wl_fixed_from_double(delta));
} else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) {
wl_pointer_send_axis_stop(d->resource, d->seat->timestamp(), wlOrientation);
}
d->sendFrame();
}
void PointerInterface::axis(Qt::Orientation orientation, quint32 delta)
{
Q_D();
......
......@@ -35,6 +35,8 @@ class RelativePointerManagerUnstableV1Interface;
class SeatInterface;
class SurfaceInterface;
enum class PointerAxisSource;
/**
* @brief Resource for the wl_pointer interface.
*
......@@ -73,6 +75,7 @@ private:
void setFocusedSurface(SurfaceInterface *surface, quint32 serial);
void buttonPressed(quint32 button, quint32 serial);
void buttonReleased(quint32 button, quint32 serial);
void axis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source);
void axis(Qt::Orientation orientation, quint32 delta);
void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds);
friend class SeatInterface;
......
......@@ -846,6 +846,20 @@ bool SeatInterface::isPointerButtonPressed(quint32 button) const
return it.value() == Private::Pointer::State::Pressed ? true : false;
}
void SeatInterface::pointerAxisV5(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source)
{
Q_D();
if (d->drag.mode == Private::Drag::Mode::Pointer) {
// ignore
return;
}
if (d->globalPointer.focus.surface) {
for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) {
(*it)->axis(orientation, delta, discreteDelta, source);
}
}
}
void SeatInterface::pointerAxis(Qt::Orientation orientation, quint32 delta)
{
Q_D();
......
......@@ -43,6 +43,34 @@ class Display;
class SurfaceInterface;
class TextInputInterface;
/**
* Describes the source types for axis events. This indicates to the
* client how an axis event was physically generated; a client may
* adjust the user interface accordingly. For example, scroll events
* from a "finger" source may be in a smooth coordinate space with
* kinetic scrolling whereas a "wheel" source may be in discrete steps
* of a number of lines.
*
* The "continuous" axis source is a device generating events in a
* continuous coordinate space, but using something other than a
* finger. One example for this source is button-based scrolling where
* the vertical motion of a device is converted to scroll events while
* a button is held down.
*
* The "wheel tilt" axis source indicates that the actual device is a
* wheel but the scroll event is not caused by a rotation but a
* (usually sideways) tilt of the wheel.
*
* @since 5.59
**/
enum class PointerAxisSource {
Unknown,
Wheel,
Finger,
Continuous,
WheelTilt
};
/**
* @brief Represents a Seat on the Wayland Display.
*
......@@ -361,6 +389,20 @@ public:
* @returns the last serial for @p button.
**/
quint32 pointerButtonSerial(Qt::MouseButton button) const;
/**
* Sends axis events to the currently focused pointer surface.
*
* @param orientation The scroll axis.
* @param delta The length of a vector along the specified axis @p orientation.
* @param discreteDelta The number of discrete steps, e.g. mouse wheel clicks.
* @param source Describes how the axis event was physically generated.
* @since 5.59
* @todo Drop V5 suffix with KF6.
**/
void pointerAxisV5(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source);
/**
* @see pointerAxisV5
**/
void pointerAxis(Qt::Orientation orientation, quint32 delta);
/**
* @returns true if there is a pressed button with the given @p serial
......
Markdown is supported
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