Add support for 6DoF (#138)
* Pull in driver and sombrero updates for 6DoF * Update Breezy GNOME to support 6DoF position * Update Breezy KDE to support 6DoF position
This commit is contained in:
parent
d9445942c9
commit
054296da56
|
|
@ -21,7 +21,7 @@ const IPC_FILE_PATH = "/dev/shm/breezy_desktop_imu";
|
||||||
const KEEPALIVE_REFRESH_INTERVAL_SEC = 1;
|
const KEEPALIVE_REFRESH_INTERVAL_SEC = 1;
|
||||||
|
|
||||||
// the driver should be using the same data layout version
|
// the driver should be using the same data layout version
|
||||||
const DATA_LAYOUT_VERSION = 4;
|
const DATA_LAYOUT_VERSION = 5;
|
||||||
|
|
||||||
// DataView info: [offset, size, count]
|
// DataView info: [offset, size, count]
|
||||||
const VERSION = [0, UINT8_SIZE, 1];
|
const VERSION = [0, UINT8_SIZE, 1];
|
||||||
|
|
@ -34,16 +34,17 @@ const SBS_ENABLED = [dataViewEnd(LENS_DISTANCE_RATIO), BOOL_SIZE, 1];
|
||||||
const CUSTOM_BANNER_ENABLED = [dataViewEnd(SBS_ENABLED), BOOL_SIZE, 1];
|
const CUSTOM_BANNER_ENABLED = [dataViewEnd(SBS_ENABLED), BOOL_SIZE, 1];
|
||||||
const SMOOTH_FOLLOW_ENABLED = [dataViewEnd(CUSTOM_BANNER_ENABLED), BOOL_SIZE, 1];
|
const SMOOTH_FOLLOW_ENABLED = [dataViewEnd(CUSTOM_BANNER_ENABLED), BOOL_SIZE, 1];
|
||||||
const SMOOTH_FOLLOW_ORIGIN_DATA = [dataViewEnd(SMOOTH_FOLLOW_ENABLED), FLOAT_SIZE, 16];
|
const SMOOTH_FOLLOW_ORIGIN_DATA = [dataViewEnd(SMOOTH_FOLLOW_ENABLED), FLOAT_SIZE, 16];
|
||||||
const EPOCH_MS = [dataViewEnd(SMOOTH_FOLLOW_ORIGIN_DATA), UINT_SIZE, 2];
|
const POSE_POSITION = [dataViewEnd(SMOOTH_FOLLOW_ORIGIN_DATA), FLOAT_SIZE, 3];
|
||||||
const IMU_QUAT_DATA = [dataViewEnd(EPOCH_MS), FLOAT_SIZE, 16];
|
const EPOCH_MS = [dataViewEnd(POSE_POSITION), UINT_SIZE, 2];
|
||||||
const IMU_PARITY_BYTE = [dataViewEnd(IMU_QUAT_DATA), UINT8_SIZE, 1];
|
const POSE_ORIENTATION = [dataViewEnd(EPOCH_MS), FLOAT_SIZE, 16];
|
||||||
|
const IMU_PARITY_BYTE = [dataViewEnd(POSE_ORIENTATION), UINT8_SIZE, 1];
|
||||||
const DATA_VIEW_LENGTH = dataViewEnd(IMU_PARITY_BYTE);
|
const DATA_VIEW_LENGTH = dataViewEnd(IMU_PARITY_BYTE);
|
||||||
|
|
||||||
function checkParityByte(dataView) {
|
function checkParityByte(dataView) {
|
||||||
const parityByte = dataViewUint8(dataView, IMU_PARITY_BYTE);
|
const parityByte = dataViewUint8(dataView, IMU_PARITY_BYTE);
|
||||||
let parity = 0;
|
let parity = 0;
|
||||||
const epochUint8 = dataViewUint8Array(dataView, EPOCH_MS);
|
const epochUint8 = dataViewUint8Array(dataView, EPOCH_MS);
|
||||||
const imuDataUint8 = dataViewUint8Array(dataView, IMU_QUAT_DATA);
|
const imuDataUint8 = dataViewUint8Array(dataView, POSE_ORIENTATION);
|
||||||
for (let i = 0; i < epochUint8.length; i++) {
|
for (let i = 0; i < epochUint8.length; i++) {
|
||||||
parity ^= epochUint8[i];
|
parity ^= epochUint8[i];
|
||||||
}
|
}
|
||||||
|
|
@ -210,10 +211,11 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
const validData = validKeepAlive && displayFov !== 0.0;
|
const validData = validKeepAlive && displayFov !== 0.0;
|
||||||
const version = dataViewUint8(dataView, VERSION);
|
const version = dataViewUint8(dataView, VERSION);
|
||||||
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validData;
|
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validData;
|
||||||
let imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
|
let poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
|
||||||
|
let posePosition = dataViewFloatArray(dataView, POSE_POSITION);
|
||||||
let smoothFollowEnabled = !this.legacy_follow_mode && dataViewUint8(dataView, SMOOTH_FOLLOW_ENABLED) !== 0;
|
let smoothFollowEnabled = !this.legacy_follow_mode && dataViewUint8(dataView, SMOOTH_FOLLOW_ENABLED) !== 0;
|
||||||
let smoothFollowOrigin = dataViewFloatArray(dataView, SMOOTH_FOLLOW_ORIGIN_DATA);
|
let smoothFollowOrigin = dataViewFloatArray(dataView, SMOOTH_FOLLOW_ORIGIN_DATA);
|
||||||
const imuResetState = enabled && validData && imuData[0] === 0.0 && imuData[1] === 0.0 && imuData[2] === 0.0 && imuData[3] === 1.0;
|
const imuResetState = enabled && validData && poseOrientation[0] === 0.0 && poseOrientation[1] === 0.0 && poseOrientation[2] === 0.0 && poseOrientation[3] === 1.0;
|
||||||
const customBannerEnabled = dataViewUint8(dataView, CUSTOM_BANNER_ENABLED) !== 0;
|
const customBannerEnabled = dataViewUint8(dataView, CUSTOM_BANNER_ENABLED) !== 0;
|
||||||
const sbsEnabled = dataViewUint8(dataView, SBS_ENABLED) !== 0;
|
const sbsEnabled = dataViewUint8(dataView, SBS_ENABLED) !== 0;
|
||||||
|
|
||||||
|
|
@ -261,7 +263,8 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
if (checkParityByte(dataView)) {
|
if (checkParityByte(dataView)) {
|
||||||
|
|
||||||
this.imu_snapshots = {
|
this.imu_snapshots = {
|
||||||
imu_data: imuData,
|
pose_orientation: poseOrientation,
|
||||||
|
pose_position: posePosition,
|
||||||
timestamp_ms: imuDateMs,
|
timestamp_ms: imuDateMs,
|
||||||
smooth_follow_origin: smoothFollowOrigin
|
smooth_follow_origin: smoothFollowOrigin
|
||||||
};
|
};
|
||||||
|
|
@ -277,7 +280,8 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
buffer = new Uint8Array(data[1]).buffer;
|
buffer = new Uint8Array(data[1]).buffer;
|
||||||
dataView = new DataView(buffer);
|
dataView = new DataView(buffer);
|
||||||
imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
imuDateMs = dataViewBigUint(dataView, EPOCH_MS);
|
||||||
imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
|
poseOrientation = dataViewFloatArray(dataView, POSE_ORIENTATION);
|
||||||
|
posePosition = dataViewFloatArray(dataView, POSE_POSITION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,15 +315,17 @@ export const DeviceDataStream = GObject.registerClass({
|
||||||
if (!keepalive_only) {
|
if (!keepalive_only) {
|
||||||
this._counter = ((this._counter ?? -1)+1)%COUNTER_MAX;
|
this._counter = ((this._counter ?? -1)+1)%COUNTER_MAX;
|
||||||
|
|
||||||
const imuDataFirst = nextDebugIMUQuaternion(this._counter);
|
const poseOrientationFirst = nextDebugIMUQuaternion(this._counter);
|
||||||
const imuData = [
|
const poseOrientation = [
|
||||||
...imuDataFirst,
|
...poseOrientationFirst,
|
||||||
...imuDataFirst,
|
...poseOrientationFirst,
|
||||||
...imuDataFirst,
|
...poseOrientationFirst,
|
||||||
2.0, 1.0, 0.0, 0.0
|
2.0, 1.0, 0.0, 0.0
|
||||||
]
|
]
|
||||||
|
const posePosition = [0.0, 0.0, 0.0];
|
||||||
this.imu_snapshots = {
|
this.imu_snapshots = {
|
||||||
imu_data: imuData,
|
pose_orientation: poseOrientation,
|
||||||
|
pose_position: posePosition,
|
||||||
timestamp_ms: Date.now(),
|
timestamp_ms: Date.now(),
|
||||||
smooth_follow_origin: [0.0, 0.0, 0.0, 1.0]
|
smooth_follow_origin: [0.0, 0.0, 0.0, 1.0]
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,8 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
vfunc_build_pipeline() {
|
vfunc_build_pipeline() {
|
||||||
const declarations = `
|
const declarations = `
|
||||||
uniform bool u_show_banner;
|
uniform bool u_show_banner;
|
||||||
uniform mat4 u_imu_data;
|
uniform mat4 u_pose_orientation;
|
||||||
|
uniform vec3 u_pose_position;
|
||||||
uniform float u_look_ahead_ms;
|
uniform float u_look_ahead_ms;
|
||||||
uniform vec4 u_look_ahead_cfg;
|
uniform vec4 u_look_ahead_cfg;
|
||||||
uniform mat4 u_projection_matrix;
|
uniform mat4 u_projection_matrix;
|
||||||
|
|
@ -460,7 +461,11 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
vec4 nwuToESU(vec4 v) {
|
vec4 nwuToESU(vec4 v) {
|
||||||
return vec4(-v.y, v.z, -v.x, v.w);
|
return vec4(-v.y, v.z, -v.x, v.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 nwuToESU(vec3 v) {
|
||||||
|
return vec3(-v.y, v.z, -v.x);
|
||||||
|
}
|
||||||
|
|
||||||
// returns the rate of change between the two vectors, in same time units as delta_time
|
// returns the rate of change between the two vectors, in same time units as delta_time
|
||||||
// e.g. if delta_time is in ms, then the rate of change is "per ms"
|
// e.g. if delta_time is in ms, then the rate of change is "per ms"
|
||||||
vec3 rateOfChange(vec3 v1, vec3 v2, float delta_time) {
|
vec3 rateOfChange(vec3 v1, vec3 v2, float delta_time) {
|
||||||
|
|
@ -487,14 +492,23 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
if (!u_show_banner) {
|
if (!u_show_banner) {
|
||||||
float aspect_ratio = u_display_resolution.x / u_display_resolution.y;
|
float aspect_ratio = u_display_resolution.x / u_display_resolution.y;
|
||||||
|
|
||||||
|
vec4 quat_t0 = nwuToESU(quatConjugate(u_pose_orientation[0]));
|
||||||
|
vec3 position_vector = applyQuaternionToVector(nwuToESU(u_pose_position), quat_t0);
|
||||||
|
vec3 final_lens_position = u_lens_vector + position_vector;
|
||||||
|
|
||||||
vec3 complete_vector = applyXRotationToVector(world_pos.xyz, u_rotation_x_radians);
|
vec3 complete_vector = applyXRotationToVector(world_pos.xyz, u_rotation_x_radians);
|
||||||
complete_vector = applyYRotationToVector(complete_vector, u_rotation_y_radians);
|
complete_vector = applyYRotationToVector(complete_vector, u_rotation_y_radians);
|
||||||
|
|
||||||
vec4 quat_t0 = nwuToESU(quatConjugate(u_imu_data[0]));
|
|
||||||
vec3 rotated_vector_t0 = applyQuaternionToVector(complete_vector, quat_t0);
|
vec3 rotated_vector_t0 = applyQuaternionToVector(complete_vector, quat_t0);
|
||||||
vec3 rotated_vector_t1 = applyQuaternionToVector(complete_vector, nwuToESU(quatConjugate(u_imu_data[1])));
|
vec3 rotated_vector_t1 = applyQuaternionToVector(complete_vector, nwuToESU(quatConjugate(u_pose_orientation[1])));
|
||||||
float delta_time_t0 = u_imu_data[3][0] - u_imu_data[3][1];
|
float delta_time_t0 = u_pose_orientation[3][0] - u_pose_orientation[3][1];
|
||||||
vec3 velocity_t0 = rateOfChange(rotated_vector_t0, rotated_vector_t1, delta_time_t0);
|
|
||||||
|
// how quickly the vertex is moving relative to the camera
|
||||||
|
vec3 velocity_t0 = rateOfChange(
|
||||||
|
rotated_vector_t0 - final_lens_position,
|
||||||
|
rotated_vector_t1 - final_lens_position,
|
||||||
|
delta_time_t0
|
||||||
|
);
|
||||||
|
|
||||||
// compute the capped look ahead with scanline adjustments
|
// compute the capped look ahead with scanline adjustments
|
||||||
float look_ahead_scanline_ms = u_look_ahead_ms == 0.0 ? 0.0 : vectorToScanline(u_fov_vertical_radians, rotated_vector_t0) * u_look_ahead_cfg[2];
|
float look_ahead_scanline_ms = u_look_ahead_ms == 0.0 ? 0.0 : vectorToScanline(u_fov_vertical_radians, rotated_vector_t0) * u_look_ahead_cfg[2];
|
||||||
|
|
@ -502,7 +516,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
|
|
||||||
vec3 look_ahead_vector = applyLookAhead(rotated_vector_t0, velocity_t0, effective_look_ahead_ms);
|
vec3 look_ahead_vector = applyLookAhead(rotated_vector_t0, velocity_t0, effective_look_ahead_ms);
|
||||||
|
|
||||||
world_pos = vec4(look_ahead_vector - u_lens_vector, world_pos.w);
|
world_pos = vec4(look_ahead_vector - final_lens_position, world_pos.w);
|
||||||
|
|
||||||
world_pos.z /= aspect_ratio / u_actor_to_display_ratios.y;
|
world_pos.z /= aspect_ratio / u_actor_to_display_ratios.y;
|
||||||
|
|
||||||
|
|
@ -557,9 +571,12 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [0.0]);
|
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [0.0]);
|
||||||
lookAheadSet = true;
|
lookAheadSet = true;
|
||||||
}
|
}
|
||||||
this.set_uniform_matrix(this.get_uniform_location("u_imu_data"), false, 4, this.imu_snapshots.imu_data);
|
const posePositionPixels = this.imu_snapshots.pose_position.map(coord => coord * this.fov_details.completeScreenDistancePixels);
|
||||||
|
this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.pose_orientation);
|
||||||
|
this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, posePositionPixels);
|
||||||
} else {
|
} else {
|
||||||
this.set_uniform_matrix(this.get_uniform_location("u_imu_data"), false, 4, this.imu_snapshots.smooth_follow_origin);
|
this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.smooth_follow_origin);
|
||||||
|
this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, [0.0, 0.0, 0.0]);
|
||||||
}
|
}
|
||||||
if (!lookAheadSet) {
|
if (!lookAheadSet) {
|
||||||
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [lookAheadMS(this.imu_snapshots.timestamp_ms, Globals.data_stream.device_data.lookAheadCfg, this.look_ahead_override)]);
|
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [lookAheadMS(this.imu_snapshots.timestamp_ms, Globals.data_stream.device_data.lookAheadCfg, this.look_ahead_override)]);
|
||||||
|
|
|
||||||
|
|
@ -785,12 +785,12 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
(!this._smooth_follow_slerping || this.focused_monitor_index === -1)) {
|
(!this._smooth_follow_slerping || this.focused_monitor_index === -1)) {
|
||||||
// if smooth follow is enabled, use the origin IMU data to inform the initial focused monitor
|
// if smooth follow is enabled, use the origin IMU data to inform the initial focused monitor
|
||||||
// since it reflects where the user is looking in relation to the original monitor positions
|
// since it reflects where the user is looking in relation to the original monitor positions
|
||||||
const currentPoseQuat = this.smooth_follow_enabled ?
|
const currentOrientationQuat = this.smooth_follow_enabled ?
|
||||||
this.imu_snapshots.smooth_follow_origin.splice(0, 4) :
|
this.imu_snapshots.smooth_follow_origin.splice(0, 4) :
|
||||||
this.imu_snapshots.imu_data.splice(0, 4);
|
this.imu_snapshots.pose_orientation.splice(0, 4);
|
||||||
|
|
||||||
const focusedMonitorIndex = findFocusedMonitor(
|
const focusedMonitorIndex = findFocusedMonitor(
|
||||||
currentPoseQuat,
|
currentOrientationQuat,
|
||||||
this.monitor_placements.map(monitorVectors => monitorVectors.centerLook),
|
this.monitor_placements.map(monitorVectors => monitorVectors.centerLook),
|
||||||
this.focused_monitor_index,
|
this.focused_monitor_index,
|
||||||
this.display_distance / this._display_distance_default(),
|
this.display_distance / this._display_distance_default(),
|
||||||
|
|
|
||||||
|
|
@ -98,11 +98,12 @@ namespace DataView
|
||||||
constexpr int CUSTOM_BANNER_ENABLED[3] = {dataViewEnd(SBS_ENABLED), BOOL_SIZE, 1};
|
constexpr int CUSTOM_BANNER_ENABLED[3] = {dataViewEnd(SBS_ENABLED), BOOL_SIZE, 1};
|
||||||
constexpr int SMOOTH_FOLLOW_ENABLED[3] = {dataViewEnd(CUSTOM_BANNER_ENABLED), BOOL_SIZE, 1};
|
constexpr int SMOOTH_FOLLOW_ENABLED[3] = {dataViewEnd(CUSTOM_BANNER_ENABLED), BOOL_SIZE, 1};
|
||||||
constexpr int SMOOTH_FOLLOW_ORIGIN_DATA[3] = {dataViewEnd(SMOOTH_FOLLOW_ENABLED), FLOAT_SIZE, 16};
|
constexpr int SMOOTH_FOLLOW_ORIGIN_DATA[3] = {dataViewEnd(SMOOTH_FOLLOW_ENABLED), FLOAT_SIZE, 16};
|
||||||
constexpr int IMU_DATE_MS[3] = {dataViewEnd(SMOOTH_FOLLOW_ORIGIN_DATA), UINT_SIZE, 2};
|
constexpr int POSE_POSITION_DATA[3] = {dataViewEnd(SMOOTH_FOLLOW_ORIGIN_DATA), FLOAT_SIZE, 3};
|
||||||
constexpr int IMU_QUAT_ENTRIES = 4;
|
constexpr int POSE_DATE_MS[3] = {dataViewEnd(POSE_POSITION_DATA), UINT_SIZE, 2};
|
||||||
constexpr int IMU_QUAT_DATA[3] = {dataViewEnd(IMU_DATE_MS), FLOAT_SIZE, 4 * IMU_QUAT_ENTRIES};
|
constexpr int POSE_ORIENTATION_ENTRIES = 4;
|
||||||
constexpr int IMU_PARITY_BYTE[3] = {dataViewEnd(IMU_QUAT_DATA), UINT8_SIZE, 1};
|
constexpr int POSE_ORIENTATION_DATA[3] = {dataViewEnd(POSE_DATE_MS), FLOAT_SIZE, 4 * POSE_ORIENTATION_ENTRIES};
|
||||||
constexpr int LENGTH = dataViewEnd(IMU_PARITY_BYTE);
|
constexpr int POSE_PARITY_BYTE[3] = {dataViewEnd(POSE_ORIENTATION_DATA), UINT8_SIZE, 1};
|
||||||
|
constexpr int LENGTH = dataViewEnd(POSE_PARITY_BYTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
|
|
@ -142,7 +143,7 @@ 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 IPC file for changes, even if it doesn't exist at startup
|
||||||
m_shmDirectoryWatcher = new QFileSystemWatcher(this);
|
m_shmDirectoryWatcher = new QFileSystemWatcher(this);
|
||||||
m_shmDirectoryWatcher->addPath(DataView::SHM_DIR);
|
m_shmDirectoryWatcher->addPath(DataView::SHM_DIR);
|
||||||
|
|
||||||
|
|
@ -151,14 +152,14 @@ BreezyDesktopEffect::BreezyDesktopEffect()
|
||||||
// Setup file watcher with recreation detection
|
// Setup file watcher with recreation detection
|
||||||
auto setupFileWatcher = [this]() {
|
auto setupFileWatcher = [this]() {
|
||||||
if (QFile::exists(DataView::SHM_PATH) && (
|
if (QFile::exists(DataView::SHM_PATH) && (
|
||||||
m_imuTimestamp == 0 ||
|
m_poseTimestamp == 0 ||
|
||||||
QDateTime::currentMSecsSinceEpoch() - m_imuTimestamp > 50 || // file may have been deleted and recreated
|
QDateTime::currentMSecsSinceEpoch() - m_poseTimestamp > 50 || // file may have been deleted and recreated
|
||||||
!m_shmFileWatcher->files().contains(DataView::SHM_PATH)
|
!m_shmFileWatcher->files().contains(DataView::SHM_PATH)
|
||||||
)) {
|
)) {
|
||||||
m_shmFileWatcher->removePath(DataView::SHM_PATH);
|
m_shmFileWatcher->removePath(DataView::SHM_PATH);
|
||||||
disconnect(m_shmFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updateImuRotation);
|
disconnect(m_shmFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updatePoseOrientation);
|
||||||
m_shmFileWatcher->addPath(DataView::SHM_PATH);
|
m_shmFileWatcher->addPath(DataView::SHM_PATH);
|
||||||
connect(m_shmFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updateImuRotation);
|
connect(m_shmFileWatcher, &QFileSystemWatcher::fileChanged, this, &BreezyDesktopEffect::updatePoseOrientation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -168,13 +169,13 @@ BreezyDesktopEffect::BreezyDesktopEffect()
|
||||||
// Initial setup
|
// Initial setup
|
||||||
setupFileWatcher();
|
setupFileWatcher();
|
||||||
|
|
||||||
m_imuWatchdogTimer = new QTimer(this);
|
m_watchdogTimer = new QTimer(this);
|
||||||
m_imuWatchdogTimer->setInterval(1000);
|
m_watchdogTimer->setInterval(1000);
|
||||||
connect(m_imuWatchdogTimer, &QTimer::timeout, this, [this]() {
|
connect(m_watchdogTimer, &QTimer::timeout, this, [this]() {
|
||||||
if (!m_enabled) return;
|
if (!m_enabled) return;
|
||||||
this->updateImuRotation();
|
this->updatePoseOrientation();
|
||||||
});
|
});
|
||||||
m_imuWatchdogTimer->start();
|
m_watchdogTimer->start();
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -206,10 +207,10 @@ BreezyDesktopEffect::~BreezyDesktopEffect()
|
||||||
m_shmDirectoryWatcher->deleteLater();
|
m_shmDirectoryWatcher->deleteLater();
|
||||||
m_shmDirectoryWatcher = nullptr;
|
m_shmDirectoryWatcher = nullptr;
|
||||||
}
|
}
|
||||||
if (m_imuWatchdogTimer) {
|
if (m_watchdogTimer) {
|
||||||
m_imuWatchdogTimer->stop();
|
m_watchdogTimer->stop();
|
||||||
m_imuWatchdogTimer->deleteLater();
|
m_watchdogTimer->deleteLater();
|
||||||
m_imuWatchdogTimer = nullptr;
|
m_watchdogTimer = nullptr;
|
||||||
}
|
}
|
||||||
deactivate();
|
deactivate();
|
||||||
}
|
}
|
||||||
|
|
@ -439,20 +440,24 @@ void BreezyDesktopEffect::toggleSmoothFollow() {
|
||||||
XRDriverIPC::instance().writeControlFlags(flags);
|
XRDriverIPC::instance().writeControlFlags(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BreezyDesktopEffect::imuResetState() const {
|
bool BreezyDesktopEffect::poseResetState() const {
|
||||||
return m_imuResetState;
|
return m_poseResetState;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QQuaternion> BreezyDesktopEffect::imuRotations() const {
|
QList<QQuaternion> BreezyDesktopEffect::poseOrientations() const {
|
||||||
return m_imuRotations;
|
return m_poseOrientations;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 BreezyDesktopEffect::imuTimeElapsedMs() const {
|
QVector3D BreezyDesktopEffect::posePosition() const {
|
||||||
return m_imuTimeElapsedMs;
|
return m_posePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 BreezyDesktopEffect::imuTimestamp() const {
|
quint32 BreezyDesktopEffect::poseTimeElapsedMs() const {
|
||||||
return m_imuTimestamp;
|
return m_poseTimeElapsedMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 BreezyDesktopEffect::poseTimestamp() const {
|
||||||
|
return m_poseTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<qreal> BreezyDesktopEffect::lookAheadConfig() const {
|
QList<qreal> BreezyDesktopEffect::lookAheadConfig() const {
|
||||||
|
|
@ -581,17 +586,17 @@ bool BreezyDesktopEffect::smoothFollowEnabled() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
||||||
const uint8_t parityByte = static_cast<uint8_t>(data[DataView::IMU_PARITY_BYTE[DataView::OFFSET_INDEX]]);
|
const uint8_t parityByte = static_cast<uint8_t>(data[DataView::POSE_PARITY_BYTE[DataView::OFFSET_INDEX]]);
|
||||||
uint8_t parity = 0;
|
uint8_t parity = 0;
|
||||||
|
|
||||||
const int dateBytes = DataView::IMU_DATE_MS[DataView::COUNT_INDEX] * DataView::IMU_DATE_MS[DataView::SIZE_INDEX];
|
const int dateBytes = DataView::POSE_DATE_MS[DataView::COUNT_INDEX] * DataView::POSE_DATE_MS[DataView::SIZE_INDEX];
|
||||||
for (int i = 0; i < dateBytes; ++i) {
|
for (int i = 0; i < dateBytes; ++i) {
|
||||||
parity ^= static_cast<uint8_t>(data[DataView::IMU_DATE_MS[DataView::OFFSET_INDEX] + i]);
|
parity ^= static_cast<uint8_t>(data[DataView::POSE_DATE_MS[DataView::OFFSET_INDEX] + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int quatBytes = DataView::IMU_QUAT_DATA[DataView::COUNT_INDEX] * DataView::IMU_QUAT_DATA[DataView::SIZE_INDEX];
|
const int quatBytes = DataView::POSE_ORIENTATION_DATA[DataView::COUNT_INDEX] * DataView::POSE_ORIENTATION_DATA[DataView::SIZE_INDEX];
|
||||||
for (int i = 0; i < quatBytes; ++i) {
|
for (int i = 0; i < quatBytes; ++i) {
|
||||||
parity ^= static_cast<uint8_t>(data[DataView::IMU_QUAT_DATA[DataView::OFFSET_INDEX] + i]);
|
parity ^= static_cast<uint8_t>(data[DataView::POSE_ORIENTATION_DATA[DataView::OFFSET_INDEX] + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parityByte == parity;
|
return parityByte == parity;
|
||||||
|
|
@ -599,15 +604,15 @@ bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
||||||
|
|
||||||
static qint64 lastConfigUpdate = 0;
|
static qint64 lastConfigUpdate = 0;
|
||||||
static qint64 activatedAt = 0;
|
static qint64 activatedAt = 0;
|
||||||
void BreezyDesktopEffect::updateImuRotation() {
|
void BreezyDesktopEffect::updatePoseOrientation() {
|
||||||
// Reentrancy guard: if an update is already in progress, skip
|
// Reentrancy guard: if an update is already in progress, skip
|
||||||
bool expected = false;
|
bool expected = false;
|
||||||
if (!m_imuUpdateInProgress.compare_exchange_strong(expected, true)) {
|
if (!m_poseUpdateInProgress.compare_exchange_strong(expected, true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// destructor called on function exit, triggers reset of the flag
|
// destructor called on function exit, triggers reset of the flag
|
||||||
struct ResetFlag { std::atomic<bool>* f; ~ResetFlag(){ f->store(false); } } reset{&m_imuUpdateInProgress};
|
struct ResetFlag { std::atomic<bool>* f; ~ResetFlag(){ f->store(false); } } reset{&m_poseUpdateInProgress};
|
||||||
|
|
||||||
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
const QString shmPath = QStringLiteral("/dev/shm/breezy_desktop_imu");
|
||||||
QFile shmFile(shmPath);
|
QFile shmFile(shmPath);
|
||||||
|
|
@ -623,9 +628,9 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
|
|
||||||
uint8_t version = static_cast<uint8_t>(data[DataView::VERSION[DataView::OFFSET_INDEX]]);
|
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]]);
|
uint8_t enabledFlag = static_cast<uint8_t>(data[DataView::ENABLED[DataView::OFFSET_INDEX]]);
|
||||||
uint64_t imuDateMs;
|
uint64_t poseDateMs;
|
||||||
memcpy(&imuDateMs, data + DataView::IMU_DATE_MS[DataView::OFFSET_INDEX], sizeof(imuDateMs));
|
memcpy(&poseDateMs, data + DataView::POSE_DATE_MS[DataView::OFFSET_INDEX], sizeof(poseDateMs));
|
||||||
imuDateMs = qFromLittleEndian(imuDateMs);
|
poseDateMs = qFromLittleEndian(poseDateMs);
|
||||||
|
|
||||||
const qint64 currentTimeMs = QDateTime::currentMSecsSinceEpoch();
|
const qint64 currentTimeMs = QDateTime::currentMSecsSinceEpoch();
|
||||||
const bool updateConfig = lastConfigUpdate == 0 || currentTimeMs - lastConfigUpdate > 1000;
|
const bool updateConfig = lastConfigUpdate == 0 || currentTimeMs - lastConfigUpdate > 1000;
|
||||||
|
|
@ -664,9 +669,9 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
lastConfigUpdate = currentTimeMs;
|
lastConfigUpdate = currentTimeMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool validKeepAlive = (currentTimeMs - imuDateMs) < 5000;
|
const bool validKeepAlive = (currentTimeMs - poseDateMs) < 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 = 5;
|
||||||
bool enabledFlagSet = (enabledFlag != 0);
|
bool enabledFlagSet = (enabledFlag != 0);
|
||||||
bool validVersion = (version == expectedVersion);
|
bool validVersion = (version == expectedVersion);
|
||||||
const bool wasEnabled = m_enabled;
|
const bool wasEnabled = m_enabled;
|
||||||
|
|
@ -675,7 +680,7 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
// give a grace period after enabling the effect
|
// give a grace period after enabling the effect
|
||||||
if (wasEnabled && (currentTimeMs - activatedAt > 1000)) {
|
if (wasEnabled && (currentTimeMs - activatedAt > 1000)) {
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - disabling effect; currentTimeMs:" << currentTimeMs
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - disabling effect; currentTimeMs:" << currentTimeMs
|
||||||
<< "imuDateMs:" << imuDateMs
|
<< "poseDateMs:" << poseDateMs
|
||||||
<< "enabledFlag:" << enabledFlag
|
<< "enabledFlag:" << enabledFlag
|
||||||
<< "version:" << version
|
<< "version:" << version
|
||||||
<< "diagonalFOV:" << m_diagonalFOV;
|
<< "diagonalFOV:" << m_diagonalFOV;
|
||||||
|
|
@ -686,7 +691,7 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
}
|
}
|
||||||
} else if (!wasEnabled) {
|
} else if (!wasEnabled) {
|
||||||
qCCritical(KWIN_XR) << "\t\t\tBreezy - enabling effect; currentTimeMs:" << currentTimeMs
|
qCCritical(KWIN_XR) << "\t\t\tBreezy - enabling effect; currentTimeMs:" << currentTimeMs
|
||||||
<< "imuDateMs:" << imuDateMs
|
<< "poseDateMs:" << poseDateMs
|
||||||
<< "enabledFlag:" << enabledFlag
|
<< "enabledFlag:" << enabledFlag
|
||||||
<< "version:" << version
|
<< "version:" << version
|
||||||
<< "diagonalFOV:" << m_diagonalFOV;
|
<< "diagonalFOV:" << m_diagonalFOV;
|
||||||
|
|
@ -698,50 +703,56 @@ void BreezyDesktopEffect::updateImuRotation() {
|
||||||
|
|
||||||
if (updateConfig) Q_EMIT devicePropertiesChanged();
|
if (updateConfig) Q_EMIT devicePropertiesChanged();
|
||||||
|
|
||||||
float imuData[4 * DataView::IMU_QUAT_ENTRIES]; // 4 quaternion-sized rows
|
float posePositionData[3];
|
||||||
memcpy(imuData, data + DataView::IMU_QUAT_DATA[DataView::OFFSET_INDEX], sizeof(imuData));
|
memcpy(posePositionData, data + DataView::POSE_POSITION_DATA[DataView::OFFSET_INDEX], sizeof(posePositionData));
|
||||||
bool wasImuResetState = m_imuResetState;
|
|
||||||
m_imuResetState = (imuData[0] == 0.0f && imuData[1] == 0.0f && imuData[2] == 0.0f && imuData[3] == 1.0f);
|
// convert NWU to EUS by passing position values: -y, z, -x
|
||||||
if (m_imuResetState != wasImuResetState) {
|
m_posePosition = QVector3D(-posePositionData[1], posePositionData[2], -posePositionData[0]);
|
||||||
if (m_imuResetState) recenter();
|
|
||||||
Q_EMIT imuResetStateChanged();
|
float poseOrientationData[4 * DataView::POSE_ORIENTATION_ENTRIES]; // 4 quaternion-sized rows
|
||||||
|
memcpy(poseOrientationData, data + DataView::POSE_ORIENTATION_DATA[DataView::OFFSET_INDEX], sizeof(poseOrientationData));
|
||||||
|
bool wasPoseResetState = m_poseResetState;
|
||||||
|
m_poseResetState = (poseOrientationData[0] == 0.0f && poseOrientationData[1] == 0.0f && poseOrientationData[2] == 0.0f && poseOrientationData[3] == 1.0f);
|
||||||
|
if (m_poseResetState != wasPoseResetState) {
|
||||||
|
if (m_poseResetState) recenter();
|
||||||
|
Q_EMIT poseResetStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert NWU to EUS by passing root.rotation values: -y, z, -x
|
// convert NWU to EUS by passing orientation values: -y, z, -x
|
||||||
QQuaternion quatT0(imuData[3], -imuData[1], imuData[2], -imuData[0]);
|
QQuaternion quatT0(poseOrientationData[3], -poseOrientationData[1], poseOrientationData[2], -poseOrientationData[0]);
|
||||||
|
|
||||||
int imuDataOffset = DataView::IMU_QUAT_ENTRIES;
|
int orientationDataOffset = DataView::POSE_ORIENTATION_ENTRIES;
|
||||||
QQuaternion quatT1(imuData[imuDataOffset + 3], -imuData[imuDataOffset + 1], imuData[imuDataOffset + 2], -imuData[imuDataOffset + 0]);
|
QQuaternion quatT1(poseOrientationData[orientationDataOffset + 3], -poseOrientationData[orientationDataOffset + 1], poseOrientationData[orientationDataOffset + 2], -poseOrientationData[orientationDataOffset + 0]);
|
||||||
|
|
||||||
imuDataOffset += DataView::IMU_QUAT_ENTRIES;
|
orientationDataOffset += DataView::POSE_ORIENTATION_ENTRIES;
|
||||||
|
|
||||||
// skip the 3rd quaternion
|
// skip the 3rd quaternion
|
||||||
imuDataOffset += DataView::IMU_QUAT_ENTRIES;
|
orientationDataOffset += DataView::POSE_ORIENTATION_ENTRIES;
|
||||||
|
|
||||||
// set imuRotations to the last two rotations, leave out the elapsed time
|
// set poseOrientations to the last two rotations, leave out the elapsed time
|
||||||
m_imuRotations.clear();
|
m_poseOrientations.clear();
|
||||||
m_imuRotations.append(quatT0);
|
m_poseOrientations.append(quatT0);
|
||||||
m_imuRotations.append(quatT1);
|
m_poseOrientations.append(quatT1);
|
||||||
|
|
||||||
// 4th row isn't actually a quaternion, it contains the timestamps for each of the 3 quaternions
|
// 4th row isn't actually a quaternion, it contains the timestamps for each of the 3 quaternions
|
||||||
// elapsed time between T0 and T1 is: imuData[0] - imuData[1]
|
// elapsed time between T0 and T1 is: poseOrientationData[0] - poseOrientationData[1]
|
||||||
m_imuTimeElapsedMs = static_cast<quint32>(imuData[imuDataOffset + 0] - imuData[imuDataOffset + 1]);
|
m_poseTimeElapsedMs = static_cast<quint32>(poseOrientationData[orientationDataOffset + 0] - poseOrientationData[orientationDataOffset + 1]);
|
||||||
|
|
||||||
m_imuTimestamp = imuDateMs;
|
m_poseTimestamp = poseDateMs;
|
||||||
|
|
||||||
float originData[4 * DataView::IMU_QUAT_ENTRIES]; // 4 quaternion-sized rows
|
float originData[4 * DataView::POSE_ORIENTATION_ENTRIES]; // 4 quaternion-sized rows
|
||||||
memcpy(originData, data + DataView::SMOOTH_FOLLOW_ORIGIN_DATA[DataView::OFFSET_INDEX], sizeof(originData));
|
memcpy(originData, data + DataView::SMOOTH_FOLLOW_ORIGIN_DATA[DataView::OFFSET_INDEX], sizeof(originData));
|
||||||
|
|
||||||
// convert NWU to EUS by passing root.rotation values: -y, z, -x
|
// convert NWU to EUS by passing root.rotation values: -y, z, -x
|
||||||
QQuaternion sfQuatT0(originData[3], -originData[1], originData[2], -originData[0]);
|
QQuaternion sfQuatT0(originData[3], -originData[1], originData[2], -originData[0]);
|
||||||
|
|
||||||
int originDataOffset = DataView::IMU_QUAT_ENTRIES;
|
int originDataOffset = DataView::POSE_ORIENTATION_ENTRIES;
|
||||||
QQuaternion sfQuatT1(originData[originDataOffset + 3], -originData[originDataOffset + 1], originData[originDataOffset + 2], -originData[originDataOffset + 0]);
|
QQuaternion sfQuatT1(originData[originDataOffset + 3], -originData[originDataOffset + 1], originData[originDataOffset + 2], -originData[originDataOffset + 0]);
|
||||||
|
|
||||||
originDataOffset += DataView::IMU_QUAT_ENTRIES;
|
originDataOffset += DataView::POSE_ORIENTATION_ENTRIES;
|
||||||
|
|
||||||
// skip the 3rd quaternion
|
// skip the 3rd quaternion
|
||||||
originDataOffset += DataView::IMU_QUAT_ENTRIES;
|
originDataOffset += DataView::POSE_ORIENTATION_ENTRIES;
|
||||||
|
|
||||||
// set smoothFollowOrigin to the last two rotations, leave out the elapsed time
|
// set smoothFollowOrigin to the last two rotations, leave out the elapsed time
|
||||||
m_smoothFollowOrigin.clear();
|
m_smoothFollowOrigin.clear();
|
||||||
|
|
@ -878,9 +889,9 @@ void BreezyDesktopEffect::evaluateCursorOnScreenState(const QPointF &prevPos, co
|
||||||
const bool onScreen =
|
const bool onScreen =
|
||||||
m_effectOnScreenExpandedGeometry.contains(newPos.toPoint()) ||
|
m_effectOnScreenExpandedGeometry.contains(newPos.toPoint()) ||
|
||||||
m_effectOnScreenExpandedGeometry.contains(predicted.toPoint());
|
m_effectOnScreenExpandedGeometry.contains(predicted.toPoint());
|
||||||
if (m_enabled && !m_imuResetState && !m_cursorHidden && onScreen) {
|
if (m_enabled && !m_poseResetState && !m_cursorHidden && onScreen) {
|
||||||
hideCursor();
|
hideCursor();
|
||||||
} else if (m_cursorHidden && (!m_enabled || m_imuResetState || !onScreen)) {
|
} else if (m_cursorHidden && (!m_enabled || m_poseResetState || !onScreen)) {
|
||||||
showCursor();
|
showCursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,11 @@ namespace KWin
|
||||||
Q_PROPERTY(int effectTargetScreenIndex READ effectTargetScreenIndex WRITE setEffectTargetScreenIndex)
|
Q_PROPERTY(int effectTargetScreenIndex READ effectTargetScreenIndex WRITE setEffectTargetScreenIndex)
|
||||||
Q_PROPERTY(bool zoomOnFocusEnabled READ isZoomOnFocusEnabled WRITE setZoomOnFocusEnabled NOTIFY zoomOnFocusChanged)
|
Q_PROPERTY(bool zoomOnFocusEnabled READ isZoomOnFocusEnabled WRITE setZoomOnFocusEnabled NOTIFY zoomOnFocusChanged)
|
||||||
Q_PROPERTY(int lookingAtScreenIndex READ lookingAtScreenIndex WRITE setLookingAtScreenIndex)
|
Q_PROPERTY(int lookingAtScreenIndex READ lookingAtScreenIndex WRITE setLookingAtScreenIndex)
|
||||||
Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuResetStateChanged)
|
Q_PROPERTY(bool poseResetState READ poseResetState NOTIFY poseResetStateChanged)
|
||||||
Q_PROPERTY(QList<QQuaternion> imuRotations READ imuRotations)
|
Q_PROPERTY(QList<QQuaternion> poseOrientations READ poseOrientations)
|
||||||
Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs)
|
Q_PROPERTY(QVector3D posePosition READ posePosition)
|
||||||
Q_PROPERTY(quint64 imuTimestamp READ imuTimestamp)
|
Q_PROPERTY(quint32 poseTimeElapsedMs READ poseTimeElapsedMs)
|
||||||
|
Q_PROPERTY(quint64 poseTimestamp READ poseTimestamp)
|
||||||
Q_PROPERTY(QString cursorImageSource READ cursorImageSource NOTIFY cursorImageSourceChanged)
|
Q_PROPERTY(QString cursorImageSource READ cursorImageSource NOTIFY cursorImageSourceChanged)
|
||||||
Q_PROPERTY(QSize cursorImageSize READ cursorImageSize NOTIFY cursorImageSourceChanged)
|
Q_PROPERTY(QSize cursorImageSize READ cursorImageSize NOTIFY cursorImageSourceChanged)
|
||||||
Q_PROPERTY(QPointF cursorPos READ cursorPos NOTIFY cursorPosChanged)
|
Q_PROPERTY(QPointF cursorPos READ cursorPos NOTIFY cursorPosChanged)
|
||||||
|
|
@ -73,10 +74,11 @@ namespace KWin
|
||||||
void setZoomOnFocusEnabled(bool enabled);
|
void setZoomOnFocusEnabled(bool enabled);
|
||||||
int lookingAtScreenIndex() const { return m_lookingAtScreenIndex; }
|
int lookingAtScreenIndex() const { return m_lookingAtScreenIndex; }
|
||||||
void setLookingAtScreenIndex(int index);
|
void setLookingAtScreenIndex(int index);
|
||||||
QList<QQuaternion> imuRotations() const;
|
QList<QQuaternion> poseOrientations() const;
|
||||||
quint32 imuTimeElapsedMs() const;
|
QVector3D posePosition() const;
|
||||||
quint64 imuTimestamp() const;
|
quint32 poseTimeElapsedMs() const;
|
||||||
bool imuResetState() const;
|
quint64 poseTimestamp() const;
|
||||||
|
bool poseResetState() const;
|
||||||
QList<qreal> lookAheadConfig() const;
|
QList<qreal> lookAheadConfig() const;
|
||||||
qreal lookAheadOverride() const;
|
qreal lookAheadOverride() const;
|
||||||
void setLookAheadOverride(qreal override);
|
void setLookAheadOverride(qreal override);
|
||||||
|
|
@ -112,7 +114,7 @@ namespace KWin
|
||||||
void disableDriver();
|
void disableDriver();
|
||||||
void toggle();
|
void toggle();
|
||||||
void addVirtualDisplay(QSize size);
|
void addVirtualDisplay(QSize size);
|
||||||
void updateImuRotation();
|
void updatePoseOrientation();
|
||||||
void updateCursorImage();
|
void updateCursorImage();
|
||||||
void updateCursorPos();
|
void updateCursorPos();
|
||||||
QVariantList listVirtualDisplays() const;
|
QVariantList listVirtualDisplays() const;
|
||||||
|
|
@ -129,7 +131,7 @@ namespace KWin
|
||||||
void displayWrappingSchemeChanged();
|
void displayWrappingSchemeChanged();
|
||||||
void enabledStateChanged();
|
void enabledStateChanged();
|
||||||
void zoomOnFocusChanged();
|
void zoomOnFocusChanged();
|
||||||
void imuResetStateChanged();
|
void poseResetStateChanged();
|
||||||
void sbsEnabledChanged();
|
void sbsEnabledChanged();
|
||||||
void smoothFollowEnabledChanged();
|
void smoothFollowEnabledChanged();
|
||||||
void devicePropertiesChanged();
|
void devicePropertiesChanged();
|
||||||
|
|
@ -165,10 +167,11 @@ namespace KWin
|
||||||
bool m_zoomOnFocusEnabled = false;
|
bool m_zoomOnFocusEnabled = false;
|
||||||
int m_lookingAtScreenIndex = -1;
|
int m_lookingAtScreenIndex = -1;
|
||||||
int m_effectTargetScreenIndex = -1;
|
int m_effectTargetScreenIndex = -1;
|
||||||
bool m_imuResetState;
|
bool m_poseResetState;
|
||||||
QList<QQuaternion> m_imuRotations;
|
QList<QQuaternion> m_poseOrientations;
|
||||||
quint32 m_imuTimeElapsedMs;
|
QVector3D m_posePosition;
|
||||||
quint64 m_imuTimestamp = 0;
|
quint32 m_poseTimeElapsedMs;
|
||||||
|
quint64 m_poseTimestamp = 0;
|
||||||
QList<qreal> m_lookAheadConfig;
|
QList<qreal> m_lookAheadConfig;
|
||||||
qreal m_lookAheadOverride = -1.0; // -1 = use device default
|
qreal m_lookAheadOverride = -1.0; // -1 = use device default
|
||||||
QList<quint32> m_displayResolution;
|
QList<quint32> m_displayResolution;
|
||||||
|
|
@ -183,8 +186,8 @@ namespace KWin
|
||||||
bool m_cursorHidden = false;
|
bool m_cursorHidden = false;
|
||||||
QPointF m_cursorPos;
|
QPointF m_cursorPos;
|
||||||
QTimer *m_cursorUpdateTimer = nullptr;
|
QTimer *m_cursorUpdateTimer = nullptr;
|
||||||
QTimer *m_imuWatchdogTimer = nullptr;
|
QTimer *m_watchdogTimer = nullptr;
|
||||||
std::atomic<bool> m_imuUpdateInProgress{false};
|
std::atomic<bool> m_poseUpdateInProgress{false};
|
||||||
qreal m_focusedDisplayDistance = 0.85;
|
qreal m_focusedDisplayDistance = 0.85;
|
||||||
qreal m_allDisplaysDistance = 1.05;
|
qreal m_allDisplaysDistance = 1.05;
|
||||||
qreal m_displaySpacing = 0.0;
|
qreal m_displaySpacing = 0.0;
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFocus(smoothFollowEnabledChanged = false) {
|
function updateFocus(smoothFollowEnabledChanged = false) {
|
||||||
const rotations = smoothFollowEnabled ? effect.smoothFollowOrigin : effect.imuRotations;
|
const orientations = smoothFollowEnabled ? effect.smoothFollowOrigin : effect.poseOrientations;
|
||||||
if (rotations && rotations.length > 0) {
|
if (orientations && orientations.length > 0) {
|
||||||
let focusedIndex = -1;
|
let focusedIndex = -1;
|
||||||
const lookingAtIndex = displays.findFocusedMonitor(
|
const lookingAtIndex = displays.findFocusedMonitor(
|
||||||
displays.eusToNwuQuat(rotations[0]),
|
displays.eusToNwuQuat(orientations[0]),
|
||||||
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
|
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
|
||||||
breezyDesktop.focusedMonitorIndex,
|
breezyDesktop.focusedMonitorIndex,
|
||||||
smoothFollowEnabled,
|
smoothFollowEnabled,
|
||||||
|
|
@ -120,10 +120,10 @@ Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
// smoothFollowOrigin is the rotation away from the original placement of the displays
|
// smoothFollowOrigin is the rotation away from the original placement of the displays
|
||||||
// imuRotations is the smooth follow rotation relative to the camera (very near an identity quat)
|
// poseOrientations is the smooth follow rotation relative to the camera (very near an identity quat)
|
||||||
// subtract the latter from the former to get the complete rotation
|
// subtract the latter from the former to get the complete rotation
|
||||||
function smoothFollowQuat() {
|
function smoothFollowQuat() {
|
||||||
return effect.smoothFollowOrigin[0].times(effect.imuRotations[0].conjugated());
|
return effect.smoothFollowOrigin[0].times(effect.poseOrientations[0].conjugated());
|
||||||
}
|
}
|
||||||
|
|
||||||
function displaySmoothFollowVector(display, smoothFollowRotation) {
|
function displaySmoothFollowVector(display, smoothFollowRotation) {
|
||||||
|
|
@ -192,9 +192,9 @@ Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// smoothFollowEnabled gets cleared before the IMU begins slerping back to the origin so we can't just
|
// smoothFollowEnabled gets cleared before the orientation begins slerping back to the origin so we can't just
|
||||||
// switch off smooth follow logic based on this flag. Instead, we have to rely on
|
// switch off smooth follow logic based on this flag. Instead, we have to rely on
|
||||||
// smoothFollowTransitionProgress to determine how much of the IMU positions to apply.
|
// smoothFollowTransitionProgress to determine how much of the orientations to apply.
|
||||||
onSmoothFollowEnabledChanged: {
|
onSmoothFollowEnabledChanged: {
|
||||||
updateFocus(true);
|
updateFocus(true);
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +218,7 @@ Node {
|
||||||
|
|
||||||
// When smooth follow is running, we're updating the position of the display manually
|
// When smooth follow is running, we're updating the position of the display manually
|
||||||
// on every frame (avoid binding to a function that uses non-notify effect properties
|
// on every frame (avoid binding to a function that uses non-notify effect properties
|
||||||
// imuRotations and smoothFollowOrigin).
|
// poseOrientations and smoothFollowOrigin).
|
||||||
focusedDisplay.position = displayPosition(focusedDisplay, smoothFollowRotation);
|
focusedDisplay.position = displayPosition(focusedDisplay, smoothFollowRotation);
|
||||||
} else {
|
} else {
|
||||||
focusedDisplay.rotation = Qt.quaternion(1, 0, 0, 0);
|
focusedDisplay.rotation = Qt.quaternion(1, 0, 0, 0);
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,17 @@ Item {
|
||||||
aspectRatio
|
aspectRatio
|
||||||
);
|
);
|
||||||
|
|
||||||
// if true, then smoothFollowEnabled just cleared and the IMU data is slerping back,
|
// if true, then smoothFollowEnabled just cleared and the orientation data is slerping back,
|
||||||
// continue to use the origin data for the duration of the Timer
|
// continue to use the origin data for the duration of the Timer
|
||||||
property bool smoothFollowDisabling: false
|
property bool smoothFollowDisabling: false
|
||||||
|
|
||||||
property real clipNear: 10.0
|
property real clipNear: 10.0
|
||||||
property real clipFar: 10000.0
|
property real clipFar: 10000.0
|
||||||
|
|
||||||
function ratesOfChange(rotations) {
|
function ratesOfChange(orientations) {
|
||||||
const e0 = rotations[0].toEulerAngles();
|
const e0 = orientations[0].toEulerAngles();
|
||||||
const e1 = rotations[1].toEulerAngles();
|
const e1 = orientations[1].toEulerAngles();
|
||||||
const dt = effect.imuTimeElapsedMs;
|
const dt = effect.poseTimeElapsedMs;
|
||||||
const yawDegrees = (e0.y - e1.y) / dt;
|
const yawDegrees = (e0.y - e1.y) / dt;
|
||||||
const pitchDegrees = (e0.x - e1.x) / dt;
|
const pitchDegrees = (e0.x - e1.x) / dt;
|
||||||
const rollDegrees = (e0.z - e1.z) / dt;
|
const rollDegrees = (e0.z - e1.z) / dt;
|
||||||
|
|
@ -49,22 +49,22 @@ Item {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCamera(rotations, rates) {
|
function updateCamera(orientations, position, rates) {
|
||||||
camera.eulerRotation = applyLookAhead(
|
camera.eulerRotation = applyLookAhead(
|
||||||
rates,
|
rates,
|
||||||
lookAheadMS(
|
lookAheadMS(
|
||||||
effect.imuTimestamp,
|
effect.poseTimestamp,
|
||||||
effect.lookAheadConfig,
|
effect.lookAheadConfig,
|
||||||
effect.lookAheadOverride
|
effect.lookAheadOverride
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
camera.position = rotations[0].times(Qt.vector3d(0, 0, -fovDetails.lensDistancePixels));
|
camera.position = position.times(fovDetails.completeScreenDistancePixels).plus(orientations[0].times(Qt.vector3d(0, 0, -fovDetails.lensDistancePixels)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// how far to look ahead is how old the IMU data is plus a constant that is either the default for this device or an override
|
// how far to look ahead is how old the pose data is plus a constant that is either the default for this device or an override
|
||||||
function lookAheadMS(imuDateMs, lookAheadConfig, override) {
|
function lookAheadMS(poseDateMs, lookAheadConfig, override) {
|
||||||
// how stale the imu data is
|
// how stale the pose data is
|
||||||
const dataAge = Date.now() - imuDateMs;
|
const dataAge = Date.now() - poseDateMs;
|
||||||
|
|
||||||
const lookAheadConstant = lookAheadConfig[0];
|
const lookAheadConstant = lookAheadConfig[0];
|
||||||
const lookAheadMultiplier = lookAheadConfig[1];
|
const lookAheadMultiplier = lookAheadConfig[1];
|
||||||
|
|
@ -148,10 +148,10 @@ Item {
|
||||||
FrameAnimation {
|
FrameAnimation {
|
||||||
running: true
|
running: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
const rotations = (effect.smoothFollowEnabled || smoothFollowDisabling) ? effect.smoothFollowOrigin : effect.imuRotations;
|
const orientations = (effect.smoothFollowEnabled || smoothFollowDisabling) ? effect.smoothFollowOrigin : effect.poseOrientations;
|
||||||
if (rotations && rotations.length > 0) {
|
if (orientations && orientations.length > 0) {
|
||||||
const rates = ratesOfChange(rotations);
|
const rates = ratesOfChange(orientations);
|
||||||
updateCamera(rotations, rates);
|
updateCamera(orientations, effect.posePosition, rates);
|
||||||
applyRollingShutterShear(rates);
|
applyRollingShutterShear(rates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,14 +93,14 @@ Item {
|
||||||
|
|
||||||
property bool targetScreenSupported: supportedModels.some(model => root.targetScreen.model.includes(model))
|
property bool targetScreenSupported: supportedModels.some(model => root.targetScreen.model.includes(model))
|
||||||
property bool targetScreenIsVirtual: targetScreen.name.includes("BreezyDesktop")
|
property bool targetScreenIsVirtual: targetScreen.name.includes("BreezyDesktop")
|
||||||
property bool imuResetState: effect.imuResetState
|
property bool poseResetState: effect.poseResetState
|
||||||
property bool isEnabled: effect.isEnabled
|
property bool isEnabled: effect.isEnabled
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: desktopViewComponent
|
id: desktopViewComponent
|
||||||
SingleDesktopView {
|
SingleDesktopView {
|
||||||
supportsXR: targetScreenSupported
|
supportsXR: targetScreenSupported
|
||||||
showCalibratingBanner: isEnabled && imuResetState
|
showCalibratingBanner: isEnabled && poseResetState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,8 +141,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkLoadedComponent() {
|
function checkLoadedComponent() {
|
||||||
console.log(`Breezy - checking screen ${targetScreen.model}: ${targetScreenSupported} ${targetScreenIsVirtual} ${isEnabled} ${imuResetState}`);
|
console.log(`Breezy - checking screen ${targetScreen.model}: ${targetScreenSupported} ${targetScreenIsVirtual} ${isEnabled} ${poseResetState}`);
|
||||||
const show3DView = targetScreenSupported && isEnabled && !imuResetState;
|
const show3DView = targetScreenSupported && isEnabled && !poseResetState;
|
||||||
if (!targetScreenIsVirtual) viewLoader.sourceComponent = show3DView ? view3DComponent : desktopViewComponent;
|
if (!targetScreenIsVirtual) viewLoader.sourceComponent = show3DView ? view3DComponent : desktopViewComponent;
|
||||||
if (targetScreenSupported) effect.effectTargetScreenIndex = KWinComponents.Workspace.screens.indexOf(targetScreen);
|
if (targetScreenSupported) effect.effectTargetScreenIndex = KWinComponents.Workspace.screens.indexOf(targetScreen);
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +151,7 @@ Item {
|
||||||
checkLoadedComponent();
|
checkLoadedComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
onImuResetStateChanged: {
|
onPoseResetStateChanged: {
|
||||||
checkLoadedComponent();
|
checkLoadedComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3bab088768849a3068146586fed60ef0cc7d506d
|
Subproject commit 6432928fe013298b82ce69f466cef615d3aad0dd
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 712497b4795dbcbaa4c6fdb60a82144cfde26086
|
Subproject commit 40326b9ec0266352f24500f47693c06f39832509
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0eb04ff4429ce7a025f126843cd0d3b24bc0d73e
|
Subproject commit da173bd9e0392aaeb2cb68a332e5d4a20dd4dae1
|
||||||
Loading…
Reference in New Issue