WIP - working XR effect demo
This commit is contained in:
parent
75e31f3f16
commit
458700798f
|
|
@ -59,7 +59,7 @@ if [ -z "$binary_path_arg" ]
|
||||||
then
|
then
|
||||||
# download and unzip the binary
|
# download and unzip the binary
|
||||||
echo "Downloading to: ${tmp_dir}/$FILE_NAME"
|
echo "Downloading to: ${tmp_dir}/$FILE_NAME"
|
||||||
curl -L -O $binary_download_url
|
curl -L "$binary_download_url" > "$FILE_NAME"
|
||||||
else
|
else
|
||||||
FILE_NAME=$(basename $binary_path_arg)
|
FILE_NAME=$(basename $binary_path_arg)
|
||||||
if [[ "$binary_path_arg" = /* ]]; then
|
if [[ "$binary_path_arg" = /* ]]; then
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,11 @@ fi
|
||||||
|
|
||||||
if [ -z "$binary_path_arg" ]
|
if [ -z "$binary_path_arg" ]
|
||||||
then
|
then
|
||||||
# download and unzip the latest driver
|
# download and unzip the binary
|
||||||
echo "Downloading to: ${tmp_dir}/breezyVulkan-$ARCH.tar.gz"
|
|
||||||
curl -L -O $binary_download_url
|
|
||||||
binary_path_arg="breezyVulkan-$ARCH.tar.gz"
|
binary_path_arg="breezyVulkan-$ARCH.tar.gz"
|
||||||
|
echo "Downloading to: ${tmp_dir}/$binary_path_arg"
|
||||||
|
|
||||||
|
curl -L "$binary_download_url" > "$binary_path_arg"
|
||||||
else
|
else
|
||||||
if [[ "$binary_path_arg" = /* ]]; then
|
if [[ "$binary_path_arg" = /* ]]; then
|
||||||
abs_path="$binary_path_arg"
|
abs_path="$binary_path_arg"
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@
|
||||||
|
|
||||||
add_subdirectory(kcm)
|
add_subdirectory(kcm)
|
||||||
|
|
||||||
kcoreaddons_add_plugin(kwin4_effect_cube INSTALL_NAMESPACE "kwin/effects/plugins/")
|
kcoreaddons_add_plugin(breezy_desktop_effect INSTALL_NAMESPACE "kwin/effects/plugins/")
|
||||||
target_sources(kwin4_effect_cube PRIVATE
|
target_sources(breezy_desktop_effect PRIVATE
|
||||||
cubeeffect.cpp
|
cubeeffect.cpp
|
||||||
main.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::Core
|
||||||
Qt6::Gui
|
Qt6::Gui
|
||||||
Qt6::Quick
|
Qt6::Quick
|
||||||
|
|
@ -23,7 +24,7 @@ target_link_libraries(kwin4_effect_cube
|
||||||
KF6::I18n
|
KF6::I18n
|
||||||
KF6::WindowSystem
|
KF6::WindowSystem
|
||||||
|
|
||||||
kwineffects
|
KWin::kwin
|
||||||
)
|
)
|
||||||
|
|
||||||
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/cube)
|
install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/cube)
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,27 @@
|
||||||
|
|
||||||
#include "cubeeffect.h"
|
#include "cubeeffect.h"
|
||||||
#include "cubeconfig.h"
|
#include "cubeconfig.h"
|
||||||
|
#include "effect/effect.h"
|
||||||
|
#include "effect/effecthandler.h"
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QLoggingCategory>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <KGlobalAccel>
|
#include <KGlobalAccel>
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
CubeEffect::CubeEffect()
|
CubeEffect::CubeEffect()
|
||||||
: m_shutdownTimer(new QTimer(this))
|
: 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"));
|
qmlRegisterUncreatableType<CubeEffect>("org.kde.kwin.effect.cube", 1, 0, "CubeEffect", QStringLiteral("Cube cannot be created in QML"));
|
||||||
|
|
||||||
m_shutdownTimer->setSingleShot(true);
|
m_shutdownTimer->setSingleShot(true);
|
||||||
|
|
@ -57,7 +63,7 @@ CubeEffect::CubeEffect()
|
||||||
void CubeEffect::reconfigure(ReconfigureFlags)
|
void CubeEffect::reconfigure(ReconfigureFlags)
|
||||||
{
|
{
|
||||||
CubeConfig::self()->read();
|
CubeConfig::self()->read();
|
||||||
setAnimationDuration(animationTime(200));
|
setAnimationDuration(animationTime(std::chrono::milliseconds(200)));
|
||||||
setCubeFaceDisplacement(CubeConfig::cubeFaceDisplacement());
|
setCubeFaceDisplacement(CubeConfig::cubeFaceDisplacement());
|
||||||
setDistanceFactor(CubeConfig::distanceFactor() / 100.0);
|
setDistanceFactor(CubeConfig::distanceFactor() / 100.0);
|
||||||
setMouseInvertedX(CubeConfig::mouseInvertedX());
|
setMouseInvertedX(CubeConfig::mouseInvertedX());
|
||||||
|
|
@ -75,11 +81,11 @@ void CubeEffect::reconfigure(ReconfigureFlags)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ElectricBorder &border : qAsConst(m_borderActivate)) {
|
for (const ElectricBorder &border : std::as_const(m_borderActivate)) {
|
||||||
effects->unreserveElectricBorder(border, this);
|
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);
|
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{
|
return QVariantMap{
|
||||||
{QStringLiteral("effect"), QVariant::fromValue(this)},
|
{QStringLiteral("effect"), QVariant::fromValue(this)},
|
||||||
|
|
@ -137,6 +143,7 @@ void CubeEffect::toggle()
|
||||||
if (isRunning()) {
|
if (isRunning()) {
|
||||||
deactivate();
|
deactivate();
|
||||||
} else {
|
} else {
|
||||||
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - activate";
|
||||||
activate();
|
activate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +153,7 @@ void CubeEffect::activate()
|
||||||
if (effects->isScreenLocked()) {
|
if (effects->isScreenLocked()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (effects->numberOfDesktops() < 3) {
|
if (effects->desktops().size() < 3) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,8 +166,8 @@ void CubeEffect::deactivate()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<EffectScreen *> screens = effects->screens();
|
const QList<Output *> screens = effects->screens();
|
||||||
for (EffectScreen *screen : screens) {
|
for (Output *screen : screens) {
|
||||||
if (QuickSceneView *view = viewForScreen(screen)) {
|
if (QuickSceneView *view = viewForScreen(screen)) {
|
||||||
QMetaObject::invokeMethod(view->rootItem(), "stop");
|
QMetaObject::invokeMethod(view->rootItem(), "stop");
|
||||||
}
|
}
|
||||||
|
|
@ -283,18 +290,74 @@ QQuaternion CubeEffect::xrRotation() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CubeEffect::updateXrRotation() {
|
void CubeEffect::updateXrRotation() {
|
||||||
// Example: Read quaternion from /dev/shm/breezy_xr_quat (float32[4], binary)
|
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
||||||
QFile shmFile("/dev/shm/breezy_xr_quat");
|
QFile shmFile(shmPath);
|
||||||
if (shmFile.open(QIODevice::ReadOnly)) {
|
|
||||||
float data[4];
|
if (!shmFile.open(QIODevice::ReadOnly)) {
|
||||||
if (shmFile.read(reinterpret_cast<char*>(data), sizeof(data)) == sizeof(data)) {
|
return;
|
||||||
QQuaternion quat(data[3], data[0], data[1], data[2]); // w, x, y, z
|
}
|
||||||
if (quat != m_xrRotation) {
|
|
||||||
m_xrRotation = quat;
|
QByteArray buffer = shmFile.readAll();
|
||||||
Q_EMIT xrRotationChanged();
|
shmFile.close();
|
||||||
}
|
|
||||||
}
|
if (buffer.size() < 64) { // Minimum expected size based on the data structure
|
||||||
shmFile.close();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libkwineffects/kwinquickeffect.h>
|
#include <effect/quickeffect.h>
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QQuaternion>
|
#include <QQuaternion>
|
||||||
|
|
||||||
|
|
@ -85,7 +86,7 @@ Q_SIGNALS:
|
||||||
void xrRotationChanged();
|
void xrRotationChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariantMap initialProperties(EffectScreen *screen) override;
|
QVariantMap initialProperties(Output *screen) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void realDeactivate();
|
void realDeactivate();
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
set(kwin_cube_config_SOURCES cubeeffectkcm.cpp)
|
set(breezy_desktop_config_SOURCES cubeeffectkcm.cpp)
|
||||||
ki18n_wrap_ui(kwin_cube_config_SOURCES cubeeffectkcm.ui)
|
ki18n_wrap_ui(breezy_desktop_config_SOURCES cubeeffectkcm.ui)
|
||||||
qt_add_dbus_interface(kwin_cube_config_SOURCES ${KWIN_EFFECTS_INTERFACE} kwineffects_interface)
|
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})
|
kcoreaddons_add_plugin(breezy_desktop_config INSTALL_NAMESPACE "kwin/effects/configs" SOURCES ${breezy_desktop_config_SOURCES})
|
||||||
kconfig_add_kcfg_files(kwin_cube_config ../cubeconfig.kcfgc)
|
kconfig_add_kcfg_files(breezy_desktop_config ../cubeconfig.kcfgc)
|
||||||
target_link_libraries(kwin_cube_config
|
target_link_libraries(breezy_desktop_config
|
||||||
KF6::ConfigCore
|
KF6::ConfigCore
|
||||||
KF6::ConfigGui
|
KF6::ConfigGui
|
||||||
KF6::ConfigWidgets
|
KF6::ConfigWidgets
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
K_PLUGIN_CLASS(CubeEffectConfig)
|
K_PLUGIN_CLASS(CubeEffectConfig)
|
||||||
|
|
||||||
CubeEffectConfig::CubeEffectConfig(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
|
CubeEffectConfig::CubeEffectConfig(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
|
||||||
: KCModule(parent, data, args)
|
: KCModule(parent, data)
|
||||||
{
|
{
|
||||||
ui.setupUi(widget());
|
ui.setupUi(widget());
|
||||||
addConfig(CubeConfig::self(), widget());
|
addConfig(CubeConfig::self(), widget());
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,16 @@
|
||||||
"KPlugin": {
|
"KPlugin": {
|
||||||
"Authors": [
|
"Authors": [
|
||||||
{
|
{
|
||||||
"Email": "vlad.zahorodnii@kde.org",
|
"Email": "wayne@xronlinux.com",
|
||||||
"Name": "Vlad Zahorodnii"
|
"Name": "Wayne Heaney"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Category": "Window Management",
|
"Category": "XR",
|
||||||
"Description": "Arrange desktops in a virtual cube",
|
"Description": "Breezy Desktop XR Effect",
|
||||||
"EnabledByDefault": false,
|
"EnabledByDefault": true,
|
||||||
"Id": "cube",
|
|
||||||
"License": "GPL",
|
"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
|
"X-KWin-Border-Activate": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,17 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCamera() {
|
function updateCamera() {
|
||||||
const eulerRotation = root.rotation.toEulerAngles();
|
// convert NWU to EUS by passing root.rotation values: w, -y, z, -x
|
||||||
const theta = (eulerRotation.x + 90) * Math.PI / 180;
|
let effectiveRotation = Qt.quaternion(root.rotation.scalar, -root.rotation.y, root.rotation.z, -root.rotation.x);
|
||||||
const phi = eulerRotation.y * Math.PI / 180;
|
|
||||||
|
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),
|
camera.position = Qt.vector3d(radius * Math.sin(phi) * Math.sin(theta),
|
||||||
radius * Math.cos(theta),
|
radius * Math.cos(theta),
|
||||||
radius * Math.cos(phi) * Math.sin(theta));
|
radius * Math.cos(phi) * Math.sin(theta));
|
||||||
camera.rotation = root.rotation;
|
camera.rotation = effectiveRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add property to receive XR rotation from effect
|
// Add property to receive XR rotation from effect
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue