Add data validity and parity checks, improve device detection on multiple plug and unplug events
This commit is contained in:
parent
c266e3cbd3
commit
f5cbd9281b
|
|
@ -21,6 +21,9 @@ Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
|
||||||
|
|
||||||
namespace DataView
|
namespace DataView
|
||||||
{
|
{
|
||||||
|
const QString SHM_DIR = QStringLiteral("/dev/shm");
|
||||||
|
const QString SHM_PATH = SHM_DIR + QStringLiteral("/breezy_desktop_imu");
|
||||||
|
|
||||||
// Helper constants and functions for shared memory buffer offsets
|
// Helper constants and functions for shared memory buffer offsets
|
||||||
constexpr int UINT8_SIZE = sizeof(uint8_t);
|
constexpr int UINT8_SIZE = sizeof(uint8_t);
|
||||||
constexpr int BOOL_SIZE = UINT8_SIZE;
|
constexpr int BOOL_SIZE = UINT8_SIZE;
|
||||||
|
|
@ -63,10 +66,6 @@ BreezyDesktopEffect::BreezyDesktopEffect()
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - constructor";
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - constructor";
|
||||||
qmlRegisterUncreatableType<BreezyDesktopEffect>("org.kde.kwin.effect.breezy_desktop", 1, 0, "BreezyDesktopEffect", QStringLiteral("BreezyDesktop cannot be created in QML"));
|
qmlRegisterUncreatableType<BreezyDesktopEffect>("org.kde.kwin.effect.breezy_desktop", 1, 0, "BreezyDesktopEffect", QStringLiteral("BreezyDesktop cannot be created in QML"));
|
||||||
|
|
||||||
m_shutdownTimer->setSingleShot(true);
|
|
||||||
connect(m_shutdownTimer, &QTimer::timeout, this, &BreezyDesktopEffect::realDeactivate);
|
|
||||||
connect(effects, &EffectsHandler::screenAboutToLock, this, &BreezyDesktopEffect::realDeactivate);
|
|
||||||
|
|
||||||
const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_B;
|
const QKeySequence defaultToggleShortcut = Qt::META | Qt::Key_B;
|
||||||
m_toggleAction = new QAction(this);
|
m_toggleAction = new QAction(this);
|
||||||
m_toggleAction->setObjectName(QStringLiteral("BreezyDesktop"));
|
m_toggleAction->setObjectName(QStringLiteral("BreezyDesktop"));
|
||||||
|
|
@ -90,20 +89,30 @@ BreezyDesktopEffect::BreezyDesktopEffect()
|
||||||
setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/breezy_desktop/qml/main.qml"))));
|
setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/breezy_desktop/qml/main.qml"))));
|
||||||
|
|
||||||
// Monitor the IMU file for changes, even if it doesn't exist at startup
|
// Monitor the IMU file for changes, even if it doesn't exist at startup
|
||||||
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
m_shmDirectoryWatcher = new QFileSystemWatcher(this);
|
||||||
const QString shmDir = QStringLiteral("/dev/shm");
|
m_shmDirectoryWatcher->addPath(DataView::SHM_DIR);
|
||||||
m_imuRotationFileWatcher = new QFileSystemWatcher(this);
|
|
||||||
if (QFile::exists(shmPath)) {
|
m_shmFileWatcher = new QFileSystemWatcher(this);
|
||||||
m_imuRotationFileWatcher->addPath(shmPath);
|
|
||||||
} else {
|
// Setup file watcher with recreation detection
|
||||||
m_imuRotationFileWatcher->addPath(shmDir);
|
auto setupFileWatcher = [this]() {
|
||||||
connect(m_imuRotationFileWatcher, &QFileSystemWatcher::directoryChanged, this, [this, shmPath](const QString &) {
|
if (QFile::exists(DataView::SHM_PATH) && (
|
||||||
if (QFile::exists(shmPath) && !m_imuRotationFileWatcher->files().contains(shmPath)) {
|
m_imuTimestamp == 0 ||
|
||||||
m_imuRotationFileWatcher->addPath(shmPath);
|
QDateTime::currentMSecsSinceEpoch() - m_imuTimestamp > 50 || // file may have been deleted and recreated
|
||||||
}
|
!m_shmFileWatcher->files().contains(DataView::SHM_PATH)
|
||||||
});
|
)) {
|
||||||
}
|
m_shmFileWatcher->removePath(DataView::SHM_PATH);
|
||||||
connect(m_imuRotationFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updateImuRotation);
|
disconnect(m_shmFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updateImuRotation);
|
||||||
|
m_shmFileWatcher->addPath(DataView::SHM_PATH);
|
||||||
|
connect(m_shmFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updateImuRotation);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle directory changes (file creation/recreation)
|
||||||
|
connect(m_shmDirectoryWatcher, &QFileSystemWatcher::directoryChanged, this, setupFileWatcher);
|
||||||
|
|
||||||
|
// Initial setup
|
||||||
|
setupFileWatcher();
|
||||||
|
|
||||||
m_cursorUpdateTimer = new QTimer(this);
|
m_cursorUpdateTimer = new QTimer(this);
|
||||||
connect(m_cursorUpdateTimer, &QTimer::timeout, this, &BreezyDesktopEffect::updateCursorPos);
|
connect(m_cursorUpdateTimer, &QTimer::timeout, this, &BreezyDesktopEffect::updateCursorPos);
|
||||||
|
|
@ -131,21 +140,17 @@ int BreezyDesktopEffect::requestedEffectChainPosition() const
|
||||||
|
|
||||||
void BreezyDesktopEffect::toggle()
|
void BreezyDesktopEffect::toggle()
|
||||||
{
|
{
|
||||||
if (isRunning()) {
|
// TODO update this to use a persistent on/off value
|
||||||
deactivate();
|
|
||||||
} else {
|
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
|
||||||
activate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::activate()
|
void BreezyDesktopEffect::activate()
|
||||||
{
|
{
|
||||||
if (effects->isScreenLocked()) {
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRunning(true);
|
if (!isRunning()) setRunning(true);
|
||||||
|
|
||||||
|
connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
||||||
|
m_cursorUpdateTimer->start();
|
||||||
|
|
||||||
// QuickSceneEffect grabs the keyboard and mouse input, which pulls focus away from the active window
|
// QuickSceneEffect grabs the keyboard and mouse input, which pulls focus away from the active window
|
||||||
// and doesn't allow for interaction with anything on the desktop. These two calls fix that.
|
// and doesn't allow for interaction with anything on the desktop. These two calls fix that.
|
||||||
|
|
@ -158,10 +163,7 @@ void BreezyDesktopEffect::activate()
|
||||||
|
|
||||||
void BreezyDesktopEffect::deactivate()
|
void BreezyDesktopEffect::deactivate()
|
||||||
{
|
{
|
||||||
if (m_shutdownTimer->isActive()) {
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - deactivate";
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
||||||
m_cursorUpdateTimer->stop();
|
m_cursorUpdateTimer->stop();
|
||||||
showCursor();
|
showCursor();
|
||||||
|
|
@ -173,11 +175,12 @@ void BreezyDesktopEffect::deactivate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_shutdownTimer->start(animationDuration());
|
realDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreezyDesktopEffect::realDeactivate()
|
void BreezyDesktopEffect::realDeactivate()
|
||||||
{
|
{
|
||||||
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - realDeactivate";
|
||||||
setRunning(false);
|
setRunning(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,6 +205,10 @@ QColor BreezyDesktopEffect::backgroundColor() const {
|
||||||
return QColor(Qt::black);
|
return QColor(Qt::black);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BreezyDesktopEffect::isEnabled() const {
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
bool BreezyDesktopEffect::imuResetState() const {
|
bool BreezyDesktopEffect::imuResetState() const {
|
||||||
return m_imuResetState;
|
return m_imuResetState;
|
||||||
}
|
}
|
||||||
|
|
@ -242,8 +249,26 @@ bool BreezyDesktopEffect::customBannerEnabled() const {
|
||||||
return m_customBannerEnabled;
|
return m_customBannerEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
||||||
|
const uint8_t parityByte = static_cast<uint8_t>(data[DataView::IMU_PARITY_BYTE[DataView::OFFSET_INDEX]]);
|
||||||
|
uint8_t parity = 0;
|
||||||
|
|
||||||
|
const int dateBytes = DataView::IMU_DATE_MS[DataView::COUNT_INDEX] * DataView::IMU_DATE_MS[DataView::SIZE_INDEX];
|
||||||
|
for (int i = 0; i < dateBytes; ++i) {
|
||||||
|
parity ^= static_cast<uint8_t>(data[DataView::IMU_DATE_MS[DataView::OFFSET_INDEX] + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int quatBytes = DataView::IMU_QUAT_DATA[DataView::COUNT_INDEX] * DataView::IMU_QUAT_DATA[DataView::SIZE_INDEX];
|
||||||
|
for (int i = 0; i < quatBytes; ++i) {
|
||||||
|
parity ^= static_cast<uint8_t>(data[DataView::IMU_QUAT_DATA[DataView::OFFSET_INDEX] + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parityByte == parity;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO - can this be something callable from the camera qml code, so it's pulled only when needed?
|
// TODO - can this be something callable from the camera qml code, so it's pulled only when needed?
|
||||||
static qint64 lastConfigUpdate = 0;
|
static qint64 lastConfigUpdate = 0;
|
||||||
|
static qint64 activatedAt = 0;
|
||||||
void BreezyDesktopEffect::updateImuRotation() {
|
void BreezyDesktopEffect::updateImuRotation() {
|
||||||
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
||||||
QFile shmFile(shmPath);
|
QFile shmFile(shmPath);
|
||||||
|
|
@ -252,12 +277,13 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
}
|
}
|
||||||
QByteArray buffer = shmFile.readAll();
|
QByteArray buffer = shmFile.readAll();
|
||||||
shmFile.close();
|
shmFile.close();
|
||||||
if (buffer.size() < 64) {
|
if (buffer.size() != DataView::LENGTH) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const char* data = buffer.constData();
|
const char* data = buffer.constData();
|
||||||
uint8_t version = static_cast<uint8_t>(data[0]);
|
if (!checkParityByte(data)) return;
|
||||||
uint8_t enabledFlag = static_cast<uint8_t>(data[1]);
|
|
||||||
|
uint8_t version = static_cast<uint8_t>(data[DataView::VERSION[DataView::OFFSET_INDEX]]);
|
||||||
|
uint8_t enabledFlag = static_cast<uint8_t>(data[DataView::ENABLED[DataView::OFFSET_INDEX]]);
|
||||||
uint64_t imuDateMs;
|
uint64_t imuDateMs;
|
||||||
memcpy(&imuDateMs, data + DataView::IMU_DATE_MS[DataView::OFFSET_INDEX], sizeof(imuDateMs));
|
memcpy(&imuDateMs, data + DataView::IMU_DATE_MS[DataView::OFFSET_INDEX], sizeof(imuDateMs));
|
||||||
imuDateMs = qFromLittleEndian(imuDateMs);
|
imuDateMs = qFromLittleEndian(imuDateMs);
|
||||||
|
|
@ -302,14 +328,35 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
const bool validKeepAlive = (currentTimeMs - imuDateMs) < 5000;
|
const bool validKeepAlive = (currentTimeMs - imuDateMs) < 5000;
|
||||||
const bool validData = validKeepAlive && m_diagonalFOV != 0.0f;
|
const bool validData = validKeepAlive && m_diagonalFOV != 0.0f;
|
||||||
const uint8_t expectedVersion = 4;
|
const uint8_t expectedVersion = 4;
|
||||||
const bool enabled = (enabledFlag != 0) && (version == expectedVersion) && validData;
|
bool enabledFlagSet = (enabledFlag != 0);
|
||||||
|
bool validVersion = (version == expectedVersion);
|
||||||
|
const bool wasEnabled = m_enabled;
|
||||||
|
const bool enabled = enabledFlagSet && validVersion && validData;
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
if (isRunning()) {
|
// give a grace period after enabling the effect
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - deactivate due to disabled";
|
if (wasEnabled && (currentTimeMs - activatedAt > 1000)) {
|
||||||
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - disabling effect; currentTimeMs:" << currentTimeMs
|
||||||
|
<< "imuDateMs:" << imuDateMs
|
||||||
|
<< "enabledFlag:" << enabledFlag
|
||||||
|
<< "version:" << version
|
||||||
|
<< "diagonalFOV:" << m_diagonalFOV;
|
||||||
deactivate();
|
deactivate();
|
||||||
|
m_enabled = false;
|
||||||
|
Q_EMIT enabledStateChanged();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
} else if (!wasEnabled) {
|
||||||
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - enabling effect; currentTimeMs:" << currentTimeMs
|
||||||
|
<< "imuDateMs:" << imuDateMs
|
||||||
|
<< "enabledFlag:" << enabledFlag
|
||||||
|
<< "version:" << version
|
||||||
|
<< "diagonalFOV:" << m_diagonalFOV;
|
||||||
|
activate();
|
||||||
|
m_enabled = true;
|
||||||
|
Q_EMIT enabledStateChanged();
|
||||||
|
activatedAt = currentTimeMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateConfig) Q_EMIT devicePropertiesChanged();
|
if (updateConfig) Q_EMIT devicePropertiesChanged();
|
||||||
|
|
||||||
float imuData[4 * DataView::IMU_QUAT_ENTRIES]; // 4 quaternion-sized rows
|
float imuData[4 * DataView::IMU_QUAT_ENTRIES]; // 4 quaternion-sized rows
|
||||||
|
|
@ -337,10 +384,6 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
m_imuTimeElapsedMs = static_cast<quint32>(imuData[imuDataOffset + 0] - imuData[imuDataOffset + 1]);
|
m_imuTimeElapsedMs = static_cast<quint32>(imuData[imuDataOffset + 0] - imuData[imuDataOffset + 1]);
|
||||||
|
|
||||||
m_imuTimestamp = imuDateMs;
|
m_imuTimestamp = imuDateMs;
|
||||||
if (!isRunning()) {
|
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
|
||||||
activate();
|
|
||||||
}
|
|
||||||
Q_EMIT imuRotationsChanged();
|
Q_EMIT imuRotationsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ namespace KWin
|
||||||
Q_PROPERTY(qreal distanceFactor READ distanceFactor NOTIFY distanceFactorChanged)
|
Q_PROPERTY(qreal distanceFactor READ distanceFactor NOTIFY distanceFactorChanged)
|
||||||
Q_PROPERTY(BackgroundMode backgroundMode READ backgroundMode NOTIFY backgroundModeChanged)
|
Q_PROPERTY(BackgroundMode backgroundMode READ backgroundMode NOTIFY backgroundModeChanged)
|
||||||
Q_PROPERTY(QColor backgroundColor READ backgroundColor NOTIFY backgroundColorChanged)
|
Q_PROPERTY(QColor backgroundColor READ backgroundColor NOTIFY backgroundColorChanged)
|
||||||
|
Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledStateChanged)
|
||||||
Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuRotationsChanged)
|
Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuRotationsChanged)
|
||||||
Q_PROPERTY(QList<QQuaternion> imuRotations READ imuRotations NOTIFY imuRotationsChanged)
|
Q_PROPERTY(QList<QQuaternion> imuRotations READ imuRotations NOTIFY imuRotationsChanged)
|
||||||
Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs NOTIFY imuRotationsChanged)
|
Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs NOTIFY imuRotationsChanged)
|
||||||
|
|
@ -53,6 +54,10 @@ namespace KWin
|
||||||
QString cursorImageSource() const;
|
QString cursorImageSource() const;
|
||||||
QPointF cursorPos() const;
|
QPointF cursorPos() const;
|
||||||
|
|
||||||
|
bool isEnabled() const;
|
||||||
|
QList<QQuaternion> imuRotations() const;
|
||||||
|
quint32 imuTimeElapsedMs() const;
|
||||||
|
quint64 imuTimestamp() const;
|
||||||
bool imuResetState() const;
|
bool imuResetState() const;
|
||||||
QList<qreal> lookAheadConfig() const;
|
QList<qreal> lookAheadConfig() const;
|
||||||
QList<quint32> displayResolution() const;
|
QList<quint32> displayResolution() const;
|
||||||
|
|
@ -64,10 +69,6 @@ namespace KWin
|
||||||
void showCursor();
|
void showCursor();
|
||||||
void hideCursor();
|
void hideCursor();
|
||||||
|
|
||||||
QList<QQuaternion> imuRotations() const;
|
|
||||||
quint32 imuTimeElapsedMs() const;
|
|
||||||
quint64 imuTimestamp() const;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void activate();
|
void activate();
|
||||||
void deactivate();
|
void deactivate();
|
||||||
|
|
@ -83,6 +84,7 @@ namespace KWin
|
||||||
void skyboxChanged();
|
void skyboxChanged();
|
||||||
void backgroundModeChanged();
|
void backgroundModeChanged();
|
||||||
void backgroundColorChanged();
|
void backgroundColorChanged();
|
||||||
|
void enabledStateChanged();
|
||||||
void imuRotationsChanged();
|
void imuRotationsChanged();
|
||||||
void cursorImageChanged();
|
void cursorImageChanged();
|
||||||
void cursorPosChanged();
|
void cursorPosChanged();
|
||||||
|
|
@ -93,6 +95,7 @@ namespace KWin
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void realDeactivate();
|
void realDeactivate();
|
||||||
|
bool checkParityByte(const char* data);
|
||||||
|
|
||||||
QTimer *m_shutdownTimer;
|
QTimer *m_shutdownTimer;
|
||||||
QAction *m_toggleAction = nullptr;
|
QAction *m_toggleAction = nullptr;
|
||||||
|
|
@ -101,17 +104,19 @@ namespace KWin
|
||||||
QList<ElectricBorder> m_touchBorderActivate;
|
QList<ElectricBorder> m_touchBorderActivate;
|
||||||
QString m_cursorImageSource;
|
QString m_cursorImageSource;
|
||||||
|
|
||||||
|
bool m_enabled = false;
|
||||||
bool m_imuResetState;
|
bool m_imuResetState;
|
||||||
QList<QQuaternion> m_imuRotations;
|
QList<QQuaternion> m_imuRotations;
|
||||||
quint32 m_imuTimeElapsedMs;
|
quint32 m_imuTimeElapsedMs;
|
||||||
quint64 m_imuTimestamp;
|
quint64 m_imuTimestamp = 0;
|
||||||
QList<qreal> m_lookAheadConfig;
|
QList<qreal> m_lookAheadConfig;
|
||||||
QList<quint32> m_displayResolution;
|
QList<quint32> m_displayResolution;
|
||||||
qreal m_diagonalFOV;
|
qreal m_diagonalFOV;
|
||||||
qreal m_lensDistanceRatio;
|
qreal m_lensDistanceRatio;
|
||||||
bool m_sbsEnabled;
|
bool m_sbsEnabled;
|
||||||
bool m_customBannerEnabled;
|
bool m_customBannerEnabled;
|
||||||
QFileSystemWatcher *m_imuRotationFileWatcher = nullptr;
|
QFileSystemWatcher *m_shmFileWatcher = nullptr;
|
||||||
|
QFileSystemWatcher *m_shmDirectoryWatcher = nullptr;
|
||||||
QPointF m_cursorPos;
|
QPointF m_cursorPos;
|
||||||
QTimer *m_cursorUpdateTimer = nullptr;
|
QTimer *m_cursorUpdateTimer = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,7 @@ Item {
|
||||||
required property QtObject effect
|
required property QtObject effect
|
||||||
required property QtObject targetScreen
|
required property QtObject targetScreen
|
||||||
|
|
||||||
property bool animationEnabled: false
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
root.animationEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stop() {
|
|
||||||
}
|
|
||||||
|
|
||||||
View3D {
|
View3D {
|
||||||
id: view
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
environment: SceneEnvironment {
|
environment: SceneEnvironment {
|
||||||
antialiasingMode: SceneEnvironment.MSAA
|
antialiasingMode: SceneEnvironment.MSAA
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue