fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed

Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
This commit is contained in:
2026-05-05 20:20:37 +01:00
parent a5f97b6632
commit f31522130f
81834 changed files with 11051982 additions and 108 deletions
@@ -0,0 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Generated from seatv5.pro.
#####################################################################
## tst_seatv5 Test:
#####################################################################
qt_internal_add_test(tst_seat
SOURCES
tst_seat.cpp
LIBRARIES
SharedClientTest
)
@@ -0,0 +1,685 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "mockcompositor.h"
#include <QtGui/QRasterWindow>
#include <QtGui/QEventPoint>
using namespace MockCompositor;
class SeatCompositor : public DefaultCompositor {
public:
explicit SeatCompositor()
{
exec([this] {
m_config.autoConfigure = true;
removeAll<Seat>();
uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch;
int version = 9;
add<Seat>(capabilities, version);
});
}
};
class tst_seat : public QObject, private SeatCompositor
{
Q_OBJECT
private slots:
void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
void bindsToSeat();
// Pointer tests
void createsPointer();
void setsCursorOnEnter();
void usesEnterSerial();
void simpleAxis_data();
void simpleAxis();
void fingerScroll();
void fingerScrollSlow();
void continuousScroll();
void highResolutionScroll();
// Touch tests
void createsTouch();
void singleTap();
void singleTapFloat();
void multiTouch();
void multiTouchUpAndMotionFrame();
void tapAndMoveInSameFrame();
void cancelTouch();
};
void tst_seat::bindsToSeat()
{
QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1);
QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 9);
}
void tst_seat::createsPointer()
{
QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1);
QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 9);
}
void tst_seat::setsCursorOnEnter()
{
QRasterWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *surface = xdgSurface()->m_surface;
pointer()->sendEnter(surface, {0, 0});
pointer()->sendFrame(surface->resource()->client());
});
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
}
void tst_seat::usesEnterSerial()
{
QSignalSpy setCursorSpy(exec([&] { return pointer(); }), &Pointer::setCursor);
QRasterWindow window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
uint enterSerial = exec([&] {
return pointer()->sendEnter(xdgSurface()->m_surface, {0, 0});
});
QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface());
QTRY_COMPARE(setCursorSpy.size(), 1);
QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial);
}
class WheelWindow : QRasterWindow {
public:
WheelWindow()
{
resize(64, 64);
show();
}
void wheelEvent(QWheelEvent *event) override
{
QRasterWindow::wheelEvent(event);
// qDebug() << event << "angleDelta" << event->angleDelta() << "pixelDelta" << event->pixelDelta();
if (event->phase() != Qt::ScrollUpdate && event->phase() != Qt::NoScrollPhase) {
// Shouldn't have deltas in the these phases
QCOMPARE(event->angleDelta(), QPoint(0, 0));
QCOMPARE(event->pixelDelta(), QPoint(0, 0));
}
// We didn't press any buttons
QCOMPARE(event->buttons(), Qt::NoButton);
m_events.append(Event{event});
}
struct Event // Because I didn't find a convenient way to copy it entirely
{
explicit Event() = default;
explicit Event(const QWheelEvent *event)
: phase(event->phase())
, pixelDelta(event->pixelDelta())
, angleDelta(event->angleDelta())
, source(event->source())
, inverted(event->inverted())
{
}
Qt::ScrollPhase phase{};
QPoint pixelDelta;
QPoint angleDelta; // eights of a degree, positive is upwards, left
Qt::MouseEventSource source{};
bool inverted = false;
};
QList<Event> m_events;
};
void tst_seat::simpleAxis_data()
{
QTest::addColumn<uint>("axis");
QTest::addColumn<qreal>("value");
QTest::addColumn<QPoint>("angleDelta");
QTest::addColumn<bool>("inverted");
// Directions in regular windows/linux terms (no "natural" scrolling)
QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12} << false;
QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12} << false;
QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0} << false;
QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0} << false;
QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120} << false;
// (natural) scrolling
QTest::newRow("down inverted") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12} << true;
QTest::newRow("up inverted") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12} << true;
QTest::newRow("left inverted") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0} << true;
QTest::newRow("right inverted") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0} << true;
QTest::newRow("up big inverted") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120} << true;
}
void tst_seat::simpleAxis()
{
QFETCH(uint, axis);
QFETCH(qreal, value);
QFETCH(QPoint, angleDelta);
QFETCH(bool, inverted);
WheelWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *p = pointer();
p->sendEnter(xdgToplevel()->surface(), {32, 32});
p->sendFrame(client());
p->sendAxis(
client(),
Pointer::axis(axis),
value // Length of vector in surface-local space. i.e. positive is downwards
);
auto direction = inverted ? Pointer::axis_relative_direction_inverted : Pointer::axis_relative_direction_identical;
p->sendAxisRelativeDirection(client(), Pointer::axis(axis), direction);
p->sendFrame(client());
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::NoScrollPhase);
// Pixel delta should only be set if we know it's a high-res input device (which we don't)
QCOMPARE(e.pixelDelta, QPoint(0, 0));
// There has been no information about what created the event.
// Documentation says not synthesized is appropriate in such cases
QCOMPARE(e.source, Qt::MouseEventNotSynthesized);
QCOMPARE(e.angleDelta, angleDelta);
QCOMPARE(e.inverted, inverted);
}
// Sending axis_stop is not mandatory when axis source != finger
}
void tst_seat::fingerScroll()
{
WheelWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *p = pointer();
auto *c = client();
p->sendEnter(xdgToplevel()->surface(), {32, 32});
p->sendFrame(c);
p->sendAxisSource(c, Pointer::axis_source_finger);
p->sendAxis(c, Pointer::axis_vertical_scroll, 10);
p->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::ScrollBegin);
QCOMPARE(e.angleDelta, QPoint());
QCOMPARE(e.pixelDelta, QPoint());
}
QTRY_VERIFY(!window.m_events.empty());
// For some reason we send two ScrollBegins, one for each direction, not sure if this is really
// necessary, (could be removed from QtBase, hence the conditional below.
if (window.m_events.first().phase == Qt::ScrollBegin) {
auto e = window.m_events.takeFirst();
QCOMPARE(e.angleDelta, QPoint());
QCOMPARE(e.pixelDelta, QPoint());
}
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::ScrollUpdate);
QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll
// QCOMPARE(e.angleDelta, angleDelta); // TODO: what should this be?
QCOMPARE(e.pixelDelta, QPoint(0, -10));
QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel
}
QTRY_VERIFY(window.m_events.empty());
// Scroll horizontally as well
exec([&] {
pointer()->sendAxisSource(client(), Pointer::axis_source_finger);
pointer()->sendAxis(client(), Pointer::axis_horizontal_scroll, 10);
pointer()->sendFrame(client());
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::ScrollUpdate);
QVERIFY(qAbs(e.angleDelta.x()) > qAbs(e.angleDelta.y())); // Horizontal scroll
QCOMPARE(e.pixelDelta, QPoint(-10, 0));
QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel
}
// Scroll diagonally
exec([&] {
pointer()->sendAxisSource(client(), Pointer::axis_source_finger);
pointer()->sendAxis(client(), Pointer::axis_horizontal_scroll, 10);
pointer()->sendAxis(client(), Pointer::axis_vertical_scroll, 10);
pointer()->sendFrame(client());
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::ScrollUpdate);
QCOMPARE(e.pixelDelta, QPoint(-10, -10));
QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel
}
// For diagonal events, Qt sends an additional compatibility ScrollUpdate event
if (window.m_events.first().phase == Qt::ScrollUpdate) {
auto e = window.m_events.takeFirst();
QCOMPARE(e.angleDelta, QPoint());
QCOMPARE(e.pixelDelta, QPoint());
}
QVERIFY(window.m_events.empty());
// Sending axis_stop is mandatory when axis source == finger
exec([&] {
pointer()->sendAxisStop(client(), Pointer::axis_vertical_scroll);
pointer()->sendFrame(client());
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::ScrollEnd);
}
}
void tst_seat::fingerScrollSlow()
{
WheelWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *p = pointer();
auto *c = client();
p->sendEnter(xdgToplevel()->surface(), {32, 32});
p->sendFrame(c);
// Send 10 really small updates
for (int i = 0; i < 10; ++i) {
p->sendAxisSource(c, Pointer::axis_source_finger);
p->sendAxis(c, Pointer::axis_vertical_scroll, 0.1);
p->sendFrame(c);
}
p->sendAxisStop(c, Pointer::axis_vertical_scroll);
p->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
QPoint accumulated;
while (window.m_events.first().phase != Qt::ScrollEnd) {
auto e = window.m_events.takeFirst();
accumulated += e.pixelDelta;
QTRY_VERIFY(!window.m_events.empty());
}
QCOMPARE(accumulated.y(), -1);
}
void tst_seat::highResolutionScroll()
{
WheelWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *p = pointer();
auto *c = client();
p->sendEnter(xdgToplevel()->surface(), {32, 32});
p->sendFrame(c);
p->sendAxisSource(c, Pointer::axis_source_wheel);
p->sendAxisValue120(c, Pointer::axis_vertical_scroll, 30); // quarter of a click
p->sendAxis(c, Pointer::axis_vertical_scroll, 3.75);
p->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::NoScrollPhase);
QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll
QCOMPARE(e.angleDelta, QPoint(0, -30));
// Click scrolls are not continuous and should not have a pixel delta
QCOMPARE(e.pixelDelta, QPoint(0, 0));
}
exec([&] {
auto *p = pointer();
auto *c = client();
p->sendAxisSource(c, Pointer::axis_source_wheel);
p->sendAxisValue120(c, Pointer::axis_vertical_scroll, 90); // complete the click
p->sendAxis(c, Pointer::axis_vertical_scroll, 11.25);
p->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::NoScrollPhase);
QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll
QCOMPARE(e.angleDelta, QPoint(0, -90));
// Click scrolls are not continuous and should not have a pixel delta
QCOMPARE(e.pixelDelta, QPoint(0, 0));
}
}
void tst_seat::continuousScroll()
{
WheelWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *p = pointer();
auto *c = client();
p->sendEnter(xdgToplevel()->surface(), {32, 32});
p->sendFrame(c);
p->sendAxisSource(c, Pointer::axis_source_continuous);
p->sendAxis(c, Pointer::axis_vertical_scroll, 10);
p->sendAxis(c, Pointer::axis_horizontal_scroll, -5);
p->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.phase, Qt::NoScrollPhase);
QCOMPARE(e.pixelDelta, QPoint(5, -10));
QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // touchpads are not wheels
QCOMPARE(e.inverted, false);
}
// Sending axis_stop is not mandatory when axis source != finger
}
void tst_seat::createsTouch()
{
QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().size(), 1);
QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 9);
}
class TouchWindow : public QRasterWindow {
public:
TouchWindow()
{
resize(64, 64);
show();
}
void touchEvent(QTouchEvent *event) override
{
QRasterWindow::touchEvent(event);
m_events.append(Event{event});
}
struct Event // Because I didn't find a convenient way to copy it entirely
{
explicit Event() = default;
explicit Event(const QTouchEvent *event)
: type(event->type())
, touchPointStates(event->touchPointStates())
, touchPoints(event->points())
{
}
QEvent::Type type{};
QEventPoint::States touchPointStates{};
QList<QTouchEvent::TouchPoint> touchPoints;
};
QList<Event> m_events;
};
void tst_seat::singleTap()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32, 32}, 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, QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints.first().position(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top()));
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchEnd);
QCOMPARE(e.touchPointStates, QEventPoint::State::Released);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints.first().position(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top()));
}
}
void tst_seat::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, QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints.first().position(), 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, QEventPoint::State::Released);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints.first().position(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top()));
}
}
void tst_seat::multiTouch()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32, 32}, 0);
t->sendDown(xdgToplevel()->surface(), {48, 48}, 1);
t->sendFrame(c);
// Compositor event order should not change the order of the QTouchEvent::points()
// See QTBUG-77014
t->sendMotion(c, {49, 48}, 1);
t->sendMotion(c, {33, 32}, 0);
t->sendFrame(c);
t->sendUp(c, 0);
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, QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints.size(), 2);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints[0].position(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top()));
QCOMPARE(e.touchPoints[1].state(), QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints[1].position(), QPointF(48-window.frameMargins().left(), 48-window.frameMargins().top()));
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchUpdate);
QCOMPARE(e.touchPoints.size(), 2);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Updated);
QCOMPARE(e.touchPoints[0].position(), QPointF(33-window.frameMargins().left(), 32-window.frameMargins().top()));
QCOMPARE(e.touchPoints[1].state(), QEventPoint::State::Updated);
QCOMPARE(e.touchPoints[1].position(), QPointF(49-window.frameMargins().left(), 48-window.frameMargins().top()));
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchUpdate);
QCOMPARE(e.touchPointStates, QEventPoint::State::Released | QEventPoint::State::Stationary);
QCOMPARE(e.touchPoints.size(), 2);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Released);
QCOMPARE(e.touchPoints[0].position(), QPointF(33-window.frameMargins().left(), 32-window.frameMargins().top()));
QCOMPARE(e.touchPoints[1].state(), QEventPoint::State::Stationary);
QCOMPARE(e.touchPoints[1].position(), QPointF(49-window.frameMargins().left(), 48-window.frameMargins().top()));
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchEnd);
QCOMPARE(e.touchPointStates, QEventPoint::State::Released);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Released);
QCOMPARE(e.touchPoints[0].position(), QPointF(49-window.frameMargins().left(), 48-window.frameMargins().top()));
}
}
void tst_seat::multiTouchUpAndMotionFrame()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32, 32}, 0);
t->sendDown(xdgToplevel()->surface(), {48, 48}, 1);
t->sendFrame(c);
// Sending an up event after a frame event, before any motion or down events used to
// unnecessarily trigger a workaround for a bug in an old version of Weston. The workaround
// would prematurely insert a fake frame event splitting the touch event up into two events.
// However, this should only be needed on the up event for the very last touch point. So in
// this test we verify that it doesn't unncecessarily break up the events.
t->sendUp(c, 0);
t->sendMotion(c, {49, 48}, 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.touchPoints[0].state(), QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints[1].state(), QEventPoint::State::Pressed);
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchUpdate);
QCOMPARE(e.touchPoints.size(), 2);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Released);
QCOMPARE(e.touchPoints[1].state(), QEventPoint::State::Updated);
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchEnd);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Released);
}
QVERIFY(window.m_events.empty());
}
void tst_seat::tapAndMoveInSameFrame()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32, 32}, 0);
t->sendMotion(c, {33, 33}, 0);
t->sendFrame(c);
// Don't leave touch in a weird state
t->sendUp(c, 0);
t->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchBegin);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints[0].state(), QEventPoint::State::Pressed);
// Position isn't that important, we just want to make sure we actually get the pressed event
}
// Make sure we eventually release
QTRY_VERIFY(!window.m_events.empty());
QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), QEventPoint::State::Released);
}
void tst_seat::cancelTouch()
{
TouchWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *t = touch();
auto *c = client();
t->sendDown(xdgToplevel()->surface(), {32, 32}, 1);
t->sendFrame(c);
t->sendCancel(c);
t->sendFrame(c);
});
QTRY_VERIFY(!window.m_events.empty());
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchBegin);
QCOMPARE(e.touchPointStates, QEventPoint::State::Pressed);
QCOMPARE(e.touchPoints.size(), 1);
QCOMPARE(e.touchPoints.first().position(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top()));
}
{
auto e = window.m_events.takeFirst();
QCOMPARE(e.type, QEvent::TouchCancel);
QCOMPARE(e.touchPoints.size(), 0);
}
}
QCOMPOSITOR_TEST_MAIN(tst_seat)
#include "tst_seat.moc"