Finalize Vulkan and GNOME 6DoF (#144)
* Pull in the latest driver with VITURE SDK and 6DoF improvements * Add SteamOS vulkan build artifact * Unifies "display size" setting
This commit is contained in:
parent
a0388d074c
commit
f4fecdc3e5
|
|
@ -59,6 +59,10 @@ cp -r $VULKAN_DIR/config $PACKAGE_DIR
|
|||
|
||||
# build XR driver
|
||||
XR_DRIVER_BINARY=$XR_DRIVER_DIR/out/xrDriver-$ARCH.tar.gz
|
||||
if [ -n "$STEAMOS" ]; then
|
||||
XR_DRIVER_BINARY=$XR_DRIVER_DIR/out/xrDriver-$ARCH.steamos.tar.gz
|
||||
BUILD_FILE_NAME=breezyVulkan-$ARCH.steamos.tar.gz
|
||||
fi
|
||||
|
||||
if [ ! -e "$XR_DRIVER_BINARY" ] || [ "$1" == "--rebuild-driver" ]; then
|
||||
# if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory
|
||||
|
|
@ -70,7 +74,10 @@ if [ ! -e "$XR_DRIVER_BINARY" ] || [ "$1" == "--rebuild-driver" ]; then
|
|||
|
||||
# strange issue where the base library produces a .so file if the build is not cleaned
|
||||
rm -rf build/
|
||||
|
||||
|
||||
if [ -n "${STEAMOS:-}" ]; then
|
||||
export STEAMOS
|
||||
fi
|
||||
docker-build/init.sh
|
||||
docker-build/run-build.sh $ARCH
|
||||
popd
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this._data_stream_bindings = [];
|
||||
this._show_banner_connection = null;
|
||||
this._distance_connection = null;
|
||||
this._display_size_connection = null;
|
||||
this._focused_monitor_distance_connection = null;
|
||||
this._follow_threshold_connection = null;
|
||||
this._breezy_desktop_running_connection = null;
|
||||
|
|
@ -228,6 +229,13 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this._virtual_displays_overlay.set_position(targetMonitor.x, targetMonitor.y);
|
||||
this._virtual_displays_overlay.set_size(targetMonitor.width, targetMonitor.height);
|
||||
|
||||
const state = this._read_state();
|
||||
const pose_has_position = state['connected_device_pose_has_position'] === 'true';
|
||||
|
||||
Globals.logger.log_debug(
|
||||
`connected_device_pose_has_position=${pose_has_position}`
|
||||
);
|
||||
|
||||
Globals.data_stream.refresh_data();
|
||||
this._virtual_displays_actor = new VirtualDisplaysActor({
|
||||
width: targetMonitor.width,
|
||||
|
|
@ -241,12 +249,14 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
viewport_offset_x: this.settings.get_double('viewport-offset-x'),
|
||||
viewport_offset_y: this.settings.get_double('viewport-offset-y'),
|
||||
display_distance: this.settings.get_double('display-distance'),
|
||||
display_size: this.settings.get_double('display-size'),
|
||||
toggle_display_distance_start: this.settings.get_double('toggle-display-distance-start'),
|
||||
toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'),
|
||||
framerate_cap: this.settings.get_double('framerate-cap'),
|
||||
imu_snapshots: Globals.data_stream.imu_snapshots,
|
||||
show_banner: Globals.data_stream.show_banner,
|
||||
custom_banner_enabled: Globals.data_stream.custom_banner_enabled
|
||||
custom_banner_enabled: Globals.data_stream.custom_banner_enabled,
|
||||
pose_has_position
|
||||
});
|
||||
|
||||
this._virtual_displays_overlay.set_child(this._virtual_displays_actor);
|
||||
|
|
@ -299,6 +309,9 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
);
|
||||
|
||||
this._distance_connection = this.settings.connect('changed::display-distance', this._update_display_distance.bind(this));
|
||||
this._toggle_distance_start_connection = this.settings.connect('changed::toggle-display-distance-start', this._update_display_distance.bind(this));
|
||||
this._toggle_distance_end_connection = this.settings.connect('changed::toggle-display-distance-end', this._update_display_distance.bind(this));
|
||||
this._display_size_connection = this.settings.connect('changed::display-size', this._update_display_distance.bind(this));
|
||||
this._focused_monitor_distance_connection =
|
||||
this._virtual_displays_actor.connect('notify::focused-monitor-details', this._update_display_distance.bind(this));
|
||||
this._follow_threshold_connection = this.settings.connect('changed::follow-threshold', this._update_follow_threshold.bind(this));
|
||||
|
|
@ -398,17 +411,23 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
}
|
||||
|
||||
_update_display_distance(object, event) {
|
||||
const value = this.settings.get_double('display-distance');
|
||||
Globals.logger.log_debug(`BreezyDesktopExtension _update_display_distance ${value}`);
|
||||
if (value !== undefined) {
|
||||
let focusedMonitorSizeAdjustment = 1.0;
|
||||
const distance = this.settings.get_double('display-distance');
|
||||
const size = this.settings.get_double('display-size');
|
||||
Globals.logger.log_debug(`BreezyDesktopExtension _update_display_distance ${distance} ${size}`);
|
||||
if (distance !== undefined && size !== undefined) {
|
||||
const defaultDistance = Math.max(
|
||||
distance,
|
||||
this.settings.get_double('toggle-display-distance-start'),
|
||||
this.settings.get_double('toggle-display-distance-end')
|
||||
);
|
||||
let focusedMonitorSizeAdjustment = size * defaultDistance;
|
||||
if (this._virtual_displays_actor?.focused_monitor_details && this._target_monitor) {
|
||||
const fovMonitor = this._target_monitor.monitor;
|
||||
const focusedMonitor = this._virtual_displays_actor.focused_monitor_details;
|
||||
focusedMonitorSizeAdjustment =
|
||||
focusedMonitorSizeAdjustment *=
|
||||
Math.max(focusedMonitor.width / fovMonitor.width, focusedMonitor.height / fovMonitor.height);
|
||||
}
|
||||
this._write_control('breezy_desktop_display_distance', value / focusedMonitorSizeAdjustment);
|
||||
this._write_control('breezy_desktop_display_distance', distance / focusedMonitorSizeAdjustment);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -586,6 +605,18 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this.settings.disconnect(this._distance_connection);
|
||||
this._distance_connection = null;
|
||||
}
|
||||
if (this._toggle_distance_start_connection) {
|
||||
this.settings.disconnect(this._toggle_distance_start_connection);
|
||||
this._toggle_distance_start_connection = null;
|
||||
}
|
||||
if (this._toggle_distance_end_connection) {
|
||||
this.settings.disconnect(this._toggle_distance_end_connection);
|
||||
this._toggle_distance_end_connection = null;
|
||||
}
|
||||
if (this._display_size_connection) {
|
||||
this.settings.disconnect(this._display_size_connection);
|
||||
this._display_size_connection = null;
|
||||
}
|
||||
if (this._focused_monitor_distance_connection) {
|
||||
this._virtual_displays_actor.disconnect(this._focused_monitor_distance_connection);
|
||||
this._focused_monitor_distance_connection = null;
|
||||
|
|
|
|||
|
|
@ -4,18 +4,23 @@ export function degreeToRadian(degree) {
|
|||
|
||||
// FOV in radians is spherical, so doesn't follow Pythagoras' theorem
|
||||
export function diagonalToCrossFOVs(diagonalFOVRadians, aspectRatio) {
|
||||
// first convert from a spherical FOV to a diagonal FOV on a flat plane at a generic distance of 1.0
|
||||
const flatDiagonalFOV = 2 * Math.tan(diagonalFOVRadians / 2);
|
||||
// first convert from a spherical FOV to a diagonal FOV on a flat plane at a unit distance of 1.0
|
||||
const diagonalLengthUnitDistance = 2 * Math.tan(diagonalFOVRadians / 2);
|
||||
|
||||
// then convert to flat plane horizontal and vertical FOVs
|
||||
const flatVerticalFOV = flatDiagonalFOV / Math.sqrt(1 + aspectRatio * aspectRatio);
|
||||
const flatHorizontalFOV = flatVerticalFOV * aspectRatio;
|
||||
const heightUnitDistance = diagonalLengthUnitDistance / Math.sqrt(1 + aspectRatio * aspectRatio);
|
||||
const widthUnitDistance = heightUnitDistance * aspectRatio;
|
||||
|
||||
// then convert back to spherical FOV
|
||||
return {
|
||||
diagonal: diagonalFOVRadians,
|
||||
horizontal: 2 * Math.atan(flatHorizontalFOV / 2),
|
||||
vertical: 2 * Math.atan(flatVerticalFOV / 2)
|
||||
// then convert back to spherical FOV
|
||||
diagonalRadians: diagonalFOVRadians,
|
||||
horizontalRadians: 2 * Math.atan(widthUnitDistance / 2),
|
||||
verticalRadians: 2 * Math.atan(heightUnitDistance / 2),
|
||||
|
||||
// flat values are relative to a unit distance of 1.0
|
||||
diagonalLengthUnitDistance,
|
||||
widthUnitDistance,
|
||||
heightUnitDistance
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +36,10 @@ export const fovConversionFns = {
|
|||
fovEdgeToScreenCenterDistance: (edgeDistance, screenLength) => Math.sqrt(Math.pow(edgeDistance, 2) - Math.pow(screenLength / 2, 2)),
|
||||
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => Math.asin(toLength / 2 / screenEdgeDistance) * 2,
|
||||
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => {
|
||||
return toAngleOpposite / toAngleAdjacent * screenDistance
|
||||
return toAngleOpposite / toAngleAdjacent * screenDistance;
|
||||
},
|
||||
fovRadiansAtDistance: (fovRadians, unitLength, newScreenDistance) => {
|
||||
return 2 * Math.atan(unitLength / 2 / newScreenDistance);
|
||||
},
|
||||
radiansToSegments: (screenRadians) => 1
|
||||
},
|
||||
|
|
@ -42,6 +50,7 @@ export const fovConversionFns = {
|
|||
fovEdgeToScreenCenterDistance: (edgeDistance, screenLength) => edgeDistance,
|
||||
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => fovRadians / fovLength * toLength,
|
||||
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => fovLength / fovRadians * Math.atan2(toAngleOpposite, toAngleAdjacent),
|
||||
fovRadiansAtDistance: (fovRadians, unitLength, newScreenDistance) => fovRadians / newScreenDistance,
|
||||
radiansToSegments: (screenRadians) => Math.ceil(screenRadians * segmentsPerRadian)
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +68,11 @@ export const applyQuaternionToVector = (vector, quaternion) => {
|
|||
];
|
||||
}
|
||||
|
||||
export const vectorMagnitude = (vector) => {
|
||||
return Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
|
||||
}
|
||||
|
||||
export const normalizeVector = (vector) => {
|
||||
const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
|
||||
const length = vectorMagnitude(vector);
|
||||
return [vector[0] / length, vector[1] / length, vector[2] / length];
|
||||
}
|
||||
|
|
@ -28,33 +28,34 @@ function lookAheadMS(imuDateMs, lookAheadCfg, override) {
|
|||
|
||||
// Create a mesh of vertices in a pattern suitable for TRIANGLE_STRIP
|
||||
function createVertexMesh(fovDetails, monitorDetails, positionVectorNWU) {
|
||||
let fovConversions = fovDetails.curvedDisplay ? fovConversionFns.curved : fovConversionFns.flat;
|
||||
const sideEdgeDistancePixels = fovConversions.centerToFovEdgeDistance(
|
||||
let horizontalWrap = fovDetails.monitorWrappingScheme === 'horizontal';
|
||||
const horizontalConversions = fovDetails.curvedDisplay && horizontalWrap ? fovConversionFns.curved : fovConversionFns.flat;
|
||||
const sideEdgeDistancePixels = horizontalConversions.centerToFovEdgeDistance(
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
fovDetails.widthPixels
|
||||
fovDetails.sizeAdjustedWidthPixels
|
||||
);
|
||||
const horizontalRadians = fovConversions.lengthToRadians(
|
||||
fovDetails.defaultDistanceHorizontalRadians,
|
||||
const horizontalRadians = horizontalConversions.lengthToRadians(
|
||||
fovDetails.defaultDistanceHorizontalRadians,
|
||||
fovDetails.widthPixels,
|
||||
sideEdgeDistancePixels,
|
||||
monitorDetails.width
|
||||
);
|
||||
|
||||
const topEdgeDistancePixels = fovConversions.centerToFovEdgeDistance(
|
||||
let verticalWrap = fovDetails.monitorWrappingScheme === 'vertical';
|
||||
const verticalConversions = fovDetails.curvedDisplay && verticalWrap ? fovConversionFns.curved : fovConversionFns.flat;
|
||||
const topEdgeDistancePixels = verticalConversions.centerToFovEdgeDistance(
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
fovDetails.heightPixels
|
||||
fovDetails.sizeAdjustedHeightPixels
|
||||
);
|
||||
const verticalRadians = fovConversions.lengthToRadians(
|
||||
const verticalRadians = verticalConversions.lengthToRadians(
|
||||
fovDetails.defaultDistanceVerticalRadians,
|
||||
fovDetails.heightPixels,
|
||||
topEdgeDistancePixels,
|
||||
monitorDetails.height
|
||||
);
|
||||
|
||||
let horizontalWrap = fovDetails.monitorWrappingScheme === 'horizontal';
|
||||
let verticalWrap = fovDetails.monitorWrappingScheme === 'vertical';
|
||||
const xSegments = horizontalWrap ? fovConversions.radiansToSegments(horizontalRadians) : 1;
|
||||
const ySegments = verticalWrap ? fovConversions.radiansToSegments(verticalRadians) : 1;
|
||||
const xSegments = horizontalConversions.radiansToSegments(horizontalRadians);
|
||||
const ySegments = verticalConversions.radiansToSegments(verticalRadians);
|
||||
|
||||
const texXLeft = 0;
|
||||
const texYTop = 0;
|
||||
|
|
@ -148,6 +149,13 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
'pose-has-position': GObject.ParamSpec.boolean(
|
||||
'pose-has-position',
|
||||
'Pose Has Position',
|
||||
'Whether the IMU snapshots contain pose data',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
false
|
||||
),
|
||||
'smooth-follow-enabled': GObject.ParamSpec.boolean(
|
||||
'smooth-follow-enabled',
|
||||
'Smooth follow enabled',
|
||||
|
|
@ -176,12 +184,21 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
GObject.ParamFlags.READWRITE,
|
||||
true
|
||||
),
|
||||
'display-size': GObject.ParamSpec.double(
|
||||
'display-size',
|
||||
'Display size',
|
||||
'Size of the display',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.1,
|
||||
2.5,
|
||||
1.0
|
||||
),
|
||||
'display-distance': GObject.ParamSpec.double(
|
||||
'display-distance',
|
||||
'Display Distance',
|
||||
'Distance of the display from the camera',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.0,
|
||||
0.1,
|
||||
2.5,
|
||||
1.0
|
||||
),
|
||||
|
|
@ -190,7 +207,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
'Display distance default',
|
||||
'Distance to use when not explicitly set, or when reset',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.2,
|
||||
0.1,
|
||||
2.5,
|
||||
1.0
|
||||
),
|
||||
|
|
@ -253,6 +270,8 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
this._use_smooth_follow_origin = false;
|
||||
|
||||
this.connect('notify::display-distance', this._update_display_distance.bind(this));
|
||||
this.connect('notify::display-distance-default', this._update_display_distance.bind(this));
|
||||
this.connect('notify::display-size', this._update_display_position.bind(this));
|
||||
this.connect('notify::focused-monitor-index', this._update_display_distance.bind(this));
|
||||
this.connect('notify::monitor-placements', this._update_display_position.bind(this));
|
||||
this.connect('notify::show-banner', this._handle_banner_update.bind(this));
|
||||
|
|
@ -389,7 +408,11 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
finalPositionVector = noRotationVector.map(coord => coord * inverse_follow_ease);
|
||||
finalPositionVector[0] = noRotationVector[0];
|
||||
}
|
||||
this._vertices = createVertexMesh(this.fov_details, this.monitor_details, finalPositionVector);
|
||||
const resizedMonitorDetails = {
|
||||
width: this.monitor_details.width * this.fov_details.distanceAdjustedSize,
|
||||
height: this.monitor_details.height * this.fov_details.distanceAdjustedSize
|
||||
};
|
||||
this._vertices = createVertexMesh(this.fov_details, resizedMonitorDetails, finalPositionVector);
|
||||
|
||||
const rotation_radians = this.monitor_placements[this.monitor_index].rotationAngleRadians;
|
||||
if (this._initialized) {
|
||||
|
|
@ -402,8 +425,8 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
this.set_uniform_float(this.get_uniform_location("u_show_banner"), 1, [this.show_banner ? 1.0 : 0.0]);
|
||||
}
|
||||
|
||||
perspective(fovHorizontalRadians, aspect, near, far) {
|
||||
const f = 1.0 / Math.tan(fovHorizontalRadians / 2.0);
|
||||
perspective(widthUnitDistance, aspect, near, far) {
|
||||
const f = 2.0 / widthUnitDistance;
|
||||
const range = far - near;
|
||||
|
||||
return [
|
||||
|
|
@ -458,11 +481,11 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
return vec3(v.x * c + v.z * s, v.y, v.z * c - v.x * s);
|
||||
}
|
||||
|
||||
vec4 nwuToESU(vec4 v) {
|
||||
vec4 nwuToEUS(vec4 v) {
|
||||
return vec4(-v.y, v.z, -v.x, v.w);
|
||||
}
|
||||
|
||||
vec3 nwuToESU(vec3 v) {
|
||||
vec3 nwuToEUS(vec3 v) {
|
||||
return vec3(-v.y, v.z, -v.x);
|
||||
}
|
||||
|
||||
|
|
@ -492,15 +515,15 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
if (!u_show_banner) {
|
||||
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;
|
||||
vec4 quat_t0 = nwuToEUS(quatConjugate(u_pose_orientation[0]));
|
||||
vec3 position_vector = applyQuaternionToVector(nwuToEUS(u_pose_position), quat_t0);
|
||||
vec3 final_lens_position = nwuToEUS(u_lens_vector) + position_vector;
|
||||
|
||||
vec3 complete_vector = applyXRotationToVector(world_pos.xyz, u_rotation_x_radians);
|
||||
complete_vector = applyYRotationToVector(complete_vector, u_rotation_y_radians);
|
||||
|
||||
vec3 rotated_vector_t0 = applyQuaternionToVector(complete_vector, quat_t0);
|
||||
vec3 rotated_vector_t1 = applyQuaternionToVector(complete_vector, nwuToESU(quatConjugate(u_pose_orientation[1])));
|
||||
vec3 rotated_vector_t1 = applyQuaternionToVector(complete_vector, nwuToEUS(quatConjugate(u_pose_orientation[1])));
|
||||
float delta_time_t0 = u_pose_orientation[3][0] - u_pose_orientation[3][1];
|
||||
|
||||
// how quickly the vertex is moving relative to the camera
|
||||
|
|
@ -545,20 +568,20 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
this._initialized = true;
|
||||
|
||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
||||
const fovRadians = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||
const fovLengths = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||
const projection_matrix = this.perspective(
|
||||
fovRadians.horizontal,
|
||||
fovLengths.widthUnitDistance,
|
||||
aspect,
|
||||
1.0,
|
||||
10000.0
|
||||
);
|
||||
this.set_uniform_matrix(this.get_uniform_location("u_projection_matrix"), false, 4, projection_matrix);
|
||||
this.set_uniform_float(this.get_uniform_location("u_fov_vertical_radians"), 1, [fovRadians.vertical]);
|
||||
this.set_uniform_float(this.get_uniform_location("u_fov_vertical_radians"), 1, [fovLengths.verticalRadians]);
|
||||
this.set_uniform_float(this.get_uniform_location("u_display_resolution"), 2, [this.target_monitor.width, this.target_monitor.height]);
|
||||
this.set_uniform_float(this.get_uniform_location("u_look_ahead_cfg"), 4, Globals.data_stream.device_data.lookAheadCfg);
|
||||
this.set_uniform_float(this.get_uniform_location("u_actor_to_display_ratios"), 2, this.actor_to_display_ratios);
|
||||
this.set_uniform_float(this.get_uniform_location("u_actor_to_display_offsets"), 2, this.actor_to_display_offsets);
|
||||
this.set_uniform_float(this.get_uniform_location("u_lens_vector"), 3, this.lens_vector);
|
||||
this.set_uniform_float(this.get_uniform_location("u_lens_vector"), 3, this.pose_has_position ? [0.0, 0.0, 0.0] : this.lens_vector);
|
||||
this._update_display_position();
|
||||
this._handle_banner_update();
|
||||
}
|
||||
|
|
@ -571,7 +594,12 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
|||
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [0.0]);
|
||||
lookAheadSet = true;
|
||||
}
|
||||
const posePositionPixels = this.imu_snapshots.pose_position.map(coord => coord * this.fov_details.completeScreenDistancePixels);
|
||||
let posePositionPixels = [0.0, 0.0, 0.0];
|
||||
if (this.pose_has_position) {
|
||||
posePositionPixels = this.imu_snapshots.pose_position.map((coord, index) => {
|
||||
return coord * this.fov_details.fullScreenDistancePixels + this.lens_vector[index];
|
||||
});
|
||||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Shell from 'gi://Shell';
|
|||
import St from 'gi://St';
|
||||
|
||||
import { VirtualDisplayEffect, SMOOTH_FOLLOW_SLERP_TIMELINE_MS } from './virtualdisplayeffect.js';
|
||||
import { applyQuaternionToVector, degreeToRadian, diagonalToCrossFOVs, fovConversionFns, normalizeVector } from './math.js';
|
||||
import { applyQuaternionToVector, degreeToRadian, diagonalToCrossFOVs, fovConversionFns, vectorMagnitude } from './math.js';
|
||||
|
||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
|
||||
|
|
@ -19,38 +19,40 @@ const FOCUS_THRESHOLD = 0.95 / 2.0;
|
|||
// if we leave the monitor with some margin, unfocus even if no other monitor is in focus
|
||||
const UNFOCUS_THRESHOLD = 1.1 / 2.0;
|
||||
|
||||
// returns how far the look vector is from the center of the monitor, as a percentage of the monitor's width
|
||||
// returns how far the look vector is from the center of the monitor, as a percentage of the monitor's dimensions
|
||||
function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVector, monitorDetails, upAngleToLength, westAngleToLength) {
|
||||
const monitorAspectRatio = monitorDetails.width / monitorDetails.height;
|
||||
|
||||
// weight the up distance by the aspect ratio
|
||||
// since the monitor vector has been modified to be relative to the lens position, we need to calculate its distance from the lens
|
||||
// we need to adjust all angle-based lengths based on new vector distance
|
||||
const monitorDistance = vectorMagnitude(monitorVector);
|
||||
const distanceAdjustment = monitorDistance / fovDetails.completeScreenDistancePixels;
|
||||
|
||||
const vectorUpPixels = upAngleToLength(
|
||||
fovDetails.defaultDistanceVerticalRadians,
|
||||
fovDetails.heightPixels,
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
monitorDistance,
|
||||
monitorVector[2],
|
||||
monitorVector[0]
|
||||
);
|
||||
const upDeltaPixels = (lookUpPixels - vectorUpPixels) * monitorAspectRatio;
|
||||
) * distanceAdjustment;
|
||||
const upPercentage = Math.abs(lookUpPixels * distanceAdjustment - vectorUpPixels) / monitorDetails.height;
|
||||
|
||||
const vectorWestPixels = westAngleToLength(
|
||||
fovDetails.defaultDistanceHorizontalRadians,
|
||||
fovDetails.widthPixels,
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
monitorDistance,
|
||||
monitorVector[1],
|
||||
monitorVector[0]
|
||||
);
|
||||
const westDeltaPixels = lookWestPixels - vectorWestPixels;
|
||||
const totalDeltaPixels = Math.sqrt(upDeltaPixels * upDeltaPixels + westDeltaPixels * westDeltaPixels);
|
||||
) * distanceAdjustment;
|
||||
const westPercentage = Math.abs(lookWestPixels * distanceAdjustment - vectorWestPixels) / monitorDetails.width;
|
||||
|
||||
// threshold is a percentage of width, and height was already properly weighted
|
||||
return totalDeltaPixels / monitorDetails.width;
|
||||
// how close we are to any edge is the largest of the two percentages
|
||||
return Math.max(upPercentage, westPercentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the vector in the array that's closest to the quaternion rotation
|
||||
*
|
||||
* @param {number[]} quaternion - Reference quaternion [x, y, z, w]
|
||||
* @param {number[]} position - Reference position [x, y, z] in NWU space
|
||||
* @param {number[][]} monitorVectors - Array of monitor vectors [x, y, z] to search from
|
||||
* @param {number} currentFocusedIndex - Index of the currently focused monitor
|
||||
* @param {number} focusedMonitorDistance - Distance to the focused monitor, < 1.0 if zoomed in
|
||||
|
|
@ -59,7 +61,9 @@ function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVec
|
|||
* @param {Object[]} monitorsDetails - Contains x, y, width, height (coordinates from top-left) for each monitor
|
||||
* @returns {number} Index of the closest vector, if it surpasses the previous closest index by a certain margin, otherwise the previous index
|
||||
*/
|
||||
function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, focusedMonitorDistance, smoothFollowEnabled, fovDetails, monitorsDetails) {
|
||||
function findFocusedMonitor(quaternion, position, monitorVectors, currentFocusedIndex, focusedMonitorDistance, smoothFollowEnabled, fovDetails, monitorsDetails) {
|
||||
if (currentFocusedIndex !== -1 && smoothFollowEnabled) return currentFocusedIndex;
|
||||
|
||||
const lookVector = [1.0, 0.0, 0.0]; // NWU vector pointing to the center of the screen
|
||||
const rotatedLookVector = applyQuaternionToVector(lookVector, quaternion);
|
||||
|
||||
|
|
@ -82,8 +86,13 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
|||
rotatedLookVector[0]
|
||||
);
|
||||
|
||||
let closestIndex = -1;
|
||||
let closestDistance = Infinity;
|
||||
function vectorRelativeToLensPosition(vector) {
|
||||
return [
|
||||
vector[0] - position[0],
|
||||
vector[1] - position[1],
|
||||
vector[2] - position[2]
|
||||
]
|
||||
}
|
||||
|
||||
// the currently focused monitor is the most likely to be the closest, check it first and exit early if it is
|
||||
if (currentFocusedIndex !== -1) {
|
||||
|
|
@ -91,15 +100,18 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
|||
fovDetails,
|
||||
lookUpPixels,
|
||||
lookWestPixels,
|
||||
monitorVectors[currentFocusedIndex],
|
||||
vectorRelativeToLensPosition(monitorVectors[currentFocusedIndex]),
|
||||
monitorsDetails[currentFocusedIndex],
|
||||
upConversionFns.angleToLength,
|
||||
westConversionFns.angleToLength
|
||||
) * focusedMonitorDistance;
|
||||
|
||||
if (smoothFollowEnabled || focusedDistance < UNFOCUS_THRESHOLD) return currentFocusedIndex;
|
||||
if (focusedDistance < UNFOCUS_THRESHOLD) return currentFocusedIndex;
|
||||
}
|
||||
|
||||
let closestIndex = -1;
|
||||
let closestDistance = Infinity;
|
||||
|
||||
// find the vector closest to the rotated look vector
|
||||
monitorVectors.forEach((monitorVector, index) => {
|
||||
if (index === currentFocusedIndex) return;
|
||||
|
|
@ -108,7 +120,7 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
|||
fovDetails,
|
||||
lookUpPixels,
|
||||
lookWestPixels,
|
||||
monitorVector,
|
||||
vectorRelativeToLensPosition(monitorVector),
|
||||
monitorsDetails[index],
|
||||
upConversionFns.angleToLength,
|
||||
westConversionFns.angleToLength
|
||||
|
|
@ -208,8 +220,10 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
if (fovDetails.monitorWrappingScheme === 'horizontal') {
|
||||
// monitors wrap around us horizontally
|
||||
|
||||
const sideEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.widthPixels);
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
||||
const sideEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.sizeAdjustedWidthPixels);
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.sizeAdjustedWidthPixels;
|
||||
|
||||
// targetWidth is assumed to aleady be size adjusted
|
||||
const lengthToRadianFn = (targetWidth) => conversionFns.lengthToRadians(
|
||||
fovDetails.defaultDistanceHorizontalRadians,
|
||||
fovDetails.widthPixels,
|
||||
|
|
@ -217,14 +231,14 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
targetWidth
|
||||
);
|
||||
|
||||
cachedMonitorRadians[0] = -fovDetails.defaultDistanceHorizontalRadians / 2;
|
||||
cachedMonitorRadians[0] = -lengthToRadianFn(fovDetails.sizeAdjustedWidthPixels) / 2;
|
||||
horizontalMonitorSort(monitorDetailsList).forEach(({monitorDetails, originalIndex}) => {
|
||||
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, monitorSpacingPixels, monitorDetails.x, monitorDetails.width, lengthToRadianFn);
|
||||
const monitorCenterRadius = conversionFns.fovEdgeToScreenCenterDistance(sideEdgeRadius, monitorDetails.width);
|
||||
const upTopPixels = -monitorDetails.y - (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
||||
const upTopPixels = -monitorDetails.y - (monitorDetails.y / fovDetails.sizeAdjustedHeightPixels) * monitorSpacingPixels;
|
||||
|
||||
// offset for aligning this monitor's center with the fov-sized viewport's center
|
||||
const upCenterOffsetPixels = (monitorDetails.height - fovDetails.heightPixels) / 2;
|
||||
const upCenterOffsetPixels = (monitorDetails.height - fovDetails.sizeAdjustedHeightPixels) / 2;
|
||||
|
||||
// this is where our monitor's center is in relation to an fov-sized viewport centered about (0, 0)
|
||||
const upCenterPixels = upTopPixels - upCenterOffsetPixels;
|
||||
|
|
@ -240,7 +254,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
// up is flat when wrapping horizontally
|
||||
upCenterPixels
|
||||
],
|
||||
centerLook: normalizeVector([
|
||||
centerLook: [
|
||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
||||
|
||||
|
|
@ -249,7 +263,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
|
||||
// up is flat when wrapping horizontally
|
||||
upCenterPixels
|
||||
]),
|
||||
],
|
||||
rotationAngleRadians: {
|
||||
x: 0,
|
||||
y: -monitorWrapDetails.center
|
||||
|
|
@ -259,8 +273,10 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
} else if (fovDetails.monitorWrappingScheme === 'vertical') {
|
||||
// monitors wrap around us vertically
|
||||
|
||||
const topEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.heightPixels);
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.heightPixels;
|
||||
const topEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.sizeAdjustedHeightPixels);
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.sizeAdjustedHeightPixels;
|
||||
|
||||
// targetHeight is assumed to aleady be size adjusted
|
||||
const lengthToRadianFn = (targetHeight) => conversionFns.lengthToRadians(
|
||||
fovDetails.defaultDistanceVerticalRadians,
|
||||
fovDetails.heightPixels,
|
||||
|
|
@ -268,14 +284,14 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
targetHeight
|
||||
);
|
||||
|
||||
cachedMonitorRadians[0] = -fovDetails.defaultDistanceVerticalRadians / 2;
|
||||
cachedMonitorRadians[0] = -lengthToRadianFn(fovDetails.sizeAdjustedHeightPixels) / 2;
|
||||
verticalMonitorSort(monitorDetailsList).forEach(({monitorDetails, originalIndex}) => {
|
||||
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, monitorSpacingPixels, monitorDetails.y, monitorDetails.height, lengthToRadianFn);
|
||||
const monitorCenterRadius = conversionFns.fovEdgeToScreenCenterDistance(topEdgeRadius, monitorDetails.height);
|
||||
const westLeftPixels = -monitorDetails.x - (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
||||
const westLeftPixels = -monitorDetails.x - (monitorDetails.x / fovDetails.sizeAdjustedWidthPixels) * monitorSpacingPixels;
|
||||
|
||||
// offset for aligning this monitor's center with the fov-sized viewport's center
|
||||
const westCenterOffsetPixels = (monitorDetails.width - fovDetails.widthPixels) / 2;
|
||||
const westCenterOffsetPixels = (monitorDetails.width - fovDetails.sizeAdjustedWidthPixels) / 2;
|
||||
|
||||
// this is where our monitor's center is in relation to an fov-sized viewport centered about (0, 0)
|
||||
const westCenterPixels = westLeftPixels - westCenterOffsetPixels;
|
||||
|
|
@ -291,7 +307,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
// up is centered about the FOV center
|
||||
0
|
||||
],
|
||||
centerLook: normalizeVector([
|
||||
centerLook: [
|
||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
||||
|
||||
|
|
@ -300,7 +316,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
|
||||
// up is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||
-monitorCenterRadius * Math.sin(monitorWrapDetails.center)
|
||||
]),
|
||||
],
|
||||
rotationAngleRadians: {
|
||||
x: -monitorWrapDetails.center,
|
||||
y: 0
|
||||
|
|
@ -308,17 +324,16 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
});
|
||||
});
|
||||
} else {
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
||||
const monitorSpacingPixels = monitorSpacing * fovDetails.sizeAdjustedWidthPixels;
|
||||
|
||||
// monitors make a flat wall in front of us, no wrapping
|
||||
monitorDetailsList.forEach((monitorDetails, index) => {
|
||||
const upTopPixels = -monitorDetails.y - (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
||||
const westLeftPixels = -monitorDetails.x - (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
||||
const upTopPixels = -monitorDetails.y - (monitorDetails.y / fovDetails.sizeAdjustedHeightPixels) * monitorSpacingPixels;
|
||||
const westLeftPixels = -monitorDetails.x - (monitorDetails.x / fovDetails.sizeAdjustedWidthPixels) * monitorSpacingPixels;
|
||||
|
||||
// offsets for aligning this monitor's center with the fov-sized viewport's center
|
||||
const westCenterOffsetPixels = (monitorDetails.width - fovDetails.widthPixels) / 2;
|
||||
const upCenterOffsetPixels = (monitorDetails.height - fovDetails.heightPixels) / 2;
|
||||
|
||||
const westCenterOffsetPixels = (monitorDetails.width - fovDetails.sizeAdjustedWidthPixels) / 2;
|
||||
const upCenterOffsetPixels = (monitorDetails.height - fovDetails.sizeAdjustedHeightPixels) / 2;
|
||||
const westCenterPixels = westLeftPixels - westCenterOffsetPixels;
|
||||
const upCenterPixels = upTopPixels - upCenterOffsetPixels;
|
||||
|
||||
|
|
@ -329,11 +344,11 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
|||
westCenterPixels,
|
||||
upCenterPixels
|
||||
],
|
||||
centerLook: normalizeVector([
|
||||
centerLook: [
|
||||
fovDetails.completeScreenDistancePixels,
|
||||
westCenterPixels,
|
||||
upCenterPixels
|
||||
]),
|
||||
],
|
||||
rotationAngleRadians: {
|
||||
x: 0,
|
||||
y: 0
|
||||
|
|
@ -446,6 +461,13 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
|
||||
GObject.ParamFlags.READWRITE
|
||||
),
|
||||
'pose-has-position': GObject.ParamSpec.boolean(
|
||||
'pose-has-position',
|
||||
'Pose Has Position',
|
||||
'Whether the IMU snapshots contain pose data',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
false
|
||||
),
|
||||
'curved-display': GObject.ParamSpec.boolean(
|
||||
'curved-display',
|
||||
'Curved Display',
|
||||
|
|
@ -499,7 +521,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
'Display size',
|
||||
'Size of the display',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.2,
|
||||
0.1,
|
||||
2.5,
|
||||
1.0
|
||||
),
|
||||
|
|
@ -515,7 +537,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
'Display Distance',
|
||||
'Distance of the display from the camera',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.2,
|
||||
0.1,
|
||||
2.5,
|
||||
1.05
|
||||
),
|
||||
|
|
@ -537,7 +559,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
'Display distance start',
|
||||
'Start distance when using the "change distance" shortcut.',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.2,
|
||||
0.1,
|
||||
2.5,
|
||||
1.05
|
||||
),
|
||||
|
|
@ -546,7 +568,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
'Display distance end',
|
||||
'End distance when using the "change distance" shortcut.',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
0.2,
|
||||
0.1,
|
||||
2.5,
|
||||
1.05
|
||||
),
|
||||
|
|
@ -578,7 +600,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
constructor(params = {}) {
|
||||
super(params);
|
||||
|
||||
this._all_monitors = [
|
||||
this._all_monitors_unmodified = [
|
||||
this.target_monitor,
|
||||
...this.virtual_monitors
|
||||
];
|
||||
|
|
@ -649,10 +671,10 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
this._property_connections.push(this.connect(`notify::${property}`, fn.bind(this)));
|
||||
}).bind(this);
|
||||
|
||||
this._distance_ease_timeline = null;
|
||||
notifyToFunction('toggle-display-distance-start', this._handle_display_distance_properties_change);
|
||||
notifyToFunction('toggle-display-distance-end', this._handle_display_distance_properties_change);
|
||||
notifyToFunction('display-distance', this._handle_display_distance_properties_change);
|
||||
notifyToFunction('toggle-display-distance-start', this._handle_display_size_distance_change);
|
||||
notifyToFunction('toggle-display-distance-end', this._handle_display_size_distance_change);
|
||||
notifyToFunction('display-distance', this._handle_display_size_distance_change);
|
||||
notifyToFunction('display-size', this._handle_display_size_distance_change);
|
||||
notifyToFunction('monitor-wrapping-scheme', this._update_monitor_placements);
|
||||
notifyToFunction('monitor-spacing', this._update_monitor_placements);
|
||||
notifyToFunction('headset-display-as-viewport-center', this._update_monitor_placements);
|
||||
|
|
@ -663,7 +685,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
notifyToFunction('custom-banner-enabled', this._handle_banner_update);
|
||||
notifyToFunction('framerate-cap', this._handle_frame_rate_cap_change);
|
||||
notifyToFunction('smooth-follow-enabled', this._handle_smooth_follow_enabled_change);
|
||||
this._handle_display_distance_properties_change();
|
||||
this._handle_display_size_distance_change();
|
||||
this._handle_frame_rate_cap_change();
|
||||
|
||||
const actorToDisplayRatios = [
|
||||
|
|
@ -680,8 +702,8 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
];
|
||||
|
||||
Globals.logger.log_debug(`\t\t\tActor to display ratios: ${actorToDisplayRatios}, offsets: ${actorToDisplayOffsets}`);
|
||||
|
||||
this._all_monitors.forEach(((monitor, index) => {
|
||||
|
||||
this._all_monitors_unmodified.forEach(((monitor, index) => {
|
||||
Globals.logger.log_debug(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);
|
||||
|
||||
const containerActor = new Clutter.Actor({
|
||||
|
|
@ -706,11 +728,13 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
const effect = new VirtualDisplayEffect({
|
||||
focused_monitor_index: this.focused_monitor_index,
|
||||
imu_snapshots: this.imu_snapshots,
|
||||
pose_has_position: this.pose_has_position,
|
||||
monitor_index: index,
|
||||
monitor_details: monitor,
|
||||
monitor_placements: this.monitor_placements,
|
||||
fov_details: this.fov_details,
|
||||
target_monitor: this.target_monitor,
|
||||
display_size: this.display_size,
|
||||
display_distance: this.display_distance,
|
||||
display_distance_default: this._display_distance_default(),
|
||||
actor_to_display_ratios: actorToDisplayRatios,
|
||||
|
|
@ -735,8 +759,10 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
|
||||
[
|
||||
'monitor-placements',
|
||||
'display-size',
|
||||
'fov-details',
|
||||
'imu-snapshots',
|
||||
'pose-has-position',
|
||||
'smooth-follow-enabled',
|
||||
'smooth-follow-toggle-epoch-ms',
|
||||
'focused-monitor-index',
|
||||
|
|
@ -789,8 +815,13 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
this.imu_snapshots.smooth_follow_origin.splice(0, 4) :
|
||||
this.imu_snapshots.pose_orientation.splice(0, 4);
|
||||
|
||||
const currentPosition = this.pose_has_position ?
|
||||
this.imu_snapshots.pose_position.map(coord => coord * this.fov_details.fullScreenDistancePixels) :
|
||||
[0.0, 0.0, 0.0];
|
||||
|
||||
const focusedMonitorIndex = findFocusedMonitor(
|
||||
currentOrientationQuat,
|
||||
currentPosition,
|
||||
this.monitor_placements.map(monitorVectors => monitorVectors.centerLook),
|
||||
this.focused_monitor_index,
|
||||
this.display_distance / this._display_distance_default(),
|
||||
|
|
@ -827,34 +858,61 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
this._redraw_timeline.start();
|
||||
}
|
||||
|
||||
_size_adjusted_target_monitor() {
|
||||
return this._all_monitors[0];
|
||||
}
|
||||
|
||||
_display_distance_default() {
|
||||
return Math.max(this.display_distance, this.toggle_display_distance_start, this.toggle_display_distance_end);
|
||||
}
|
||||
|
||||
_fov_details() {
|
||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
||||
const fovRadians = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||
const fovLengths = diagonalToCrossFOVs(degreeToRadian(Globals.data_stream.device_data.displayFov), aspect);
|
||||
const monitorWrappingScheme = this._actual_wrap_scheme();
|
||||
const defaultDistance = this._display_distance_default();
|
||||
const lensDistanceComplement = 1.0 - Globals.data_stream.device_data.lensDistanceRatio;
|
||||
const lensDistanceFactor = (1.0 / lensDistanceComplement) - 1.0;
|
||||
const horizontalConversions = this.curved_display && monitorWrappingScheme === 'horizontal' ? fovConversionFns.curved : fovConversionFns.flat;
|
||||
const verticalConversions = this.curved_display && monitorWrappingScheme === 'vertical' ? fovConversionFns.curved : fovConversionFns.flat;
|
||||
|
||||
// adjust FOV to a new focal point distance while keeping screens the same size
|
||||
// i.e. focus from pivot point to new screen distance, adjusted from lens at unit distance
|
||||
const defaultDistanceVerticalRadians = verticalConversions.fovRadiansAtDistance(
|
||||
fovLengths.verticalRadians,
|
||||
fovLengths.heightUnitDistance,
|
||||
defaultDistance
|
||||
);
|
||||
const defaultDistanceHorizontalRadians = horizontalConversions.fovRadiansAtDistance(
|
||||
fovLengths.horizontalRadians,
|
||||
fovLengths.widthUnitDistance,
|
||||
defaultDistance
|
||||
);
|
||||
|
||||
// adjusted angles based on how far away the screens are e.g. a closer screen takes up a larger slice of our FOV
|
||||
const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / this._display_distance_default());
|
||||
const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / this._display_distance_default());
|
||||
// distance needed for the FOV-sized monitor to fill up the screen, as measured from the lenses
|
||||
const lensToUnitDistancePixels = this.target_monitor.width / fovLengths.widthUnitDistance;
|
||||
|
||||
// distance needed for the FOV-sized monitor to fill up the screen
|
||||
const fullScreenDistance = this.target_monitor.height / 2 / Math.tan(fovRadians.vertical / 2);
|
||||
const lensDistancePixels = fullScreenDistance / (1.0 - Globals.data_stream.device_data.lensDistanceRatio) - fullScreenDistance;
|
||||
// distance from pivot point to lens
|
||||
const lensDistancePixels = lensToUnitDistancePixels * lensDistanceFactor;
|
||||
|
||||
// distance of a display at the default (most zoomed out) distance, plus the lens distance constant
|
||||
const lensToScreenDistance = this.target_monitor.height / 2 / Math.tan(defaultDistanceVerticalRadians / 2);
|
||||
const completeScreenDistancePixels = lensToScreenDistance + lensDistancePixels;
|
||||
// distance from pivot point to full screen (monitor at unit distance from lens)
|
||||
const fullScreenDistancePixels = lensToUnitDistancePixels + lensDistancePixels;
|
||||
|
||||
// distance of a display at the default (most zoomed out) distance from the pivot point
|
||||
const completeScreenDistancePixels = fullScreenDistancePixels * defaultDistance;
|
||||
|
||||
return {
|
||||
widthPixels: this.target_monitor.width,
|
||||
distanceAdjustedSize: this._distance_adjusted_size,
|
||||
sizeAdjustedWidthPixels: this.target_monitor.width * this._distance_adjusted_size,
|
||||
heightPixels: this.target_monitor.height,
|
||||
sizeAdjustedHeightPixels: this.target_monitor.height * this._distance_adjusted_size,
|
||||
defaultDistanceVerticalRadians,
|
||||
defaultDistanceHorizontalRadians,
|
||||
lensDistancePixels,
|
||||
fullScreenDistancePixels,
|
||||
completeScreenDistancePixels,
|
||||
monitorWrappingScheme: this._actual_wrap_scheme(),
|
||||
monitorWrappingScheme,
|
||||
curvedDisplay: this.curved_display
|
||||
};
|
||||
}
|
||||
|
|
@ -869,7 +927,8 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
const minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
|
||||
const maxY = Math.max(...this._all_monitors.map(monitor => monitor.y + monitor.height));
|
||||
|
||||
if ((maxX - minX) / this.target_monitor.width >= (maxY - minY) / this.target_monitor.height) {
|
||||
const targetMonitor = this._size_adjusted_target_monitor();
|
||||
if ((maxX - minX) / targetMonitor.width >= (maxY - minY) / targetMonitor.height) {
|
||||
return 'horizontal';
|
||||
} else {
|
||||
return 'vertical';
|
||||
|
|
@ -878,27 +937,29 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
|
||||
_update_monitor_placements() {
|
||||
try {
|
||||
const targetMonitor = this._size_adjusted_target_monitor();
|
||||
|
||||
const minX = Math.min(...this._all_monitors.map(monitor => monitor.x));
|
||||
const maxX = Math.max(...this._all_monitors.map(monitor => monitor.x + monitor.width));
|
||||
const minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
|
||||
const maxY = Math.max(...this._all_monitors.map(monitor => monitor.y + monitor.height));
|
||||
|
||||
// the beginning edges of the viewport if it's centered on all displays
|
||||
const allDisplaysCenterXBegin = (minX + maxX) / 2 - this.target_monitor.width / 2;
|
||||
const allDisplaysCenterYBegin = (minY + maxY) / 2 - this.target_monitor.height / 2;
|
||||
const allDisplaysCenterXBegin = (minX + maxX) / 2 - targetMonitor.width / 2;
|
||||
const allDisplaysCenterYBegin = (minY + maxY) / 2 - targetMonitor.height / 2;
|
||||
|
||||
const viewportXBegin = this.headset_display_as_viewport_center ? this.target_monitor.x : allDisplaysCenterXBegin;
|
||||
const viewportYBegin = this.headset_display_as_viewport_center ? this.target_monitor.y : allDisplaysCenterYBegin;
|
||||
const viewportXBegin = this.headset_display_as_viewport_center ? targetMonitor.x : allDisplaysCenterXBegin;
|
||||
const viewportYBegin = this.headset_display_as_viewport_center ? targetMonitor.y : allDisplaysCenterYBegin;
|
||||
|
||||
this.fov_details = this._fov_details();
|
||||
this.lens_vector = [0.0, 0.0, -this.fov_details.lensDistancePixels];
|
||||
this.lens_vector = [this.fov_details.lensDistancePixels, 0.0, 0.0];
|
||||
this.monitor_placements = monitorsToPlacements(
|
||||
this.fov_details,
|
||||
|
||||
// shift all monitors so they center around the viewport center, then adjusted by the offsets
|
||||
this._all_monitors.map(monitor => ({
|
||||
x: monitor.x - viewportXBegin - this.viewport_offset_x * this.target_monitor.width,
|
||||
y: monitor.y - viewportYBegin + this.viewport_offset_y * this.target_monitor.height,
|
||||
x: monitor.x - viewportXBegin - this.viewport_offset_x * targetMonitor.width,
|
||||
y: monitor.y - viewportYBegin + this.viewport_offset_y * targetMonitor.height,
|
||||
width: monitor.width,
|
||||
height: monitor.height
|
||||
})),
|
||||
|
|
@ -909,10 +970,22 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
|||
}
|
||||
}
|
||||
|
||||
_handle_display_distance_properties_change() {
|
||||
_handle_display_size_distance_change() {
|
||||
this._distance_adjusted_size = (this._display_distance_default() - Globals.data_stream.device_data.lensDistanceRatio) * this.display_size;
|
||||
|
||||
const distance_from_end = Math.abs(this.display_distance - this.toggle_display_distance_end);
|
||||
const distance_from_start = Math.abs(this.display_distance - this.toggle_display_distance_start);
|
||||
this._is_display_distance_at_end = distance_from_end < distance_from_start;
|
||||
|
||||
const sizeComplement = (1.0 - this._distance_adjusted_size) / 2.0;
|
||||
const sizeViewportOffsetX = sizeComplement * this.target_monitor.width;
|
||||
const sizeViewportOffsetY = sizeComplement * this.target_monitor.height;
|
||||
this._all_monitors = this._all_monitors_unmodified.map(monitor => ({
|
||||
x: monitor.x * this._distance_adjusted_size + sizeViewportOffsetX,
|
||||
y: monitor.y * this._distance_adjusted_size + sizeViewportOffsetY,
|
||||
width: monitor.width * this._distance_adjusted_size,
|
||||
height: monitor.height * this._distance_adjusted_size
|
||||
}));
|
||||
this._update_monitor_placements();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit a643978295eca99ca5b86e5b17aef29a1b713092
|
||||
Subproject commit 7cf5924771b01bccf6d8d8ab0a9a38d3aa37babb
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 40326b9ec0266352f24500f47693c06f39832509
|
||||
Subproject commit e3da76189390d81fda70ced38976a2061a8711ea
|
||||
|
|
@ -100,6 +100,15 @@
|
|||
The size of the display
|
||||
</description>
|
||||
</key>
|
||||
<key name="units" type="s">
|
||||
<default>
|
||||
"cm"
|
||||
</default>
|
||||
<summary>Measurement units</summary>
|
||||
<description>
|
||||
Units to display for physical measurements: "cm" or "in"
|
||||
</description>
|
||||
</key>
|
||||
<key name="viewport-offset-x" type="d">
|
||||
<default>
|
||||
0.0
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit da173bd9e0392aaeb2cb68a332e5d4a20dd4dae1
|
||||
Subproject commit 1655c2bb03a4e75bf2c9a9815a96b71342396fbd
|
||||
|
|
@ -33,6 +33,8 @@ class ConnectedDevice(Gtk.Box):
|
|||
effect_enable_switch = Gtk.Template.Child()
|
||||
disable_physical_displays_switch = Gtk.Template.Child()
|
||||
display_zoom_on_focus_switch = Gtk.Template.Child()
|
||||
display_size_scale = Gtk.Template.Child()
|
||||
display_size_adjustment = Gtk.Template.Child()
|
||||
follow_threshold_scale = Gtk.Template.Child()
|
||||
follow_threshold_adjustment = Gtk.Template.Child()
|
||||
follow_mode_switch = Gtk.Template.Child()
|
||||
|
|
@ -83,6 +85,7 @@ class ConnectedDevice(Gtk.Box):
|
|||
viewport_offset_x_adjustment = Gtk.Template.Child()
|
||||
viewport_offset_y_scale = Gtk.Template.Child()
|
||||
viewport_offset_y_adjustment = Gtk.Template.Child()
|
||||
units_menu = Gtk.Template.Child()
|
||||
|
||||
def __init__(self):
|
||||
super(Gtk.Box, self).__init__()
|
||||
|
|
@ -90,7 +93,7 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.active = True
|
||||
self.all_enabled_state_inputs = [
|
||||
self.display_zoom_on_focus_switch,
|
||||
# self.display_size_scale,
|
||||
self.display_size_scale,
|
||||
self.follow_mode_switch,
|
||||
self.follow_threshold_scale,
|
||||
self.curved_display_switch,
|
||||
|
|
@ -115,7 +118,7 @@ class ConnectedDevice(Gtk.Box):
|
|||
|
||||
self.settings.bind('disable-physical-displays', self.disable_physical_displays_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||
self.settings.connect('changed::display-distance', self._handle_display_distance)
|
||||
# self.settings.bind('display-size', self.display_size_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||
self.settings.bind('display-size', self.display_size_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||
self.settings.bind('follow-threshold', self.follow_threshold_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||
# self.settings.bind('widescreen-mode', self.widescreen_mode_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||
self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||
|
|
@ -136,6 +139,10 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.monitor_wrapping_scheme_menu.connect('changed', self._handle_monitor_wrapping_scheme_menu_changed)
|
||||
self._handle_monitor_wrapping_scheme_setting_changed(self.settings, self.settings.get_string('monitor-wrapping-scheme'))
|
||||
|
||||
current_units = self.settings.get_string('units')
|
||||
self.units_menu.set_active_id(current_units)
|
||||
self.units_menu.connect('changed', self._handle_units_menu_changed)
|
||||
|
||||
bind_shortcut_settings(self.get_parent(), [
|
||||
[self.reassign_toggle_xr_effect_shortcut_button, self.toggle_xr_effect_shortcut_label],
|
||||
[self.reassign_recenter_display_shortcut_button, self.recenter_display_shortcut_label],
|
||||
|
|
@ -176,6 +183,10 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.follow_mode_switch.connect('notify::active', self._refresh_follow_mode)
|
||||
self.effect_enable_switch.connect('notify::active', self._handle_switch_enabled_state)
|
||||
|
||||
self.state_manager.connect('notify::connected-device-full-size-cm', self._handle_metric_change)
|
||||
self.state_manager.connect('notify::connected-device-full-distance-cm', self._handle_metric_change)
|
||||
self.settings.connect('changed::units', self._handle_units_changed)
|
||||
|
||||
self.config_manager = ConfigManager.get_instance()
|
||||
self.config_manager.connect('notify::breezy-desktop-enabled', self._handle_enabled_config)
|
||||
self._bind_switch_to_config(self.enable_multi_tap_switch, 'multi-tap-enabled')
|
||||
|
|
@ -238,6 +249,18 @@ class ConnectedDevice(Gtk.Box):
|
|||
elif not widget.get_active() and is_zoom_on_focus_already_enabled:
|
||||
self.settings.set_double('display-distance', toggle_display_distance_end)
|
||||
|
||||
def _handle_units_menu_changed(self, widget):
|
||||
active_id = widget.get_active_id() or 'cm'
|
||||
self.settings.set_string('units', active_id)
|
||||
|
||||
def _handle_units_changed(self, *args):
|
||||
self._set_all_displays_distance(self.settings.get_double('toggle-display-distance-end'))
|
||||
self._set_focused_display_distance(self.settings.get_double('toggle-display-distance-start'))
|
||||
|
||||
def _handle_metric_change(self, *args):
|
||||
self._set_all_displays_distance(self.settings.get_double('toggle-display-distance-end'))
|
||||
self._set_focused_display_distance(self.settings.get_double('toggle-display-distance-start'))
|
||||
|
||||
def _handle_monitor_wrapping_scheme_setting_changed(self, settings, val):
|
||||
self.monitor_wrapping_scheme_menu.set_active_id(val)
|
||||
|
||||
|
|
@ -314,17 +337,33 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.display_zoom_on_focus_switch.set_active(should_zoom_on_focus_be_enabled)
|
||||
|
||||
def _set_focused_display_distance(self, distance):
|
||||
self.focused_display_distance_label.set_markup(f"{_('Focused display')}: <b>{distance}</b>")
|
||||
self.focused_display_distance_label.set_markup(f"{_('Focused display')}: <b>{self._format_distance(distance)}</b>")
|
||||
self.settings.set_double('toggle-display-distance-start', distance)
|
||||
|
||||
self.display_zoom_on_focus_switch.set_sensitive(distance != self.settings.get_double('toggle-display-distance-end'))
|
||||
|
||||
def _set_all_displays_distance(self, distance):
|
||||
self.all_displays_distance_label.set_markup(f"{_('All displays')}: <b>{distance}</b>")
|
||||
self.all_displays_distance_label.set_markup(f"{_('All displays')}: <b>{self._format_distance(distance)}</b>")
|
||||
self.settings.set_double('toggle-display-distance-end', distance)
|
||||
self.display_zoom_on_focus_switch.set_active(False)
|
||||
self.display_zoom_on_focus_switch.set_sensitive(distance != self.settings.get_double('toggle-display-distance-start'))
|
||||
|
||||
def _get_units(self):
|
||||
units = self.settings.get_string('units')
|
||||
return units if units in ['cm', 'in'] else 'cm'
|
||||
|
||||
def _format_distance(self, normalized):
|
||||
sm = getattr(self, 'state_manager', None) or StateManager.get_instance()
|
||||
full_cm = float(sm.get_property('connected-device-full-distance-cm') or 0.0)
|
||||
if full_cm <= 0:
|
||||
# Fallback to normalized display if metric unknown
|
||||
return f"{round(normalized, 2)}"
|
||||
cm = normalized * full_cm
|
||||
if self._get_units() == 'in':
|
||||
inches = cm / 2.54
|
||||
return f"{inches:.2f} in"
|
||||
return f"{cm:.1f} cm"
|
||||
|
||||
def _on_display_distance_preset_change_button_clicked(self, widget, settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit):
|
||||
dialog = DisplayDistanceDialog(settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit)
|
||||
dialog.set_transient_for(widget.get_ancestor(Gtk.Window))
|
||||
|
|
@ -332,23 +371,28 @@ class ConnectedDevice(Gtk.Box):
|
|||
|
||||
def _on_set_all_displays_distance(self, prev_distance, distance):
|
||||
focused_display_distance = self.settings.get_double('toggle-display-distance-start')
|
||||
all_displays_distance = self.settings.get_double('toggle-display-distance-end')
|
||||
if (distance < focused_display_distance):
|
||||
self._set_focused_display_distance(distance)
|
||||
|
||||
all_displays_distance = self.settings.get_double('toggle-display-distance-end')
|
||||
self._set_all_displays_distance(distance)
|
||||
|
||||
if prev_distance == focused_display_distance:
|
||||
self.settings.set_double('display-distance', prev_distance)
|
||||
# if we were at the unfocused distance, put us at the new unfocused distance
|
||||
if prev_distance == all_displays_distance:
|
||||
self.settings.set_double('display-distance', distance)
|
||||
|
||||
def _on_set_focused_display_distance(self, prev_distance, distance):
|
||||
focused_display_distance = self.settings.get_double('toggle-display-distance-start')
|
||||
all_displays_distance = self.settings.get_double('toggle-display-distance-end')
|
||||
if (distance > all_displays_distance):
|
||||
self._set_all_displays_distance(distance)
|
||||
|
||||
focused_display_distance = self.settings.get_double('toggle-display-distance-start')
|
||||
self._set_focused_display_distance(distance)
|
||||
|
||||
# if we were at the focused distance, put us at the new focused distance
|
||||
if prev_distance == focused_display_distance:
|
||||
self.settings.set_double('display-distance', distance)
|
||||
|
||||
def _save_custom_resolutions(self):
|
||||
with open(self._custom_resolutions_file_path, 'w') as f:
|
||||
json.dump(self._custom_resolution_options, f)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from .displaydistancedialogcontent import DisplayDistanceDialogContent
|
|||
class DisplayDistanceDialog(Gtk.Dialog):
|
||||
__gtype_name__ = 'DisplayDistanceDialog'
|
||||
|
||||
show_full_scale_button = Gtk.Template.Child()
|
||||
save_button = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit):
|
||||
|
|
@ -15,14 +14,9 @@ class DisplayDistanceDialog(Gtk.Dialog):
|
|||
self.on_save_callback = on_save_callback
|
||||
self.set_title(title)
|
||||
|
||||
self.content = DisplayDistanceDialogContent(settings_key, self.show_full_scale_button, self.save_button, self._on_save_callback, subtitle, lower_limit, upper_limit)
|
||||
self.content = DisplayDistanceDialogContent(settings_key, self.save_button, self._on_save_callback, subtitle, lower_limit, upper_limit)
|
||||
self.get_content_area().append(self.content)
|
||||
|
||||
self.show_full_scale_button.connect('clicked', self._on_show_full_scale_button_clicked)
|
||||
|
||||
def _on_show_full_scale_button_clicked(self, button):
|
||||
self.show_full_scale_button.set_visible(False)
|
||||
|
||||
def _on_save_callback(self, prev_distance, distance):
|
||||
self.on_save_callback(prev_distance, distance)
|
||||
self.close()
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from gi.repository import Gtk, Gio
|
||||
from .settingsmanager import SettingsManager
|
||||
from .statemanager import StateManager
|
||||
|
||||
import gettext
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ class DisplayDistanceDialogContent(Gtk.Box):
|
|||
display_distance_scale = Gtk.Template.Child()
|
||||
display_distance_adjustment = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, settings_key, show_full_scale_button, save_button, on_save_callback, subtitle, lower_limit, upper_limit):
|
||||
def __init__(self, settings_key, save_button, on_save_callback, subtitle, lower_limit, upper_limit):
|
||||
super(Gtk.Box, self).__init__()
|
||||
self.init_template()
|
||||
|
||||
|
|
@ -22,37 +23,28 @@ class DisplayDistanceDialogContent(Gtk.Box):
|
|||
|
||||
self.on_save_callback = on_save_callback
|
||||
self.settings = SettingsManager.get_instance().settings
|
||||
self.state_manager = StateManager.get_instance()
|
||||
self.prev_distance = self.settings.get_double('display-distance')
|
||||
self.display_distance_adjustment.set_value(self.settings.get_double(settings_key))
|
||||
|
||||
self.lower_limit_orig = self.display_distance_adjustment.get_lower()
|
||||
self.upper_limit_orig = self.display_distance_adjustment.get_upper()
|
||||
|
||||
self._add_marks(lower_limit, upper_limit)
|
||||
|
||||
self.settings.bind('display-distance', self.display_distance_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||
|
||||
show_full_scale_button.connect('clicked', self._on_show_full_scale_button_clicked)
|
||||
self.display_distance_scale.set_format_value_func(lambda scale, val: self._format_distance(val))
|
||||
self.state_manager.connect('notify::connected-device-full-distance-cm', lambda *args: self.display_distance_scale.queue_draw())
|
||||
self.settings.connect('changed::units', lambda *args: self.display_distance_scale.queue_draw())
|
||||
save_button.connect('clicked', self._on_save_button_clicked)
|
||||
|
||||
def _add_marks(self, lower_limit, upper_limit):
|
||||
self.display_distance_scale.clear_marks()
|
||||
|
||||
if self.lower_limit_orig == lower_limit:
|
||||
self.display_distance_scale.add_mark(self.lower_limit_orig, Gtk.PositionType.BOTTOM, _("closer"))
|
||||
else:
|
||||
self.display_distance_adjustment.set_lower(lower_limit)
|
||||
|
||||
self.display_distance_scale.add_mark(1.0, Gtk.PositionType.BOTTOM, _("fullscreen"))
|
||||
|
||||
if self.upper_limit_orig == upper_limit:
|
||||
self.display_distance_scale.add_mark(self.upper_limit_orig, Gtk.PositionType.BOTTOM, _("farther"))
|
||||
else:
|
||||
self.display_distance_adjustment.set_upper(upper_limit)
|
||||
|
||||
def _on_show_full_scale_button_clicked(self, button):
|
||||
self._add_marks(self.lower_limit_orig, self.upper_limit_orig)
|
||||
self.display_distance_adjustment.set_lower(self.lower_limit_orig)
|
||||
self.display_distance_adjustment.set_upper(self.upper_limit_orig)
|
||||
|
||||
def _on_save_button_clicked(self, button):
|
||||
self.on_save_callback(self.prev_distance, self.display_distance_adjustment.get_value())
|
||||
self.on_save_callback(self.prev_distance, self.display_distance_adjustment.get_value())
|
||||
|
||||
def _get_units(self):
|
||||
units = self.settings.get_string('units')
|
||||
return units if units in ['cm', 'in'] else 'cm'
|
||||
|
||||
def _format_distance(self, normalized):
|
||||
full_cm = float(self.state_manager.get_property('connected-device-full-distance-cm') or 0.0)
|
||||
if full_cm <= 0:
|
||||
return f"{round(normalized, 2)}"
|
||||
cm = normalized * full_cm
|
||||
if self._get_units() == 'in':
|
||||
inches = cm / 2.54
|
||||
return f"{inches:.2f} in"
|
||||
return f"{cm:.1f} cm"
|
||||
|
|
@ -229,6 +229,36 @@
|
|||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow" id="display_size_row">
|
||||
<property name="title" translatable="yes"><!-- adjustment slider -->Display size</property>
|
||||
<property name="subtitle" translatable="yes">Set how large you want the display to appear.</property>
|
||||
<child>
|
||||
<object class="GtkScale" id="display_size_scale">
|
||||
<property name="valign">3</property>
|
||||
<property name="draw-value">true</property>
|
||||
<property name="value-pos">0</property>
|
||||
<property name="digits">2</property>
|
||||
<property name="width-request">350</property>
|
||||
<property name="has-origin">false</property>
|
||||
<property name="adjustment">
|
||||
<object class="GtkAdjustment" id="display_size_adjustment">
|
||||
<property name="lower">0.1</property>
|
||||
<property name="upper">2.5</property>
|
||||
<property name="step-increment">0.01</property>
|
||||
<property name="value">1.0</property>
|
||||
</object>
|
||||
</property>
|
||||
<marks>
|
||||
<mark value="0.5" position="bottom">0.5×</mark>
|
||||
<mark value="1.0" position="bottom" translatable="yes">full</mark>
|
||||
<mark value="1.5" position="bottom">1.5×</mark>
|
||||
<mark value="2.0" position="bottom">2.0×</mark>
|
||||
</marks>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes"><!-- adjustment slider -->Follow threshold</property>
|
||||
|
|
@ -554,6 +584,35 @@
|
|||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="yes"><!-- section heading for the advanced settings -->Advanced Settings</property>
|
||||
<property name="width-request">450</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">Units</property>
|
||||
<property name="subtitle" translatable="yes">Choose measurement units for size and distance displays.</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">30</property>
|
||||
<property name="width-request">150</property>
|
||||
<property name="margin-start">30</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="valign">3</property>
|
||||
<style>
|
||||
<class name="flat"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="units_menu">
|
||||
<items>
|
||||
<item translatable="yes" id="cm">Centimeters</item>
|
||||
<item translatable="yes" id="in">Inches</item>
|
||||
</items>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes"><!-- feature that tries to the find best-fit monitor config -->Find optimal display config</property>
|
||||
|
|
|
|||
|
|
@ -25,12 +25,17 @@
|
|||
<property name="has-origin">false</property>
|
||||
<property name="adjustment">
|
||||
<object class="GtkAdjustment" id="display_distance_adjustment">
|
||||
<property name="lower">0.2</property>
|
||||
<property name="upper">2.5</property>
|
||||
<property name="lower">0.1</property>
|
||||
<property name="upper">1.5</property>
|
||||
<property name="step-increment">0.01</property>
|
||||
<property name="value">1.05</property>
|
||||
</object>
|
||||
</property>
|
||||
<marks>
|
||||
<mark value="0.1" position="bottom" translatable="yes">closer</mark>
|
||||
<mark value="1.0" position="bottom" translatable="yes">default</mark>
|
||||
<mark value="1.5" position="bottom" translatable="yes">farther</mark>
|
||||
</marks>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,6 @@
|
|||
<template class="DisplayDistanceDialog" parent="GtkDialog">
|
||||
<property name="modal">1</property>
|
||||
<property name="use-header-bar">1</property>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="show_full_scale_button">
|
||||
<property name="label" translatable="yes">Show full range</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="action">
|
||||
<object class="GtkButton" id="save_button">
|
||||
<property name="label" translatable="yes">Done</property>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class StateManager(GObject.GObject):
|
|||
'license-present': (bool, 'License Present', 'Whether a license is present', False, GObject.ParamFlags.READWRITE),
|
||||
'enabled-features-list': (object, 'Enabled Features List', 'A list of the enabled features', GObject.ParamFlags.READWRITE),
|
||||
'device-supports-sbs': (bool, 'Device Supports SBS', 'Whether the connected device supports SBS', False, GObject.ParamFlags.READWRITE),
|
||||
'connected-device-full-distance-cm': (float, 'Full Distance (cm)', 'Device full distance in cm', 0.0, 10000.0, 0.0, GObject.ParamFlags.READWRITE),
|
||||
'connected-device-full-size-cm': (float, 'Full Size (cm)', 'Device full display size in cm', 0.0, 10000.0, 0.0, GObject.ParamFlags.READWRITE),
|
||||
}
|
||||
|
||||
_instance = None
|
||||
|
|
@ -59,6 +61,8 @@ class StateManager(GObject.GObject):
|
|||
self.license_present = False
|
||||
self.enabled_features = []
|
||||
self.device_supports_sbs = False
|
||||
self.connected_device_full_distance_cm = 0.0
|
||||
self.connected_device_full_size_cm = 0.0
|
||||
self._running = True
|
||||
self._refresh_state()
|
||||
|
||||
|
|
@ -98,6 +102,14 @@ class StateManager(GObject.GObject):
|
|||
self.set_property('device-supports-sbs', self.state.get('sbs_mode_supported', False))
|
||||
self.set_property('widescreen-mode', self.state.get('sbs_mode_enabled', False))
|
||||
|
||||
full_distance = self.state.get('connected_device_full_distance_cm') or 0.0
|
||||
if full_distance != self.connected_device_full_distance_cm:
|
||||
self.set_property('connected-device-full-distance-cm', full_distance)
|
||||
|
||||
full_size = self.state.get('connected_device_full_size_cm') or 0.0
|
||||
if full_size != self.connected_device_full_size_cm:
|
||||
self.set_property('connected-device-full-size-cm', full_size)
|
||||
|
||||
if self._running: threading.Timer(1.0, self._refresh_state).start()
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
|
|
@ -115,6 +127,10 @@ class StateManager(GObject.GObject):
|
|||
self.enabled_features = value
|
||||
if prop.name == 'device-supports-sbs':
|
||||
self.device_supports_sbs = value
|
||||
if prop.name == 'connected-device-full-distance-cm':
|
||||
self.connected_device_full_distance_cm = value
|
||||
if prop.name == 'connected-device-full-size-cm':
|
||||
self.connected_device_full_size_cm = value
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name == 'driver-running':
|
||||
|
|
@ -130,4 +146,8 @@ class StateManager(GObject.GObject):
|
|||
if prop.name == 'enabled-features-list':
|
||||
return self.enabled_features
|
||||
if prop.name == 'device-supports-sbs':
|
||||
return self.device_supports_sbs
|
||||
return self.device_supports_sbs
|
||||
if prop.name == 'connected-device-full-distance-cm':
|
||||
return self.connected_device_full_distance_cm
|
||||
if prop.name == 'connected-device-full-size-cm':
|
||||
return self.connected_device_full_size_cm
|
||||
Loading…
Reference in New Issue