WIP - working XR effect demo

This commit is contained in:
wheaney 2025-06-05 16:37:31 -07:00
parent 75e31f3f16
commit 458700798f
12 changed files with 117 additions and 49 deletions

View File

@ -59,7 +59,7 @@ if [ -z "$binary_path_arg" ]
then
# download and unzip the binary
echo "Downloading to: ${tmp_dir}/$FILE_NAME"
curl -L -O $binary_download_url
curl -L "$binary_download_url" > "$FILE_NAME"
else
FILE_NAME=$(basename $binary_path_arg)
if [[ "$binary_path_arg" = /* ]]; then

View File

@ -39,10 +39,11 @@ fi
if [ -z "$binary_path_arg" ]
then
# download and unzip the latest driver
echo "Downloading to: ${tmp_dir}/breezyVulkan-$ARCH.tar.gz"
curl -L -O $binary_download_url
# download and unzip the binary
binary_path_arg="breezyVulkan-$ARCH.tar.gz"
echo "Downloading to: ${tmp_dir}/$binary_path_arg"
curl -L "$binary_download_url" > "$binary_path_arg"
else
if [[ "$binary_path_arg" = /* ]]; then
abs_path="$binary_path_arg"

View File

@ -4,14 +4,15 @@
add_subdirectory(kcm)
kcoreaddons_add_plugin(kwin4_effect_cube INSTALL_NAMESPACE "kwin/effects/plugins/")
target_sources(kwin4_effect_cube PRIVATE
kcoreaddons_add_plugin(breezy_desktop_effect INSTALL_NAMESPACE "kwin/effects/plugins/")
target_sources(breezy_desktop_effect PRIVATE
cubeeffect.cpp
main.cpp
)
kconfig_add_kcfg_files(kwin4_effect_cube cubeconfig.kcfgc)
kconfig_add_kcfg_files(breezy_desktop_effect cubeconfig.kcfgc)
target_link_libraries(kwin4_effect_cube
target_include_directories(breezy_desktop_effect PRIVATE /usr/include/kwin)
target_link_libraries(breezy_desktop_effect
Qt6::Core
Qt6::Gui
Qt6::Quick
@ -23,7 +24,7 @@ target_link_libraries(kwin4_effect_cube
KF6::I18n
KF6::WindowSystem
kwineffects
KWin::kwin
)
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/cube)

View File

@ -6,21 +6,27 @@
#include "cubeeffect.h"
#include "cubeconfig.h"
#include "effect/effect.h"
#include "effect/effecthandler.h"
#include <QAction>
#include <QFile>
#include <QLoggingCategory>
#include <QQuickItem>
#include <QTimer>
#include <KGlobalAccel>
#include <KLocalizedString>
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
namespace KWin
{
CubeEffect::CubeEffect()
: m_shutdownTimer(new QTimer(this))
{
qCCritical(KWIN_XR) << "\t\t\tBreezy - constructor";
qmlRegisterUncreatableType<CubeEffect>("org.kde.kwin.effect.cube", 1, 0, "CubeEffect", QStringLiteral("Cube cannot be created in QML"));
m_shutdownTimer->setSingleShot(true);
@ -57,7 +63,7 @@ CubeEffect::CubeEffect()
void CubeEffect::reconfigure(ReconfigureFlags)
{
CubeConfig::self()->read();
setAnimationDuration(animationTime(200));
setAnimationDuration(animationTime(std::chrono::milliseconds(200)));
setCubeFaceDisplacement(CubeConfig::cubeFaceDisplacement());
setDistanceFactor(CubeConfig::distanceFactor() / 100.0);
setMouseInvertedX(CubeConfig::mouseInvertedX());
@ -75,11 +81,11 @@ void CubeEffect::reconfigure(ReconfigureFlags)
break;
}
for (const ElectricBorder &border : qAsConst(m_borderActivate)) {
for (const ElectricBorder &border : std::as_const(m_borderActivate)) {
effects->unreserveElectricBorder(border, this);
}
for (const ElectricBorder &border : qAsConst(m_touchBorderActivate)) {
for (const ElectricBorder &border : std::as_const(m_touchBorderActivate)) {
effects->unregisterTouchBorder(border, m_toggleAction);
}
@ -99,7 +105,7 @@ void CubeEffect::reconfigure(ReconfigureFlags)
}
}
QVariantMap CubeEffect::initialProperties(EffectScreen *screen)
QVariantMap CubeEffect::initialProperties(Output *screen)
{
return QVariantMap{
{QStringLiteral("effect"), QVariant::fromValue(this)},
@ -137,6 +143,7 @@ void CubeEffect::toggle()
if (isRunning()) {
deactivate();
} else {
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
activate();
}
}
@ -146,7 +153,7 @@ void CubeEffect::activate()
if (effects->isScreenLocked()) {
return;
}
if (effects->numberOfDesktops() < 3) {
if (effects->desktops().size() < 3) {
return;
}
@ -159,8 +166,8 @@ void CubeEffect::deactivate()
return;
}
const QList<EffectScreen *> screens = effects->screens();
for (EffectScreen *screen : screens) {
const QList<Output *> screens = effects->screens();
for (Output *screen : screens) {
if (QuickSceneView *view = viewForScreen(screen)) {
QMetaObject::invokeMethod(view->rootItem(), "stop");
}
@ -283,18 +290,74 @@ QQuaternion CubeEffect::xrRotation() const {
}
void CubeEffect::updateXrRotation() {
// Example: Read quaternion from /dev/shm/breezy_xr_quat (float32[4], binary)
QFile shmFile("/dev/shm/breezy_xr_quat");
if (shmFile.open(QIODevice::ReadOnly)) {
float data[4];
if (shmFile.read(reinterpret_cast<char*>(data), sizeof(data)) == sizeof(data)) {
QQuaternion quat(data[3], data[0], data[1], data[2]); // w, x, y, z
if (quat != m_xrRotation) {
m_xrRotation = quat;
Q_EMIT xrRotationChanged();
}
}
shmFile.close();
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
QFile shmFile(shmPath);
if (!shmFile.open(QIODevice::ReadOnly)) {
return;
}
QByteArray buffer = shmFile.readAll();
shmFile.close();
if (buffer.size() < 64) { // Minimum expected size based on the data structure
return;
}
// Create a data view for reading binary data
const char* data = buffer.constData();
// Use proper data positions based on the original GJS layout
// VERSION at offset 0, ENABLED at offset 1, etc.
// Read version and enabled flags at their correct positions
quint8 version = static_cast<quint8>(data[0]); // VERSION at offset 0
quint8 enabledFlag = static_cast<quint8>(data[1]); // ENABLED at offset 1
// DISPLAY_FOV is at offset: 1 + 1 + (4*4) + (4*2) = 26
float displayFov;
memcpy(&displayFov, data + 26, sizeof(float));
// EPOCH_MS is at offset: 26 + 4 + 4 + 1 + 1 + 1 + (4*16) = 101
quint64 imuDateMs;
memcpy(&imuDateMs, data + 101, sizeof(quint64));
imuDateMs = qFromLittleEndian(imuDateMs);
// IMU_QUAT_DATA is at offset: 101 + 8 = 109
float imuData[4];
memcpy(imuData, data + 109, sizeof(imuData));
// Validate data
const quint64 currentTimeMs = QDateTime::currentMSecsSinceEpoch();
const bool validKeepAlive = (currentTimeMs - imuDateMs) < 5000; // 5 second timeout
const bool validData = validKeepAlive && displayFov != 0.0f;
const quint8 expectedVersion = 4; // Define expected data layout version
const bool enabled = (enabledFlag != 0) && (version == expectedVersion) && validData;
if (!enabled) {
return;
}
qCCritical(KWIN_XR) << "\t\t\tBreezy" << "version:" << version
<< " enabledFlag:" << enabledFlag
<< " currentTimeMs:" << currentTimeMs
<< " imuDateMs:" << imuDateMs;
// Check for reset state (identity quaternion)
const bool imuResetState = (imuData[0] == 0.0f && imuData[1] == 0.0f &&
imuData[2] == 0.0f && imuData[3] == 1.0f);
if (imuResetState) {
return;
}
qCCritical(KWIN_XR) << "\t\t\tBreezy - here 5";
// Create quaternion (w, x, y, z)
QQuaternion quat(imuData[3], imuData[0], imuData[1], imuData[2]);
if (quat != m_xrRotation) {
qCCritical(KWIN_XR) << "\t\t\tBreezy - here 6";
m_xrRotation = quat;
Q_EMIT xrRotationChanged();
}
}

View File

@ -6,8 +6,9 @@
#pragma once
#include <libkwineffects/kwinquickeffect.h>
#include <effect/quickeffect.h>
#include <QAction>
#include <QKeySequence>
#include <QQuaternion>
@ -85,7 +86,7 @@ Q_SIGNALS:
void xrRotationChanged();
protected:
QVariantMap initialProperties(EffectScreen *screen) override;
QVariantMap initialProperties(Output *screen) override;
private:
void realDeactivate();

View File

@ -2,13 +2,13 @@
#
# SPDX-License-Identifier: BSD-3-Clause
set(kwin_cube_config_SOURCES cubeeffectkcm.cpp)
ki18n_wrap_ui(kwin_cube_config_SOURCES cubeeffectkcm.ui)
qt_add_dbus_interface(kwin_cube_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
set(breezy_desktop_config_SOURCES cubeeffectkcm.cpp)
ki18n_wrap_ui(breezy_desktop_config_SOURCES cubeeffectkcm.ui)
qt_add_dbus_interface(breezy_desktop_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
kcoreaddons_add_plugin(kwin_cube_config INSTALL_NAMESPACE "kwin/effects/configs" SOURCES ${kwin_cube_config_SOURCES})
kconfig_add_kcfg_files(kwin_cube_config ../cubeconfig.kcfgc)
target_link_libraries(kwin_cube_config
kcoreaddons_add_plugin(breezy_desktop_config INSTALL_NAMESPACE "kwin/effects/configs" SOURCES ${breezy_desktop_config_SOURCES})
kconfig_add_kcfg_files(breezy_desktop_config ../cubeconfig.kcfgc)
target_link_libraries(breezy_desktop_config
KF6::ConfigCore
KF6::ConfigGui
KF6::ConfigWidgets

View File

@ -20,7 +20,7 @@
K_PLUGIN_CLASS(CubeEffectConfig)
CubeEffectConfig::CubeEffectConfig(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: KCModule(parent, data, args)
: KCModule(parent, data)
{
ui.setupUi(widget());
addConfig(CubeConfig::self(), widget());

View File

@ -2,17 +2,16 @@
"KPlugin": {
"Authors": [
{
"Email": "vlad.zahorodnii@kde.org",
"Name": "Vlad Zahorodnii"
"Email": "wayne@xronlinux.com",
"Name": "Wayne Heaney"
}
],
"Category": "Window Management",
"Description": "Arrange desktops in a virtual cube",
"EnabledByDefault": false,
"Id": "cube",
"Category": "XR",
"Description": "Breezy Desktop XR Effect",
"EnabledByDefault": true,
"License": "GPL",
"Name": "Cube"
"Name": "Breezy Desktop XR"
},
"X-KDE-ConfigModule": "kwin_cube_config",
"X-KDE-ConfigModule": "breezy_desktop_config",
"X-KWin-Border-Activate": true
}

View File

@ -83,14 +83,17 @@ Item {
}
function updateCamera() {
const eulerRotation = root.rotation.toEulerAngles();
const theta = (eulerRotation.x + 90) * Math.PI / 180;
const phi = eulerRotation.y * Math.PI / 180;
// convert NWU to EUS by passing root.rotation values: w, -y, z, -x
let effectiveRotation = Qt.quaternion(root.rotation.scalar, -root.rotation.y, root.rotation.z, -root.rotation.x);
const eulerRotation = effectiveRotation.toEulerAngles();
const theta = 90 * Math.PI / 180;
const phi = 0.0;
camera.position = Qt.vector3d(radius * Math.sin(phi) * Math.sin(theta),
radius * Math.cos(theta),
radius * Math.cos(phi) * Math.sin(theta));
camera.rotation = root.rotation;
camera.rotation = effectiveRotation;
}
// Add property to receive XR rotation from effect