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
|
||||
{
|
||||
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
|
||||
constexpr int UINT8_SIZE = sizeof(uint8_t);
|
||||
constexpr int BOOL_SIZE = UINT8_SIZE;
|
||||
|
|
@ -63,10 +66,6 @@ BreezyDesktopEffect::BreezyDesktopEffect()
|
|||
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"));
|
||||
|
||||
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;
|
||||
m_toggleAction = new QAction(this);
|
||||
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"))));
|
||||
|
||||
// Monitor the IMU file for changes, even if it doesn't exist at startup
|
||||
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
||||
const QString shmDir = QStringLiteral("/dev/shm");
|
||||
m_imuRotationFileWatcher = new QFileSystemWatcher(this);
|
||||
if (QFile::exists(shmPath)) {
|
||||
m_imuRotationFileWatcher->addPath(shmPath);
|
||||
} else {
|
||||
m_imuRotationFileWatcher->addPath(shmDir);
|
||||
connect(m_imuRotationFileWatcher, &QFileSystemWatcher::directoryChanged, this, [this, shmPath](const QString &) {
|
||||
if (QFile::exists(shmPath) && !m_imuRotationFileWatcher->files().contains(shmPath)) {
|
||||
m_imuRotationFileWatcher->addPath(shmPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
connect(m_imuRotationFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updateImuRotation);
|
||||
m_shmDirectoryWatcher = new QFileSystemWatcher(this);
|
||||
m_shmDirectoryWatcher->addPath(DataView::SHM_DIR);
|
||||
|
||||
m_shmFileWatcher = new QFileSystemWatcher(this);
|
||||
|
||||
// Setup file watcher with recreation detection
|
||||
auto setupFileWatcher = [this]() {
|
||||
if (QFile::exists(DataView::SHM_PATH) && (
|
||||
m_imuTimestamp == 0 ||
|
||||
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);
|
||||
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);
|
||||
connect(m_cursorUpdateTimer, &QTimer::timeout, this, &BreezyDesktopEffect::updateCursorPos);
|
||||
|
|
@ -131,21 +140,17 @@ int BreezyDesktopEffect::requestedEffectChainPosition() const
|
|||
|
||||
void BreezyDesktopEffect::toggle()
|
||||
{
|
||||
if (isRunning()) {
|
||||
deactivate();
|
||||
} else {
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
||||
activate();
|
||||
}
|
||||
// TODO update this to use a persistent on/off value
|
||||
}
|
||||
|
||||
void BreezyDesktopEffect::activate()
|
||||
{
|
||||
if (effects->isScreenLocked()) {
|
||||
return;
|
||||
}
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
||||
|
||||
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
|
||||
// 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()
|
||||
{
|
||||
if (m_shutdownTimer->isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - deactivate";
|
||||
disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
|
||||
m_cursorUpdateTimer->stop();
|
||||
showCursor();
|
||||
|
|
@ -173,11 +175,12 @@ void BreezyDesktopEffect::deactivate()
|
|||
}
|
||||
}
|
||||
|
||||
m_shutdownTimer->start(animationDuration());
|
||||
realDeactivate();
|
||||
}
|
||||
|
||||
void BreezyDesktopEffect::realDeactivate()
|
||||
{
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - realDeactivate";
|
||||
setRunning(false);
|
||||
}
|
||||
|
||||
|
|
@ -202,6 +205,10 @@ QColor BreezyDesktopEffect::backgroundColor() const {
|
|||
return QColor(Qt::black);
|
||||
}
|
||||
|
||||
bool BreezyDesktopEffect::isEnabled() const {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
bool BreezyDesktopEffect::imuResetState() const {
|
||||
return m_imuResetState;
|
||||
}
|
||||
|
|
@ -242,9 +249,27 @@ bool BreezyDesktopEffect::customBannerEnabled() const {
|
|||
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?
|
||||
static qint64 lastConfigUpdate = 0;
|
||||
void BreezyDesktopEffect::updateImuRotation() {
|
||||
static qint64 activatedAt = 0;
|
||||
void BreezyDesktopEffect::updateImuRotation() {
|
||||
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
||||
QFile shmFile(shmPath);
|
||||
if (!shmFile.open(QIODevice::ReadOnly)) {
|
||||
|
|
@ -252,12 +277,13 @@ void BreezyDesktopEffect::updateImuRotation() {
|
|||
}
|
||||
QByteArray buffer = shmFile.readAll();
|
||||
shmFile.close();
|
||||
if (buffer.size() < 64) {
|
||||
return;
|
||||
}
|
||||
if (buffer.size() != DataView::LENGTH) return;
|
||||
|
||||
const char* data = buffer.constData();
|
||||
uint8_t version = static_cast<uint8_t>(data[0]);
|
||||
uint8_t enabledFlag = static_cast<uint8_t>(data[1]);
|
||||
if (!checkParityByte(data)) return;
|
||||
|
||||
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;
|
||||
memcpy(&imuDateMs, data + DataView::IMU_DATE_MS[DataView::OFFSET_INDEX], sizeof(imuDateMs));
|
||||
imuDateMs = qFromLittleEndian(imuDateMs);
|
||||
|
|
@ -302,14 +328,35 @@ void BreezyDesktopEffect::updateImuRotation() {
|
|||
const bool validKeepAlive = (currentTimeMs - imuDateMs) < 5000;
|
||||
const bool validData = validKeepAlive && m_diagonalFOV != 0.0f;
|
||||
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 (isRunning()) {
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - deactivate due to disabled";
|
||||
// give a grace period after enabling the effect
|
||||
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();
|
||||
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();
|
||||
|
||||
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_imuTimestamp = imuDateMs;
|
||||
if (!isRunning()) {
|
||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
||||
activate();
|
||||
}
|
||||
Q_EMIT imuRotationsChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace KWin
|
|||
Q_PROPERTY(qreal distanceFactor READ distanceFactor NOTIFY distanceFactorChanged)
|
||||
Q_PROPERTY(BackgroundMode backgroundMode READ backgroundMode NOTIFY backgroundModeChanged)
|
||||
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(QList<QQuaternion> imuRotations READ imuRotations NOTIFY imuRotationsChanged)
|
||||
Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs NOTIFY imuRotationsChanged)
|
||||
|
|
@ -53,6 +54,10 @@ namespace KWin
|
|||
QString cursorImageSource() const;
|
||||
QPointF cursorPos() const;
|
||||
|
||||
bool isEnabled() const;
|
||||
QList<QQuaternion> imuRotations() const;
|
||||
quint32 imuTimeElapsedMs() const;
|
||||
quint64 imuTimestamp() const;
|
||||
bool imuResetState() const;
|
||||
QList<qreal> lookAheadConfig() const;
|
||||
QList<quint32> displayResolution() const;
|
||||
|
|
@ -64,10 +69,6 @@ namespace KWin
|
|||
void showCursor();
|
||||
void hideCursor();
|
||||
|
||||
QList<QQuaternion> imuRotations() const;
|
||||
quint32 imuTimeElapsedMs() const;
|
||||
quint64 imuTimestamp() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
|
@ -83,6 +84,7 @@ namespace KWin
|
|||
void skyboxChanged();
|
||||
void backgroundModeChanged();
|
||||
void backgroundColorChanged();
|
||||
void enabledStateChanged();
|
||||
void imuRotationsChanged();
|
||||
void cursorImageChanged();
|
||||
void cursorPosChanged();
|
||||
|
|
@ -93,6 +95,7 @@ namespace KWin
|
|||
|
||||
private:
|
||||
void realDeactivate();
|
||||
bool checkParityByte(const char* data);
|
||||
|
||||
QTimer *m_shutdownTimer;
|
||||
QAction *m_toggleAction = nullptr;
|
||||
|
|
@ -101,17 +104,19 @@ namespace KWin
|
|||
QList<ElectricBorder> m_touchBorderActivate;
|
||||
QString m_cursorImageSource;
|
||||
|
||||
bool m_enabled = false;
|
||||
bool m_imuResetState;
|
||||
QList<QQuaternion> m_imuRotations;
|
||||
quint32 m_imuTimeElapsedMs;
|
||||
quint64 m_imuTimestamp;
|
||||
quint64 m_imuTimestamp = 0;
|
||||
QList<qreal> m_lookAheadConfig;
|
||||
QList<quint32> m_displayResolution;
|
||||
qreal m_diagonalFOV;
|
||||
qreal m_lensDistanceRatio;
|
||||
bool m_sbsEnabled;
|
||||
bool m_customBannerEnabled;
|
||||
QFileSystemWatcher *m_imuRotationFileWatcher = nullptr;
|
||||
QFileSystemWatcher *m_shmFileWatcher = nullptr;
|
||||
QFileSystemWatcher *m_shmDirectoryWatcher = nullptr;
|
||||
QPointF m_cursorPos;
|
||||
QTimer *m_cursorUpdateTimer = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,22 +11,12 @@ Item {
|
|||
required property QtObject effect
|
||||
required property QtObject targetScreen
|
||||
|
||||
property bool animationEnabled: false
|
||||
|
||||
function start() {
|
||||
root.animationEnabled = true;
|
||||
}
|
||||
|
||||
function stop() {
|
||||
}
|
||||
|
||||
View3D {
|
||||
id: view
|
||||
anchors.fill: parent
|
||||
environment: SceneEnvironment {
|
||||
antialiasingMode: SceneEnvironment.MSAA
|
||||
}
|
||||
|
||||
|
||||
PerspectiveCamera {
|
||||
id: camera
|
||||
frustumCullingEnabled: false
|
||||
|
|
@ -42,6 +32,6 @@ Item {
|
|||
camera: camera
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: start();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue