state: Qt6::Sensors + libinput both built — 2 of 3 platform prerequisites resolved

Platform prerequisite status:
- Qt6::Sensors: BUILT (v6.11.0, 520KB pkgar, dummy backend)
- libinput: BUILT (v1.30.2, with libevdev v1.13.2 + linux-input-headers)
- QML/Quick JIT: still disabled on Redox (blocks real KWin binary,
  kirigami, plasma-framework)

KWin: now attempts real cmake build with Sensors + libinput deps
enabled. Falls back to redbear-compositor shim on cmake failure
(QML/Quick gate). Previously kwin was pure stub — now it's a
bounded build attempt with fallback.

Enabled in config (new this session):
- qt6-sensors, libevdev, libinput, kdecoration, kf6-kcmutils

Previously OOTB dependencies now resolved:
- libevdev → libinput → KWin real build path opened
- linux-input-headers → libevdev → libinput chain
- qt6-sensors → KWin Sensors dependency satisfied
This commit is contained in:
2026-04-30 08:47:15 +01:00
parent c54dff033d
commit 8fa62d20c9
457 changed files with 106858 additions and 382 deletions
@@ -0,0 +1,2 @@
add_subdirectory(qml_cpp)
add_subdirectory(qml_quick)
@@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_test(tst_sensors_qmlcpp
SOURCES
../../common/test_backends.cpp ../../common/test_backends.h
tst_sensors_qmlcpp.cpp
LIBRARIES
Qt::Qml
Qt::SensorsPrivate
Qt::SensorsQuickPrivate
Qt::TestPrivate
)
@@ -0,0 +1,278 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtCore/QDebug>
#include <QtTest/private/qpropertytesthelper_p.h>
#include <QtSensorsQuick/private/qmlsensor_p.h>
#include <qsensorbackend.h>
#include "qsensormanager.h"
#include "../../common/test_backends.h"
#include <QtSensorsQuick/private/qmlaccelerometer_p.h>
#include <QtSensorsQuick/private/qmlpressuresensor_p.h>
#include <QtSensorsQuick/private/qmlgyroscope_p.h>
#include <QtSensorsQuick/private/qmltapsensor_p.h>
#include <QtSensorsQuick/private/qmlcompass_p.h>
#include <QtSensorsQuick/private/qmlproximitysensor_p.h>
#include <QtSensorsQuick/private/qmlorientationsensor_p.h>
#include <QtSensorsQuick/private/qmlambientlightsensor_p.h>
#include <QtSensorsQuick/private/qmlmagnetometer_p.h>
#include <QtSensorsQuick/private/qmllidsensor_p.h>
#include <QtSensorsQuick/private/qmltiltsensor_p.h>
#include <QtSensorsQuick/private/qmlrotationsensor_p.h>
#include <QtSensorsQuick/private/qmlhumiditysensor_p.h>
#include <QtSensorsQuick/private/qmlambienttemperaturesensor_p.h>
#include <QtSensorsQuick/private/qmllightsensor_p.h>
#include <QtSensorsQuick/private/qmlirproximitysensor_p.h>
QT_USE_NAMESPACE
QT_BEGIN_NAMESPACE
class tst_sensors_qmlcpp : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void testReadingBindings();
// void testGesture();
void testSensorRanges();
};
void tst_sensors_qmlcpp::initTestCase()
{
qputenv("QT_SENSORS_LOAD_PLUGINS", "0"); // Do not load plugins
}
template<typename SensorClass, typename ReadingClass, typename ValueType>
void testSensorReadings(const char* identifier, const QVariantMap& values)
{
SensorClass sensor;
sensor.setIdentifier(identifier);
sensor.componentComplete();
sensor.start();
for (const auto& key : values.keys()) {
ValueType initialValue = values[key].toList()[0].value<ValueType>();
ValueType changedValue = values[key].toList()[1].value<ValueType>();
QTestPrivate::testReadOnlyPropertyBasics<ReadingClass, ValueType>(
*static_cast<ReadingClass*>(sensor.reading()),
initialValue, changedValue, key.toStdString().c_str(),
[&](){ set_test_backend_reading(sensor.sensor(), {{key, changedValue}}); });
if (QTest::currentTestFailed()) {
qWarning() << identifier << "::" << key << "test failed.";
return;
}
}
}
void tst_sensors_qmlcpp::testReadingBindings()
{
register_test_backends();
testSensorReadings<QmlAccelerometer, QmlAccelerometerReading, qreal>(
"QAccelerometer",
{{"x", QVariantList{1.0, 2.0}},
{"y", QVariantList{1.0, 2.0}},
{"z", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlAccelerometer, QmlAccelerometerReading, quint64>(
"QAccelerometer",
{{"timestamp", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlAmbientLightSensor, QmlAmbientLightSensorReading, QAmbientLightReading::LightLevel>(
"QAmbientLightSensor",
{{"lightLevel", QVariantList{QAmbientLightReading::Twilight, QAmbientLightReading::Sunny}}});
testSensorReadings<QmlPressureSensor, QmlPressureReading, qreal>(
"QPressureSensor",
{{"pressure", QVariantList{1.0, 2.0}},
{"temperature", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlGyroscope, QmlGyroscopeReading, qreal>(
"QGyroscope",
{{"x", QVariantList{1.0, 2.0}},
{"y", QVariantList{1.0, 2.0}},
{"z", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlTapSensor, QmlTapSensorReading, bool>(
"QTapSensor",
{{"doubleTap", QVariantList{true, false}}});
testSensorReadings<QmlTapSensor, QmlTapSensorReading, QTapReading::TapDirection>(
"QTapSensor",
{{"tapDirection", QVariantList{QTapReading::Z_Both, QTapReading::X_Both}}});
testSensorReadings<QmlCompass, QmlCompassReading, qreal>(
"QCompass",
{{"azimuth", QVariantList{1.0, 2.0}},
{"calibrationLevel", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlProximitySensor, QmlProximitySensorReading, bool>(
"QProximitySensor",
{{"near", QVariantList{true, false}}});
testSensorReadings<QmlOrientationSensor, QmlOrientationSensorReading, QOrientationReading::Orientation>(
"QOrientationSensor",
{{"orientation", QVariantList{QOrientationReading::LeftUp, QOrientationReading::RightUp}}});
testSensorReadings<QmlMagnetometer, QmlMagnetometerReading, qreal>(
"QMagnetometer",
{{"x", QVariantList{1.0, 2.0}},
{"y", QVariantList{1.0, 2.0}},
{"z", QVariantList{1.0, 2.0}},
{"calibrationLevel", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlLidSensor, QmlLidReading, bool>(
"QLidSensor",
{{"backLidClosed", QVariantList{true, false}},
{"frontLidClosed", QVariantList{true, false}}});
testSensorReadings<QmlTiltSensor, QmlTiltSensorReading, qreal>(
"QTiltSensor",
{{"yRotation", QVariantList{1.0, 2.0}},
{"xRotation", QVariantList{1.0, 2.0}}});
// rotation sensor properties need to be tested separately because the setter function is
// not symmetric with getter functions ("setFromEuler()" vs. "x() & y() & z()")
testSensorReadings<QmlRotationSensor, QmlRotationSensorReading, qreal>(
"QRotationSensor",
{{"x", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlRotationSensor, QmlRotationSensorReading, qreal>(
"QRotationSensor",
{{"y", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlRotationSensor, QmlRotationSensorReading, qreal>(
"QRotationSensor",
{{"z", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlHumiditySensor, QmlHumidityReading, qreal>(
"QHumiditySensor",
{{"relativeHumidity", QVariantList{1.0, 2.0}},
{"absoluteHumidity", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlAmbientTemperatureSensor, QmlAmbientTemperatureReading, qreal>(
"QAmbientTemperatureSensor",
{{"temperature", QVariantList{30.0, 40.0}}});
testSensorReadings<QmlLightSensor, QmlLightSensorReading, qreal>(
"QLightSensor",
{{"illuminance", QVariantList{1.0, 2.0}}});
testSensorReadings<QmlIRProximitySensor, QmlIRProximitySensorReading, qreal>(
"QIRProximitySensor",
{{"reflectance", QVariantList{0.5, 0.6}}});
// The following tests QmlSensor (the baseclass) 'readingChanged' which is
// emitted every time a sensor value changes. For that we instantiate a
// concrete sensor. The actual 'reading' value (a QObject pointer) of the
// 'readingChanged' property will not change, but rather the
// 'readingChanged' is used to indicate that the value it contains has changed.
QmlAccelerometer accelerometer;
accelerometer.setIdentifier("QAccelerometer");
accelerometer.componentComplete();
accelerometer.start();
QTestPrivate::testReadOnlyPropertyBasics<QmlSensor, QmlSensorReading*>(
accelerometer, accelerometer.reading(), accelerometer.reading(), "reading",
[&](){ set_test_backend_reading(accelerometer.sensor(), {{"x", 2.0}}); });
unregister_test_backends();
}
class QDummySensorBackend : public QSensorBackend
{
Q_OBJECT
public:
QDummySensorBackend(QSensor *sensor) : QSensorBackend(sensor)
{
addDataRate(2, 3);
addDataRate(5, 7);
addOutputRange(100, 200, 1);
addOutputRange(600, 700, 10);
addOutputRange(0, 1, 2);
}
void start() override {}
void stop() override {}
};
class QDummySensorReading : public QSensorReading
{
Q_OBJECT
public:
QDummySensorReading(QObject *parent) : QSensorReading(parent, nullptr) {}
};
class QmlDummySensorReading : public QmlSensorReading
{
Q_OBJECT
public:
QmlDummySensorReading() :
m_reading(new QDummySensorReading(this))
{}
QSensorReading *reading() const override { return m_reading; }
void readingUpdate() override {}
private:
QSensorReading *m_reading = nullptr;
};
class QmlDummySensor : public QmlSensor
{
Q_OBJECT
public:
QmlDummySensor(QObject *parent = nullptr) :
QmlSensor(parent),
m_sensor(new QSensor("dummy", this))
{
QDummySensorBackend b(m_sensor);
Q_UNUSED(b);
}
QSensor *sensor() const override { return m_sensor; }
QmlSensorReading *createReading() const override { return new QmlDummySensorReading(); }
void componentComplete() override { QmlSensor::componentComplete(); }
private:
QSensor *m_sensor = nullptr;
};
void tst_sensors_qmlcpp::testSensorRanges()
{
QScopedPointer<QmlDummySensor> qmlSensor(new QmlDummySensor);
qmlSensor->componentComplete();
auto ranges = qmlSensor->availableDataRates();
QCOMPARE(ranges.count(&ranges), 2);
const auto range0 = ranges.at(&ranges, 0);
QCOMPARE(range0->minimum(), 2);
QCOMPARE(range0->maximum(), 3);
QSignalSpy range0Spy(range0, SIGNAL(destroyed()));
const auto range1 = ranges.at(&ranges, 1);
QCOMPARE(range1->minimum(), 5);
QCOMPARE(range1->maximum(), 7);
QSignalSpy range1Spy(range1, SIGNAL(destroyed()));
auto outputs = qmlSensor->outputRanges();
QCOMPARE(outputs.count(&outputs), 3);
const auto output0 = outputs.at(&outputs, 0);
QCOMPARE(output0->minimum(), 100);
QCOMPARE(output0->maximum(), 200);
QCOMPARE(output0->accuracy(), 1);
QSignalSpy output0Spy(output0, SIGNAL(destroyed()));
const auto output1 = outputs.at(&outputs, 1);
QCOMPARE(output1->minimum(), 600);
QCOMPARE(output1->maximum(), 700);
QCOMPARE(output1->accuracy(), 10);
QSignalSpy output1Spy(output1, SIGNAL(destroyed()));
const auto output2 = outputs.at(&outputs, 2);
QCOMPARE(output2->minimum(), 0);
QCOMPARE(output2->maximum(), 1);
QCOMPARE(output2->accuracy(), 2);
QSignalSpy output2Spy(output2, SIGNAL(destroyed()));
qmlSensor.reset();
QCOMPARE(range0Spy.size(), 1);
QCOMPARE(range1Spy.size(), 1);
QCOMPARE(output0Spy.size(), 1);
QCOMPARE(output1Spy.size(), 1);
QCOMPARE(output2Spy.size(), 1);
}
QT_END_NAMESPACE
QTEST_MAIN(tst_sensors_qmlcpp)
#include "tst_sensors_qmlcpp.moc"
@@ -0,0 +1,20 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/tst_*qml)
list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_sensors_qmlquick
QMLTEST
SOURCES
../../common/test_backends.cpp ../../common/test_backends.h
tst_sensors_qmlquick.cpp
LIBRARIES
Qt::Quick
Qt::Sensors
Qt::SensorsQuickPrivate
TESTDATA ${test_data}
)
@@ -0,0 +1,226 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtTest
import QtSensors
TestCase {
id: testCase
name: "SensorTest"
SignalSpy {
id: sensorActiveSpy
signalName: "activeChanged"
}
SignalSpy {
id: sensorReadingSpy
signalName: "readingChanged"
}
SignalSpy {
id: sensorBusySpy
signalName: "busyChanged"
}
SignalSpy {
id: sensorIdentifierSpy
signalName: "identifierChanged"
}
function init() {
TestControl.registerTestBackends()
}
function cleanup() {
TestControl.unregisterTestBackends()
sensorBusySpy.clear()
sensorActiveSpy.clear()
sensorReadingSpy.clear()
sensorIdentifierSpy.clear()
}
function test_activate() {
// create sensor without proper identifier and verify activation fails
var sensor = Qt.createQmlObject("import QtSensors; Accelerometer {identifier: \"nonexistent\"}",testCase);
sensorActiveSpy.target = sensor
sensorIdentifierSpy.target = sensor
verify(!sensor.active)
compare(sensor.identifier, "nonexistent")
sensor.active = true
verify(!sensor.active)
compare(sensorActiveSpy.count, 0)
// set proper identifier and verify activation succeeds
sensor.identifier = "QAccelerometer"
compare(sensor.identifier, "QAccelerometer")
compare(sensorIdentifierSpy.count, 1)
sensor.active = true
compare(sensorActiveSpy.count, 1)
verify(sensor.active)
compare(sensor.reading.x, 1.0)
// set identifier again, verify no impact
sensor.identifier = "QAccelerometer"
compare(sensor.identifier, "QAccelerometer")
compare(sensorIdentifierSpy.count, 1)
// set activate again, verify no impact
sensor.active = true
sensor.start()
compare(sensorActiveSpy.count, 1)
verify(sensor.active)
// deactivate
sensor.active = false
compare(sensorActiveSpy.count, 2)
verify(!sensor.active)
// reactivate and stop
sensor.active = true
compare(sensorActiveSpy.count, 3)
verify(sensor.active)
sensor.stop()
compare(sensorActiveSpy.count, 4)
verify(!sensor.active)
// create sensor with proper id and active 'true' on creation time
var sensor2 = Qt.createQmlObject("import QtSensors; Accelerometer {identifier: \"QAccelerometer\"; active: true}", testCase);
verify(sensor2.active)
// create sensor with nonexistent id and active 'true' on creation time
var sensor3 = Qt.createQmlObject("import QtSensors; Accelerometer {identifier: \"nonexistent\"; active: true}", testCase);
verify(!sensor3.active)
sensor3.identifier = "QAccelerometer"
sensor3.start()
verify(sensor3.active)
// create sensor with empty id, and check that a default is used
var sensor4 = Qt.createQmlObject("import QtSensors; Accelerometer {active: true}", testCase);
verify(sensor4.active)
compare(sensor4.identifier, QmlSensors.defaultSensorForType("QAccelerometer"));
// same as previous but with delayed activation
var sensor5 = Qt.createQmlObject("import QtSensors; Accelerometer {}", testCase);
verify(!sensor5.active)
sensor5.active = true
verify(sensor5.active)
compare(sensor5.identifier, QmlSensors.defaultSensorForType("QAccelerometer"));
// tidy up
sensor.destroy()
sensor2.destroy()
sensor3.destroy()
sensor4.destroy()
sensor5.destroy()
}
function test_busy() {
var sensor = Qt.createQmlObject("import QtSensors; Accelerometer {identifier: \"QAccelerometer\"}", testCase);
sensorBusySpy.target = sensor
compare(sensor.busy, false)
verify(sensor.start())
// set sensor busy and verify 'busy' property and its signaling
TestControl.setSensorBusy(sensor, true)
compare(sensorBusySpy.count, 1)
TestControl.setSensorBusy(sensor, false)
compare(sensorBusySpy.count, 2)
TestControl.setSensorBusy(sensor, false)
compare(sensorBusySpy.count, 2)
// tidy up
sensor.destroy()
}
function test_reading(data) {
var sensor = Qt.createQmlObject(
"import QtSensors; "
+ data.tag + "{"
+ "identifier: " + "\"Q" + data.tag + "\""
+ "}"
,testCase)
sensorActiveSpy.target = sensor
sensorReadingSpy.target = sensor
// verify initial values of sensor
// note: 'reading' values are 'undefined by design' before activation, and therefore aren't tested
compare(sensor.type, "Q" + data.tag)
compare(sensor.active, false)
compare(sensor.alwaysOn, false )
compare(sensor.busy, false)
compare(sensor.description, "")
compare(sensor.error, 0)
compare(sensor.skipDuplicates, false)
// start the sensor and verify activation
sensor.start()
compare(sensor.active, true)
compare(sensorActiveSpy.count, 1)
compare(sensorReadingSpy.count, 1)
// verify the initial reading values
for (var prop in data.initialReading)
fuzzyCompare(sensor.reading[prop], data.initialReading[prop], 0.0001, data.tag + "::" + prop)
// change reading values and verify them
TestControl.setSensorReading(sensor, data.newReading)
compare(sensorReadingSpy.count, 2)
for (prop in data.newReading)
fuzzyCompare(sensor.reading[prop], data.newReading[prop], 0.0001, data.tag + "::" + prop)
// stop the sensor and verify deactivation
sensor.stop()
compare(sensor.active, false)
compare(sensorActiveSpy.count, 2)
compare(sensorReadingSpy.count, 2)
// tidy up
sensor.destroy()
}
function test_reading_data() {
return [
{tag: "Accelerometer", initialReading: {timestamp: 1, x: 1.0, y: 1.0, z: 1.0}, newReading: {timestamp: 2, x: 2.0, y: 3.0, z: 4.0}},
{tag: "PressureSensor", initialReading: {pressure: 1.0, temperature: 1.0}, newReading: {pressure: 2.0, temperature: 3.0}},
{tag: "Gyroscope", initialReading: {x : 1.0, y: 1.0, z: 1.0}, newReading: {x : 2.0, y: 3.0, z: 4.0}},
{tag: "TapSensor", initialReading: {doubleTap: true, tapDirection: TapReading.Z_Both}, newReading: {doubleTap: false, tapDirection: TapReading.X_Both}},
{tag: "Compass", initialReading: {azimuth: 1.0, calibrationLevel: 1.0}, newReading: {azimuth: 2.0, calibrationLevel: 3.0}},
{tag: "ProximitySensor", initialReading: {near: true}, newReading: {near: false}},
{tag: "OrientationSensor", initialReading: {orientation: OrientationReading.LeftUp}, newReading: {orientation: OrientationReading.RightUp}},
{tag: "AmbientLightSensor", initialReading: {lightLevel: AmbientLightReading.Twilight}, newReading: {lightLevel: AmbientLightReading.Sunny}},
{tag: "Magnetometer", initialReading: {x : 1.0, y: 1.0, z: 1.0, calibrationLevel: 1.0}, newReading: {x : 2.0, y: 3.0, z: 4.0, calibrationLevel: 5.0}},
{tag: "LidSensor", initialReading: {backLidClosed:true, frontLidClosed: true}, newReading: {backLidClosed:false, frontLidClosed: false}},
{tag: "TiltSensor", initialReading: {yRotation: 1.0, xRotation: 1.0}, newReading: {yRotation: 2.0, xRotation: 3.0}},
{tag: "RotationSensor", initialReading: {x: 1.0, y: 1.0, z: 1.0}, newReading: {x: 2.0, y: 3.0, z: 4.0}},
{tag: "HumiditySensor", initialReading: {relativeHumidity: 1.0, absoluteHumidity: 1.0}, newReading: {relativeHumidity: 2.0, absoluteHumidity: 3.0}},
{tag: "AmbientTemperatureSensor", initialReading: {temperature: 30.0}, newReading: {temperature: 40.0}},
{tag: "LightSensor", initialReading: {illuminance: 1.0}, newReading: {illuminance: 2.0}},
{tag: "IRProximitySensor", initialReading: {reflectance: 0.5}, newReading: {reflectance: 0.6}}
];
}
function test_SupportedFeatures()
{
var sensor = Qt.createQmlObject("import QtSensors; Accelerometer \
{identifier: \"QAccelerometer\"}",
testCase);
verify(sensor.start())
verify(sensor.connectedToBackend)
// According to isFeatureSupported() override implementation in test_backends.h,
// only SkipDuplicates should be supported afterwards
verify(!sensor.isFeatureSupported(Sensor.Buffering))
verify(!sensor.isFeatureSupported(Sensor.AlwaysOn))
verify(!sensor.isFeatureSupported(Sensor.GeoValues))
verify(!sensor.isFeatureSupported(Sensor.FieldOfView))
verify(!sensor.isFeatureSupported(Sensor.AccelerationMode))
verify(sensor.isFeatureSupported(Sensor.SkipDuplicates))
verify(!sensor.isFeatureSupported(Sensor.AxesOrientation))
verify(!sensor.isFeatureSupported(Sensor.PressureSensorTemperature))
sensor.destroy()
}
}
@@ -0,0 +1,41 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlContext>
#include <QtSensorsQuick/private/qmlsensor_p.h>
#include "../../common/test_backends.h"
class TestSetup : public QObject
{
Q_OBJECT
public:
TestSetup() {}
public slots:
void qmlEngineAvailable(QQmlEngine *engine) {
engine->rootContext()->setContextProperty("TestControl", this);
}
void registerTestBackends() {
register_test_backends();
}
void unregisterTestBackends() {
unregister_test_backends();
}
void setSensorReading(const QmlSensor* qmlSensor, const QVariantMap& values) {
set_test_backend_reading(qmlSensor->sensor(), values);
}
void setSensorBusy(const QmlSensor* qmlSensor, bool busy) {
set_test_backend_busy(qmlSensor->sensor(), busy);
}
};
QUICK_TEST_MAIN_WITH_SETUP(tst_sensors_qmlquick, TestSetup)
#include "tst_sensors_qmlquick.moc"