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
|
# build XR driver
|
||||||
XR_DRIVER_BINARY=$XR_DRIVER_DIR/out/xrDriver-$ARCH.tar.gz
|
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 [ ! -e "$XR_DRIVER_BINARY" ] || [ "$1" == "--rebuild-driver" ]; then
|
||||||
# if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory
|
# if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory
|
||||||
|
|
@ -71,6 +75,9 @@ 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
|
# strange issue where the base library produces a .so file if the build is not cleaned
|
||||||
rm -rf build/
|
rm -rf build/
|
||||||
|
|
||||||
|
if [ -n "${STEAMOS:-}" ]; then
|
||||||
|
export STEAMOS
|
||||||
|
fi
|
||||||
docker-build/init.sh
|
docker-build/init.sh
|
||||||
docker-build/run-build.sh $ARCH
|
docker-build/run-build.sh $ARCH
|
||||||
popd
|
popd
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
this._data_stream_bindings = [];
|
this._data_stream_bindings = [];
|
||||||
this._show_banner_connection = null;
|
this._show_banner_connection = null;
|
||||||
this._distance_connection = null;
|
this._distance_connection = null;
|
||||||
|
this._display_size_connection = null;
|
||||||
this._focused_monitor_distance_connection = null;
|
this._focused_monitor_distance_connection = null;
|
||||||
this._follow_threshold_connection = null;
|
this._follow_threshold_connection = null;
|
||||||
this._breezy_desktop_running_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_position(targetMonitor.x, targetMonitor.y);
|
||||||
this._virtual_displays_overlay.set_size(targetMonitor.width, targetMonitor.height);
|
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();
|
Globals.data_stream.refresh_data();
|
||||||
this._virtual_displays_actor = new VirtualDisplaysActor({
|
this._virtual_displays_actor = new VirtualDisplaysActor({
|
||||||
width: targetMonitor.width,
|
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_x: this.settings.get_double('viewport-offset-x'),
|
||||||
viewport_offset_y: this.settings.get_double('viewport-offset-y'),
|
viewport_offset_y: this.settings.get_double('viewport-offset-y'),
|
||||||
display_distance: this.settings.get_double('display-distance'),
|
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_start: this.settings.get_double('toggle-display-distance-start'),
|
||||||
toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'),
|
toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'),
|
||||||
framerate_cap: this.settings.get_double('framerate-cap'),
|
framerate_cap: this.settings.get_double('framerate-cap'),
|
||||||
imu_snapshots: Globals.data_stream.imu_snapshots,
|
imu_snapshots: Globals.data_stream.imu_snapshots,
|
||||||
show_banner: Globals.data_stream.show_banner,
|
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);
|
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._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._focused_monitor_distance_connection =
|
||||||
this._virtual_displays_actor.connect('notify::focused-monitor-details', this._update_display_distance.bind(this));
|
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));
|
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) {
|
_update_display_distance(object, event) {
|
||||||
const value = this.settings.get_double('display-distance');
|
const distance = this.settings.get_double('display-distance');
|
||||||
Globals.logger.log_debug(`BreezyDesktopExtension _update_display_distance ${value}`);
|
const size = this.settings.get_double('display-size');
|
||||||
if (value !== undefined) {
|
Globals.logger.log_debug(`BreezyDesktopExtension _update_display_distance ${distance} ${size}`);
|
||||||
let focusedMonitorSizeAdjustment = 1.0;
|
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) {
|
if (this._virtual_displays_actor?.focused_monitor_details && this._target_monitor) {
|
||||||
const fovMonitor = this._target_monitor.monitor;
|
const fovMonitor = this._target_monitor.monitor;
|
||||||
const focusedMonitor = this._virtual_displays_actor.focused_monitor_details;
|
const focusedMonitor = this._virtual_displays_actor.focused_monitor_details;
|
||||||
focusedMonitorSizeAdjustment =
|
focusedMonitorSizeAdjustment *=
|
||||||
Math.max(focusedMonitor.width / fovMonitor.width, focusedMonitor.height / fovMonitor.height);
|
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.settings.disconnect(this._distance_connection);
|
||||||
this._distance_connection = null;
|
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) {
|
if (this._focused_monitor_distance_connection) {
|
||||||
this._virtual_displays_actor.disconnect(this._focused_monitor_distance_connection);
|
this._virtual_displays_actor.disconnect(this._focused_monitor_distance_connection);
|
||||||
this._focused_monitor_distance_connection = null;
|
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
|
// FOV in radians is spherical, so doesn't follow Pythagoras' theorem
|
||||||
export function diagonalToCrossFOVs(diagonalFOVRadians, aspectRatio) {
|
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
|
// first convert from a spherical FOV to a diagonal FOV on a flat plane at a unit distance of 1.0
|
||||||
const flatDiagonalFOV = 2 * Math.tan(diagonalFOVRadians / 2);
|
const diagonalLengthUnitDistance = 2 * Math.tan(diagonalFOVRadians / 2);
|
||||||
|
|
||||||
// then convert to flat plane horizontal and vertical FOVs
|
// then convert to flat plane horizontal and vertical FOVs
|
||||||
const flatVerticalFOV = flatDiagonalFOV / Math.sqrt(1 + aspectRatio * aspectRatio);
|
const heightUnitDistance = diagonalLengthUnitDistance / Math.sqrt(1 + aspectRatio * aspectRatio);
|
||||||
const flatHorizontalFOV = flatVerticalFOV * aspectRatio;
|
const widthUnitDistance = heightUnitDistance * aspectRatio;
|
||||||
|
|
||||||
// then convert back to spherical FOV
|
|
||||||
return {
|
return {
|
||||||
diagonal: diagonalFOVRadians,
|
// then convert back to spherical FOV
|
||||||
horizontal: 2 * Math.atan(flatHorizontalFOV / 2),
|
diagonalRadians: diagonalFOVRadians,
|
||||||
vertical: 2 * Math.atan(flatVerticalFOV / 2)
|
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)),
|
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,
|
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => Math.asin(toLength / 2 / screenEdgeDistance) * 2,
|
||||||
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => {
|
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
|
radiansToSegments: (screenRadians) => 1
|
||||||
},
|
},
|
||||||
|
|
@ -42,6 +50,7 @@ export const fovConversionFns = {
|
||||||
fovEdgeToScreenCenterDistance: (edgeDistance, screenLength) => edgeDistance,
|
fovEdgeToScreenCenterDistance: (edgeDistance, screenLength) => edgeDistance,
|
||||||
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => fovRadians / fovLength * toLength,
|
lengthToRadians: (fovRadians, fovLength, screenEdgeDistance, toLength) => fovRadians / fovLength * toLength,
|
||||||
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => fovLength / fovRadians * Math.atan2(toAngleOpposite, toAngleAdjacent),
|
angleToLength: (fovRadians, fovLength, screenDistance, toAngleOpposite, toAngleAdjacent) => fovLength / fovRadians * Math.atan2(toAngleOpposite, toAngleAdjacent),
|
||||||
|
fovRadiansAtDistance: (fovRadians, unitLength, newScreenDistance) => fovRadians / newScreenDistance,
|
||||||
radiansToSegments: (screenRadians) => Math.ceil(screenRadians * segmentsPerRadian)
|
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) => {
|
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];
|
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
|
// Create a mesh of vertices in a pattern suitable for TRIANGLE_STRIP
|
||||||
function createVertexMesh(fovDetails, monitorDetails, positionVectorNWU) {
|
function createVertexMesh(fovDetails, monitorDetails, positionVectorNWU) {
|
||||||
let fovConversions = fovDetails.curvedDisplay ? fovConversionFns.curved : fovConversionFns.flat;
|
let horizontalWrap = fovDetails.monitorWrappingScheme === 'horizontal';
|
||||||
const sideEdgeDistancePixels = fovConversions.centerToFovEdgeDistance(
|
const horizontalConversions = fovDetails.curvedDisplay && horizontalWrap ? fovConversionFns.curved : fovConversionFns.flat;
|
||||||
|
const sideEdgeDistancePixels = horizontalConversions.centerToFovEdgeDistance(
|
||||||
fovDetails.completeScreenDistancePixels,
|
fovDetails.completeScreenDistancePixels,
|
||||||
fovDetails.widthPixels
|
fovDetails.sizeAdjustedWidthPixels
|
||||||
);
|
);
|
||||||
const horizontalRadians = fovConversions.lengthToRadians(
|
const horizontalRadians = horizontalConversions.lengthToRadians(
|
||||||
fovDetails.defaultDistanceHorizontalRadians,
|
fovDetails.defaultDistanceHorizontalRadians,
|
||||||
fovDetails.widthPixels,
|
fovDetails.widthPixels,
|
||||||
sideEdgeDistancePixels,
|
sideEdgeDistancePixels,
|
||||||
monitorDetails.width
|
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.completeScreenDistancePixels,
|
||||||
fovDetails.heightPixels
|
fovDetails.sizeAdjustedHeightPixels
|
||||||
);
|
);
|
||||||
const verticalRadians = fovConversions.lengthToRadians(
|
const verticalRadians = verticalConversions.lengthToRadians(
|
||||||
fovDetails.defaultDistanceVerticalRadians,
|
fovDetails.defaultDistanceVerticalRadians,
|
||||||
fovDetails.heightPixels,
|
fovDetails.heightPixels,
|
||||||
topEdgeDistancePixels,
|
topEdgeDistancePixels,
|
||||||
monitorDetails.height
|
monitorDetails.height
|
||||||
);
|
);
|
||||||
|
|
||||||
let horizontalWrap = fovDetails.monitorWrappingScheme === 'horizontal';
|
const xSegments = horizontalConversions.radiansToSegments(horizontalRadians);
|
||||||
let verticalWrap = fovDetails.monitorWrappingScheme === 'vertical';
|
const ySegments = verticalConversions.radiansToSegments(verticalRadians);
|
||||||
const xSegments = horizontalWrap ? fovConversions.radiansToSegments(horizontalRadians) : 1;
|
|
||||||
const ySegments = verticalWrap ? fovConversions.radiansToSegments(verticalRadians) : 1;
|
|
||||||
|
|
||||||
const texXLeft = 0;
|
const texXLeft = 0;
|
||||||
const texYTop = 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',
|
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
|
||||||
GObject.ParamFlags.READWRITE
|
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': GObject.ParamSpec.boolean(
|
||||||
'smooth-follow-enabled',
|
'smooth-follow-enabled',
|
||||||
'Smooth follow enabled',
|
'Smooth follow enabled',
|
||||||
|
|
@ -176,12 +184,21 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
true
|
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': GObject.ParamSpec.double(
|
||||||
'display-distance',
|
'display-distance',
|
||||||
'Display Distance',
|
'Display Distance',
|
||||||
'Distance of the display from the camera',
|
'Distance of the display from the camera',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
0.0,
|
0.1,
|
||||||
2.5,
|
2.5,
|
||||||
1.0
|
1.0
|
||||||
),
|
),
|
||||||
|
|
@ -190,7 +207,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
'Display distance default',
|
'Display distance default',
|
||||||
'Distance to use when not explicitly set, or when reset',
|
'Distance to use when not explicitly set, or when reset',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
0.2,
|
0.1,
|
||||||
2.5,
|
2.5,
|
||||||
1.0
|
1.0
|
||||||
),
|
),
|
||||||
|
|
@ -253,6 +270,8 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
this._use_smooth_follow_origin = false;
|
this._use_smooth_follow_origin = false;
|
||||||
|
|
||||||
this.connect('notify::display-distance', this._update_display_distance.bind(this));
|
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::focused-monitor-index', this._update_display_distance.bind(this));
|
||||||
this.connect('notify::monitor-placements', this._update_display_position.bind(this));
|
this.connect('notify::monitor-placements', this._update_display_position.bind(this));
|
||||||
this.connect('notify::show-banner', this._handle_banner_update.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 = noRotationVector.map(coord => coord * inverse_follow_ease);
|
||||||
finalPositionVector[0] = noRotationVector[0];
|
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;
|
const rotation_radians = this.monitor_placements[this.monitor_index].rotationAngleRadians;
|
||||||
if (this._initialized) {
|
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]);
|
this.set_uniform_float(this.get_uniform_location("u_show_banner"), 1, [this.show_banner ? 1.0 : 0.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
perspective(fovHorizontalRadians, aspect, near, far) {
|
perspective(widthUnitDistance, aspect, near, far) {
|
||||||
const f = 1.0 / Math.tan(fovHorizontalRadians / 2.0);
|
const f = 2.0 / widthUnitDistance;
|
||||||
const range = far - near;
|
const range = far - near;
|
||||||
|
|
||||||
return [
|
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);
|
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);
|
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);
|
return vec3(-v.y, v.z, -v.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,15 +515,15 @@ 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]));
|
vec4 quat_t0 = nwuToEUS(quatConjugate(u_pose_orientation[0]));
|
||||||
vec3 position_vector = applyQuaternionToVector(nwuToESU(u_pose_position), quat_t0);
|
vec3 position_vector = applyQuaternionToVector(nwuToEUS(u_pose_position), quat_t0);
|
||||||
vec3 final_lens_position = u_lens_vector + position_vector;
|
vec3 final_lens_position = nwuToEUS(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);
|
||||||
|
|
||||||
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_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];
|
float delta_time_t0 = u_pose_orientation[3][0] - u_pose_orientation[3][1];
|
||||||
|
|
||||||
// how quickly the vertex is moving relative to the camera
|
// how quickly the vertex is moving relative to the camera
|
||||||
|
|
@ -545,20 +568,20 @@ export const VirtualDisplayEffect = GObject.registerClass({
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
|
||||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
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(
|
const projection_matrix = this.perspective(
|
||||||
fovRadians.horizontal,
|
fovLengths.widthUnitDistance,
|
||||||
aspect,
|
aspect,
|
||||||
1.0,
|
1.0,
|
||||||
10000.0
|
10000.0
|
||||||
);
|
);
|
||||||
this.set_uniform_matrix(this.get_uniform_location("u_projection_matrix"), false, 4, projection_matrix);
|
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_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_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_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_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._update_display_position();
|
||||||
this._handle_banner_update();
|
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]);
|
this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [0.0]);
|
||||||
lookAheadSet = true;
|
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_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);
|
this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, posePositionPixels);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import Shell from 'gi://Shell';
|
||||||
import St from 'gi://St';
|
import St from 'gi://St';
|
||||||
|
|
||||||
import { VirtualDisplayEffect, SMOOTH_FOLLOW_SLERP_TIMELINE_MS } from './virtualdisplayeffect.js';
|
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';
|
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
|
// if we leave the monitor with some margin, unfocus even if no other monitor is in focus
|
||||||
const UNFOCUS_THRESHOLD = 1.1 / 2.0;
|
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) {
|
function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVector, monitorDetails, upAngleToLength, westAngleToLength) {
|
||||||
const monitorAspectRatio = monitorDetails.width / monitorDetails.height;
|
// 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;
|
||||||
|
|
||||||
// weight the up distance by the aspect ratio
|
|
||||||
const vectorUpPixels = upAngleToLength(
|
const vectorUpPixels = upAngleToLength(
|
||||||
fovDetails.defaultDistanceVerticalRadians,
|
fovDetails.defaultDistanceVerticalRadians,
|
||||||
fovDetails.heightPixels,
|
fovDetails.heightPixels,
|
||||||
fovDetails.completeScreenDistancePixels,
|
monitorDistance,
|
||||||
monitorVector[2],
|
monitorVector[2],
|
||||||
monitorVector[0]
|
monitorVector[0]
|
||||||
);
|
) * distanceAdjustment;
|
||||||
const upDeltaPixels = (lookUpPixels - vectorUpPixels) * monitorAspectRatio;
|
const upPercentage = Math.abs(lookUpPixels * distanceAdjustment - vectorUpPixels) / monitorDetails.height;
|
||||||
|
|
||||||
const vectorWestPixels = westAngleToLength(
|
const vectorWestPixels = westAngleToLength(
|
||||||
fovDetails.defaultDistanceHorizontalRadians,
|
fovDetails.defaultDistanceHorizontalRadians,
|
||||||
fovDetails.widthPixels,
|
fovDetails.widthPixels,
|
||||||
fovDetails.completeScreenDistancePixels,
|
monitorDistance,
|
||||||
monitorVector[1],
|
monitorVector[1],
|
||||||
monitorVector[0]
|
monitorVector[0]
|
||||||
);
|
) * distanceAdjustment;
|
||||||
const westDeltaPixels = lookWestPixels - vectorWestPixels;
|
const westPercentage = Math.abs(lookWestPixels * distanceAdjustment - vectorWestPixels) / monitorDetails.width;
|
||||||
const totalDeltaPixels = Math.sqrt(upDeltaPixels * upDeltaPixels + westDeltaPixels * westDeltaPixels);
|
|
||||||
|
|
||||||
// threshold is a percentage of width, and height was already properly weighted
|
// how close we are to any edge is the largest of the two percentages
|
||||||
return totalDeltaPixels / monitorDetails.width;
|
return Math.max(upPercentage, westPercentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the vector in the array that's closest to the quaternion rotation
|
* Find the vector in the array that's closest to the quaternion rotation
|
||||||
*
|
*
|
||||||
* @param {number[]} quaternion - Reference quaternion [x, y, z, w]
|
* @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[][]} monitorVectors - Array of monitor vectors [x, y, z] to search from
|
||||||
* @param {number} currentFocusedIndex - Index of the currently focused monitor
|
* @param {number} currentFocusedIndex - Index of the currently focused monitor
|
||||||
* @param {number} focusedMonitorDistance - Distance to the focused monitor, < 1.0 if zoomed in
|
* @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
|
* @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
|
* @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 lookVector = [1.0, 0.0, 0.0]; // NWU vector pointing to the center of the screen
|
||||||
const rotatedLookVector = applyQuaternionToVector(lookVector, quaternion);
|
const rotatedLookVector = applyQuaternionToVector(lookVector, quaternion);
|
||||||
|
|
||||||
|
|
@ -82,8 +86,13 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
||||||
rotatedLookVector[0]
|
rotatedLookVector[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
let closestIndex = -1;
|
function vectorRelativeToLensPosition(vector) {
|
||||||
let closestDistance = Infinity;
|
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
|
// the currently focused monitor is the most likely to be the closest, check it first and exit early if it is
|
||||||
if (currentFocusedIndex !== -1) {
|
if (currentFocusedIndex !== -1) {
|
||||||
|
|
@ -91,15 +100,18 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
||||||
fovDetails,
|
fovDetails,
|
||||||
lookUpPixels,
|
lookUpPixels,
|
||||||
lookWestPixels,
|
lookWestPixels,
|
||||||
monitorVectors[currentFocusedIndex],
|
vectorRelativeToLensPosition(monitorVectors[currentFocusedIndex]),
|
||||||
monitorsDetails[currentFocusedIndex],
|
monitorsDetails[currentFocusedIndex],
|
||||||
upConversionFns.angleToLength,
|
upConversionFns.angleToLength,
|
||||||
westConversionFns.angleToLength
|
westConversionFns.angleToLength
|
||||||
) * focusedMonitorDistance;
|
) * 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
|
// find the vector closest to the rotated look vector
|
||||||
monitorVectors.forEach((monitorVector, index) => {
|
monitorVectors.forEach((monitorVector, index) => {
|
||||||
if (index === currentFocusedIndex) return;
|
if (index === currentFocusedIndex) return;
|
||||||
|
|
@ -108,7 +120,7 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
|
||||||
fovDetails,
|
fovDetails,
|
||||||
lookUpPixels,
|
lookUpPixels,
|
||||||
lookWestPixels,
|
lookWestPixels,
|
||||||
monitorVector,
|
vectorRelativeToLensPosition(monitorVector),
|
||||||
monitorsDetails[index],
|
monitorsDetails[index],
|
||||||
upConversionFns.angleToLength,
|
upConversionFns.angleToLength,
|
||||||
westConversionFns.angleToLength
|
westConversionFns.angleToLength
|
||||||
|
|
@ -208,8 +220,10 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
if (fovDetails.monitorWrappingScheme === 'horizontal') {
|
if (fovDetails.monitorWrappingScheme === 'horizontal') {
|
||||||
// monitors wrap around us horizontally
|
// monitors wrap around us horizontally
|
||||||
|
|
||||||
const sideEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.widthPixels);
|
const sideEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.sizeAdjustedWidthPixels);
|
||||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
const monitorSpacingPixels = monitorSpacing * fovDetails.sizeAdjustedWidthPixels;
|
||||||
|
|
||||||
|
// targetWidth is assumed to aleady be size adjusted
|
||||||
const lengthToRadianFn = (targetWidth) => conversionFns.lengthToRadians(
|
const lengthToRadianFn = (targetWidth) => conversionFns.lengthToRadians(
|
||||||
fovDetails.defaultDistanceHorizontalRadians,
|
fovDetails.defaultDistanceHorizontalRadians,
|
||||||
fovDetails.widthPixels,
|
fovDetails.widthPixels,
|
||||||
|
|
@ -217,14 +231,14 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
targetWidth
|
targetWidth
|
||||||
);
|
);
|
||||||
|
|
||||||
cachedMonitorRadians[0] = -fovDetails.defaultDistanceHorizontalRadians / 2;
|
cachedMonitorRadians[0] = -lengthToRadianFn(fovDetails.sizeAdjustedWidthPixels) / 2;
|
||||||
horizontalMonitorSort(monitorDetailsList).forEach(({monitorDetails, originalIndex}) => {
|
horizontalMonitorSort(monitorDetailsList).forEach(({monitorDetails, originalIndex}) => {
|
||||||
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, monitorSpacingPixels, monitorDetails.x, monitorDetails.width, lengthToRadianFn);
|
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, monitorSpacingPixels, monitorDetails.x, monitorDetails.width, lengthToRadianFn);
|
||||||
const monitorCenterRadius = conversionFns.fovEdgeToScreenCenterDistance(sideEdgeRadius, monitorDetails.width);
|
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
|
// 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)
|
// this is where our monitor's center is in relation to an fov-sized viewport centered about (0, 0)
|
||||||
const upCenterPixels = upTopPixels - upCenterOffsetPixels;
|
const upCenterPixels = upTopPixels - upCenterOffsetPixels;
|
||||||
|
|
@ -240,7 +254,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
// up is flat when wrapping horizontally
|
// up is flat when wrapping horizontally
|
||||||
upCenterPixels
|
upCenterPixels
|
||||||
],
|
],
|
||||||
centerLook: normalizeVector([
|
centerLook: [
|
||||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
||||||
|
|
||||||
|
|
@ -249,7 +263,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
|
|
||||||
// up is flat when wrapping horizontally
|
// up is flat when wrapping horizontally
|
||||||
upCenterPixels
|
upCenterPixels
|
||||||
]),
|
],
|
||||||
rotationAngleRadians: {
|
rotationAngleRadians: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: -monitorWrapDetails.center
|
y: -monitorWrapDetails.center
|
||||||
|
|
@ -259,8 +273,10 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
} else if (fovDetails.monitorWrappingScheme === 'vertical') {
|
} else if (fovDetails.monitorWrappingScheme === 'vertical') {
|
||||||
// monitors wrap around us vertically
|
// monitors wrap around us vertically
|
||||||
|
|
||||||
const topEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.heightPixels);
|
const topEdgeRadius = conversionFns.centerToFovEdgeDistance(fovDetails.completeScreenDistancePixels, fovDetails.sizeAdjustedHeightPixels);
|
||||||
const monitorSpacingPixels = monitorSpacing * fovDetails.heightPixels;
|
const monitorSpacingPixels = monitorSpacing * fovDetails.sizeAdjustedHeightPixels;
|
||||||
|
|
||||||
|
// targetHeight is assumed to aleady be size adjusted
|
||||||
const lengthToRadianFn = (targetHeight) => conversionFns.lengthToRadians(
|
const lengthToRadianFn = (targetHeight) => conversionFns.lengthToRadians(
|
||||||
fovDetails.defaultDistanceVerticalRadians,
|
fovDetails.defaultDistanceVerticalRadians,
|
||||||
fovDetails.heightPixels,
|
fovDetails.heightPixels,
|
||||||
|
|
@ -268,14 +284,14 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
targetHeight
|
targetHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
cachedMonitorRadians[0] = -fovDetails.defaultDistanceVerticalRadians / 2;
|
cachedMonitorRadians[0] = -lengthToRadianFn(fovDetails.sizeAdjustedHeightPixels) / 2;
|
||||||
verticalMonitorSort(monitorDetailsList).forEach(({monitorDetails, originalIndex}) => {
|
verticalMonitorSort(monitorDetailsList).forEach(({monitorDetails, originalIndex}) => {
|
||||||
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, monitorSpacingPixels, monitorDetails.y, monitorDetails.height, lengthToRadianFn);
|
const monitorWrapDetails = monitorWrap(cachedMonitorRadians, monitorSpacingPixels, monitorDetails.y, monitorDetails.height, lengthToRadianFn);
|
||||||
const monitorCenterRadius = conversionFns.fovEdgeToScreenCenterDistance(topEdgeRadius, monitorDetails.height);
|
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
|
// 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)
|
// this is where our monitor's center is in relation to an fov-sized viewport centered about (0, 0)
|
||||||
const westCenterPixels = westLeftPixels - westCenterOffsetPixels;
|
const westCenterPixels = westLeftPixels - westCenterOffsetPixels;
|
||||||
|
|
@ -291,7 +307,7 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
// up is centered about the FOV center
|
// up is centered about the FOV center
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
centerLook: normalizeVector([
|
centerLook: [
|
||||||
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
// north is adjacent where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
monitorCenterRadius * Math.cos(monitorWrapDetails.center),
|
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
|
// up is opposite where radius is the hypotenuse, using monitorWrapDetails.center as the radians
|
||||||
-monitorCenterRadius * Math.sin(monitorWrapDetails.center)
|
-monitorCenterRadius * Math.sin(monitorWrapDetails.center)
|
||||||
]),
|
],
|
||||||
rotationAngleRadians: {
|
rotationAngleRadians: {
|
||||||
x: -monitorWrapDetails.center,
|
x: -monitorWrapDetails.center,
|
||||||
y: 0
|
y: 0
|
||||||
|
|
@ -308,17 +324,16 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const monitorSpacingPixels = monitorSpacing * fovDetails.widthPixels;
|
const monitorSpacingPixels = monitorSpacing * fovDetails.sizeAdjustedWidthPixels;
|
||||||
|
|
||||||
// monitors make a flat wall in front of us, no wrapping
|
// monitors make a flat wall in front of us, no wrapping
|
||||||
monitorDetailsList.forEach((monitorDetails, index) => {
|
monitorDetailsList.forEach((monitorDetails, index) => {
|
||||||
const upTopPixels = -monitorDetails.y - (monitorDetails.y / fovDetails.heightPixels) * monitorSpacingPixels;
|
const upTopPixels = -monitorDetails.y - (monitorDetails.y / fovDetails.sizeAdjustedHeightPixels) * monitorSpacingPixels;
|
||||||
const westLeftPixels = -monitorDetails.x - (monitorDetails.x / fovDetails.widthPixels) * monitorSpacingPixels;
|
const westLeftPixels = -monitorDetails.x - (monitorDetails.x / fovDetails.sizeAdjustedWidthPixels) * monitorSpacingPixels;
|
||||||
|
|
||||||
// offsets for aligning this monitor's center with the fov-sized viewport's center
|
// offsets 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;
|
||||||
const upCenterOffsetPixels = (monitorDetails.height - fovDetails.heightPixels) / 2;
|
const upCenterOffsetPixels = (monitorDetails.height - fovDetails.sizeAdjustedHeightPixels) / 2;
|
||||||
|
|
||||||
const westCenterPixels = westLeftPixels - westCenterOffsetPixels;
|
const westCenterPixels = westLeftPixels - westCenterOffsetPixels;
|
||||||
const upCenterPixels = upTopPixels - upCenterOffsetPixels;
|
const upCenterPixels = upTopPixels - upCenterOffsetPixels;
|
||||||
|
|
||||||
|
|
@ -329,11 +344,11 @@ function monitorsToPlacements(fovDetails, monitorDetailsList, monitorSpacing) {
|
||||||
westCenterPixels,
|
westCenterPixels,
|
||||||
upCenterPixels
|
upCenterPixels
|
||||||
],
|
],
|
||||||
centerLook: normalizeVector([
|
centerLook: [
|
||||||
fovDetails.completeScreenDistancePixels,
|
fovDetails.completeScreenDistancePixels,
|
||||||
westCenterPixels,
|
westCenterPixels,
|
||||||
upCenterPixels
|
upCenterPixels
|
||||||
]),
|
],
|
||||||
rotationAngleRadians: {
|
rotationAngleRadians: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0
|
y: 0
|
||||||
|
|
@ -446,6 +461,13 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
|
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
|
||||||
GObject.ParamFlags.READWRITE
|
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': GObject.ParamSpec.boolean(
|
||||||
'curved-display',
|
'curved-display',
|
||||||
'Curved Display',
|
'Curved Display',
|
||||||
|
|
@ -499,7 +521,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
'Display size',
|
'Display size',
|
||||||
'Size of the display',
|
'Size of the display',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
0.2,
|
0.1,
|
||||||
2.5,
|
2.5,
|
||||||
1.0
|
1.0
|
||||||
),
|
),
|
||||||
|
|
@ -515,7 +537,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
'Display Distance',
|
'Display Distance',
|
||||||
'Distance of the display from the camera',
|
'Distance of the display from the camera',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
0.2,
|
0.1,
|
||||||
2.5,
|
2.5,
|
||||||
1.05
|
1.05
|
||||||
),
|
),
|
||||||
|
|
@ -537,7 +559,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
'Display distance start',
|
'Display distance start',
|
||||||
'Start distance when using the "change distance" shortcut.',
|
'Start distance when using the "change distance" shortcut.',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
0.2,
|
0.1,
|
||||||
2.5,
|
2.5,
|
||||||
1.05
|
1.05
|
||||||
),
|
),
|
||||||
|
|
@ -546,7 +568,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
'Display distance end',
|
'Display distance end',
|
||||||
'End distance when using the "change distance" shortcut.',
|
'End distance when using the "change distance" shortcut.',
|
||||||
GObject.ParamFlags.READWRITE,
|
GObject.ParamFlags.READWRITE,
|
||||||
0.2,
|
0.1,
|
||||||
2.5,
|
2.5,
|
||||||
1.05
|
1.05
|
||||||
),
|
),
|
||||||
|
|
@ -578,7 +600,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
constructor(params = {}) {
|
constructor(params = {}) {
|
||||||
super(params);
|
super(params);
|
||||||
|
|
||||||
this._all_monitors = [
|
this._all_monitors_unmodified = [
|
||||||
this.target_monitor,
|
this.target_monitor,
|
||||||
...this.virtual_monitors
|
...this.virtual_monitors
|
||||||
];
|
];
|
||||||
|
|
@ -649,10 +671,10 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
this._property_connections.push(this.connect(`notify::${property}`, fn.bind(this)));
|
this._property_connections.push(this.connect(`notify::${property}`, fn.bind(this)));
|
||||||
}).bind(this);
|
}).bind(this);
|
||||||
|
|
||||||
this._distance_ease_timeline = null;
|
notifyToFunction('toggle-display-distance-start', this._handle_display_size_distance_change);
|
||||||
notifyToFunction('toggle-display-distance-start', this._handle_display_distance_properties_change);
|
notifyToFunction('toggle-display-distance-end', this._handle_display_size_distance_change);
|
||||||
notifyToFunction('toggle-display-distance-end', this._handle_display_distance_properties_change);
|
notifyToFunction('display-distance', this._handle_display_size_distance_change);
|
||||||
notifyToFunction('display-distance', this._handle_display_distance_properties_change);
|
notifyToFunction('display-size', this._handle_display_size_distance_change);
|
||||||
notifyToFunction('monitor-wrapping-scheme', this._update_monitor_placements);
|
notifyToFunction('monitor-wrapping-scheme', this._update_monitor_placements);
|
||||||
notifyToFunction('monitor-spacing', this._update_monitor_placements);
|
notifyToFunction('monitor-spacing', this._update_monitor_placements);
|
||||||
notifyToFunction('headset-display-as-viewport-center', 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('custom-banner-enabled', this._handle_banner_update);
|
||||||
notifyToFunction('framerate-cap', this._handle_frame_rate_cap_change);
|
notifyToFunction('framerate-cap', this._handle_frame_rate_cap_change);
|
||||||
notifyToFunction('smooth-follow-enabled', this._handle_smooth_follow_enabled_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();
|
this._handle_frame_rate_cap_change();
|
||||||
|
|
||||||
const actorToDisplayRatios = [
|
const actorToDisplayRatios = [
|
||||||
|
|
@ -681,7 +703,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
|
|
||||||
Globals.logger.log_debug(`\t\t\tActor to display ratios: ${actorToDisplayRatios}, offsets: ${actorToDisplayOffsets}`);
|
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}`);
|
Globals.logger.log_debug(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);
|
||||||
|
|
||||||
const containerActor = new Clutter.Actor({
|
const containerActor = new Clutter.Actor({
|
||||||
|
|
@ -706,11 +728,13 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
const effect = new VirtualDisplayEffect({
|
const effect = new VirtualDisplayEffect({
|
||||||
focused_monitor_index: this.focused_monitor_index,
|
focused_monitor_index: this.focused_monitor_index,
|
||||||
imu_snapshots: this.imu_snapshots,
|
imu_snapshots: this.imu_snapshots,
|
||||||
|
pose_has_position: this.pose_has_position,
|
||||||
monitor_index: index,
|
monitor_index: index,
|
||||||
monitor_details: monitor,
|
monitor_details: monitor,
|
||||||
monitor_placements: this.monitor_placements,
|
monitor_placements: this.monitor_placements,
|
||||||
fov_details: this.fov_details,
|
fov_details: this.fov_details,
|
||||||
target_monitor: this.target_monitor,
|
target_monitor: this.target_monitor,
|
||||||
|
display_size: this.display_size,
|
||||||
display_distance: this.display_distance,
|
display_distance: this.display_distance,
|
||||||
display_distance_default: this._display_distance_default(),
|
display_distance_default: this._display_distance_default(),
|
||||||
actor_to_display_ratios: actorToDisplayRatios,
|
actor_to_display_ratios: actorToDisplayRatios,
|
||||||
|
|
@ -735,8 +759,10 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
|
|
||||||
[
|
[
|
||||||
'monitor-placements',
|
'monitor-placements',
|
||||||
|
'display-size',
|
||||||
'fov-details',
|
'fov-details',
|
||||||
'imu-snapshots',
|
'imu-snapshots',
|
||||||
|
'pose-has-position',
|
||||||
'smooth-follow-enabled',
|
'smooth-follow-enabled',
|
||||||
'smooth-follow-toggle-epoch-ms',
|
'smooth-follow-toggle-epoch-ms',
|
||||||
'focused-monitor-index',
|
'focused-monitor-index',
|
||||||
|
|
@ -789,8 +815,13 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
this.imu_snapshots.smooth_follow_origin.splice(0, 4) :
|
this.imu_snapshots.smooth_follow_origin.splice(0, 4) :
|
||||||
this.imu_snapshots.pose_orientation.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(
|
const focusedMonitorIndex = findFocusedMonitor(
|
||||||
currentOrientationQuat,
|
currentOrientationQuat,
|
||||||
|
currentPosition,
|
||||||
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(),
|
||||||
|
|
@ -827,34 +858,61 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
this._redraw_timeline.start();
|
this._redraw_timeline.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_size_adjusted_target_monitor() {
|
||||||
|
return this._all_monitors[0];
|
||||||
|
}
|
||||||
|
|
||||||
_display_distance_default() {
|
_display_distance_default() {
|
||||||
return Math.max(this.display_distance, this.toggle_display_distance_start, this.toggle_display_distance_end);
|
return Math.max(this.display_distance, this.toggle_display_distance_start, this.toggle_display_distance_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
_fov_details() {
|
_fov_details() {
|
||||||
const aspect = this.target_monitor.width / this.target_monitor.height;
|
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;
|
||||||
|
|
||||||
// adjusted angles based on how far away the screens are e.g. a closer screen takes up a larger slice of our FOV
|
// adjust FOV to a new focal point distance while keeping screens the same size
|
||||||
const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / this._display_distance_default());
|
// i.e. focus from pivot point to new screen distance, adjusted from lens at unit distance
|
||||||
const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / this._display_distance_default());
|
const defaultDistanceVerticalRadians = verticalConversions.fovRadiansAtDistance(
|
||||||
|
fovLengths.verticalRadians,
|
||||||
|
fovLengths.heightUnitDistance,
|
||||||
|
defaultDistance
|
||||||
|
);
|
||||||
|
const defaultDistanceHorizontalRadians = horizontalConversions.fovRadiansAtDistance(
|
||||||
|
fovLengths.horizontalRadians,
|
||||||
|
fovLengths.widthUnitDistance,
|
||||||
|
defaultDistance
|
||||||
|
);
|
||||||
|
|
||||||
// distance needed for the FOV-sized monitor to fill up the screen
|
// distance needed for the FOV-sized monitor to fill up the screen, as measured from the lenses
|
||||||
const fullScreenDistance = this.target_monitor.height / 2 / Math.tan(fovRadians.vertical / 2);
|
const lensToUnitDistancePixels = this.target_monitor.width / fovLengths.widthUnitDistance;
|
||||||
const lensDistancePixels = fullScreenDistance / (1.0 - Globals.data_stream.device_data.lensDistanceRatio) - fullScreenDistance;
|
|
||||||
|
|
||||||
// distance of a display at the default (most zoomed out) distance, plus the lens distance constant
|
// distance from pivot point to lens
|
||||||
const lensToScreenDistance = this.target_monitor.height / 2 / Math.tan(defaultDistanceVerticalRadians / 2);
|
const lensDistancePixels = lensToUnitDistancePixels * lensDistanceFactor;
|
||||||
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 {
|
return {
|
||||||
widthPixels: this.target_monitor.width,
|
widthPixels: this.target_monitor.width,
|
||||||
|
distanceAdjustedSize: this._distance_adjusted_size,
|
||||||
|
sizeAdjustedWidthPixels: this.target_monitor.width * this._distance_adjusted_size,
|
||||||
heightPixels: this.target_monitor.height,
|
heightPixels: this.target_monitor.height,
|
||||||
|
sizeAdjustedHeightPixels: this.target_monitor.height * this._distance_adjusted_size,
|
||||||
defaultDistanceVerticalRadians,
|
defaultDistanceVerticalRadians,
|
||||||
defaultDistanceHorizontalRadians,
|
defaultDistanceHorizontalRadians,
|
||||||
lensDistancePixels,
|
lensDistancePixels,
|
||||||
|
fullScreenDistancePixels,
|
||||||
completeScreenDistancePixels,
|
completeScreenDistancePixels,
|
||||||
monitorWrappingScheme: this._actual_wrap_scheme(),
|
monitorWrappingScheme,
|
||||||
curvedDisplay: this.curved_display
|
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 minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
|
||||||
const maxY = Math.max(...this._all_monitors.map(monitor => monitor.y + monitor.height));
|
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';
|
return 'horizontal';
|
||||||
} else {
|
} else {
|
||||||
return 'vertical';
|
return 'vertical';
|
||||||
|
|
@ -878,27 +937,29 @@ export const VirtualDisplaysActor = GObject.registerClass({
|
||||||
|
|
||||||
_update_monitor_placements() {
|
_update_monitor_placements() {
|
||||||
try {
|
try {
|
||||||
|
const targetMonitor = this._size_adjusted_target_monitor();
|
||||||
|
|
||||||
const minX = Math.min(...this._all_monitors.map(monitor => monitor.x));
|
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 maxX = Math.max(...this._all_monitors.map(monitor => monitor.x + monitor.width));
|
||||||
const minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
|
const minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
|
||||||
const maxY = Math.max(...this._all_monitors.map(monitor => monitor.y + monitor.height));
|
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
|
// the beginning edges of the viewport if it's centered on all displays
|
||||||
const allDisplaysCenterXBegin = (minX + maxX) / 2 - this.target_monitor.width / 2;
|
const allDisplaysCenterXBegin = (minX + maxX) / 2 - targetMonitor.width / 2;
|
||||||
const allDisplaysCenterYBegin = (minY + maxY) / 2 - this.target_monitor.height / 2;
|
const allDisplaysCenterYBegin = (minY + maxY) / 2 - targetMonitor.height / 2;
|
||||||
|
|
||||||
const viewportXBegin = this.headset_display_as_viewport_center ? this.target_monitor.x : allDisplaysCenterXBegin;
|
const viewportXBegin = this.headset_display_as_viewport_center ? targetMonitor.x : allDisplaysCenterXBegin;
|
||||||
const viewportYBegin = this.headset_display_as_viewport_center ? this.target_monitor.y : allDisplaysCenterYBegin;
|
const viewportYBegin = this.headset_display_as_viewport_center ? targetMonitor.y : allDisplaysCenterYBegin;
|
||||||
|
|
||||||
this.fov_details = this._fov_details();
|
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.monitor_placements = monitorsToPlacements(
|
||||||
this.fov_details,
|
this.fov_details,
|
||||||
|
|
||||||
// shift all monitors so they center around the viewport center, then adjusted by the offsets
|
// shift all monitors so they center around the viewport center, then adjusted by the offsets
|
||||||
this._all_monitors.map(monitor => ({
|
this._all_monitors.map(monitor => ({
|
||||||
x: monitor.x - viewportXBegin - this.viewport_offset_x * this.target_monitor.width,
|
x: monitor.x - viewportXBegin - this.viewport_offset_x * targetMonitor.width,
|
||||||
y: monitor.y - viewportYBegin + this.viewport_offset_y * this.target_monitor.height,
|
y: monitor.y - viewportYBegin + this.viewport_offset_y * targetMonitor.height,
|
||||||
width: monitor.width,
|
width: monitor.width,
|
||||||
height: monitor.height
|
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_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);
|
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;
|
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();
|
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
|
The size of the display
|
||||||
</description>
|
</description>
|
||||||
</key>
|
</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">
|
<key name="viewport-offset-x" type="d">
|
||||||
<default>
|
<default>
|
||||||
0.0
|
0.0
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit da173bd9e0392aaeb2cb68a332e5d4a20dd4dae1
|
Subproject commit 1655c2bb03a4e75bf2c9a9815a96b71342396fbd
|
||||||
|
|
@ -33,6 +33,8 @@ class ConnectedDevice(Gtk.Box):
|
||||||
effect_enable_switch = Gtk.Template.Child()
|
effect_enable_switch = Gtk.Template.Child()
|
||||||
disable_physical_displays_switch = Gtk.Template.Child()
|
disable_physical_displays_switch = Gtk.Template.Child()
|
||||||
display_zoom_on_focus_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_scale = Gtk.Template.Child()
|
||||||
follow_threshold_adjustment = Gtk.Template.Child()
|
follow_threshold_adjustment = Gtk.Template.Child()
|
||||||
follow_mode_switch = 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_x_adjustment = Gtk.Template.Child()
|
||||||
viewport_offset_y_scale = Gtk.Template.Child()
|
viewport_offset_y_scale = Gtk.Template.Child()
|
||||||
viewport_offset_y_adjustment = Gtk.Template.Child()
|
viewport_offset_y_adjustment = Gtk.Template.Child()
|
||||||
|
units_menu = Gtk.Template.Child()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Gtk.Box, self).__init__()
|
super(Gtk.Box, self).__init__()
|
||||||
|
|
@ -90,7 +93,7 @@ class ConnectedDevice(Gtk.Box):
|
||||||
self.active = True
|
self.active = True
|
||||||
self.all_enabled_state_inputs = [
|
self.all_enabled_state_inputs = [
|
||||||
self.display_zoom_on_focus_switch,
|
self.display_zoom_on_focus_switch,
|
||||||
# self.display_size_scale,
|
self.display_size_scale,
|
||||||
self.follow_mode_switch,
|
self.follow_mode_switch,
|
||||||
self.follow_threshold_scale,
|
self.follow_threshold_scale,
|
||||||
self.curved_display_switch,
|
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.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.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('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('widescreen-mode', self.widescreen_mode_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
|
||||||
self.settings.bind('curved-display', self.curved_display_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.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'))
|
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(), [
|
bind_shortcut_settings(self.get_parent(), [
|
||||||
[self.reassign_toggle_xr_effect_shortcut_button, self.toggle_xr_effect_shortcut_label],
|
[self.reassign_toggle_xr_effect_shortcut_button, self.toggle_xr_effect_shortcut_label],
|
||||||
[self.reassign_recenter_display_shortcut_button, self.recenter_display_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.follow_mode_switch.connect('notify::active', self._refresh_follow_mode)
|
||||||
self.effect_enable_switch.connect('notify::active', self._handle_switch_enabled_state)
|
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 = ConfigManager.get_instance()
|
||||||
self.config_manager.connect('notify::breezy-desktop-enabled', self._handle_enabled_config)
|
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')
|
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:
|
elif not widget.get_active() and is_zoom_on_focus_already_enabled:
|
||||||
self.settings.set_double('display-distance', toggle_display_distance_end)
|
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):
|
def _handle_monitor_wrapping_scheme_setting_changed(self, settings, val):
|
||||||
self.monitor_wrapping_scheme_menu.set_active_id(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)
|
self.display_zoom_on_focus_switch.set_active(should_zoom_on_focus_be_enabled)
|
||||||
|
|
||||||
def _set_focused_display_distance(self, distance):
|
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.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'))
|
self.display_zoom_on_focus_switch.set_sensitive(distance != self.settings.get_double('toggle-display-distance-end'))
|
||||||
|
|
||||||
def _set_all_displays_distance(self, distance):
|
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.settings.set_double('toggle-display-distance-end', distance)
|
||||||
self.display_zoom_on_focus_switch.set_active(False)
|
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'))
|
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):
|
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 = DisplayDistanceDialog(settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit)
|
||||||
dialog.set_transient_for(widget.get_ancestor(Gtk.Window))
|
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):
|
def _on_set_all_displays_distance(self, prev_distance, distance):
|
||||||
focused_display_distance = self.settings.get_double('toggle-display-distance-start')
|
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):
|
if (distance < focused_display_distance):
|
||||||
self._set_focused_display_distance(distance)
|
self._set_focused_display_distance(distance)
|
||||||
|
|
||||||
|
all_displays_distance = self.settings.get_double('toggle-display-distance-end')
|
||||||
self._set_all_displays_distance(distance)
|
self._set_all_displays_distance(distance)
|
||||||
|
|
||||||
if prev_distance == focused_display_distance:
|
# if we were at the unfocused distance, put us at the new unfocused distance
|
||||||
self.settings.set_double('display-distance', prev_distance)
|
if prev_distance == all_displays_distance:
|
||||||
|
self.settings.set_double('display-distance', distance)
|
||||||
|
|
||||||
def _on_set_focused_display_distance(self, prev_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')
|
all_displays_distance = self.settings.get_double('toggle-display-distance-end')
|
||||||
if (distance > all_displays_distance):
|
if (distance > all_displays_distance):
|
||||||
self._set_all_displays_distance(distance)
|
self._set_all_displays_distance(distance)
|
||||||
|
|
||||||
|
focused_display_distance = self.settings.get_double('toggle-display-distance-start')
|
||||||
self._set_focused_display_distance(distance)
|
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):
|
def _save_custom_resolutions(self):
|
||||||
with open(self._custom_resolutions_file_path, 'w') as f:
|
with open(self._custom_resolutions_file_path, 'w') as f:
|
||||||
json.dump(self._custom_resolution_options, f)
|
json.dump(self._custom_resolution_options, f)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from .displaydistancedialogcontent import DisplayDistanceDialogContent
|
||||||
class DisplayDistanceDialog(Gtk.Dialog):
|
class DisplayDistanceDialog(Gtk.Dialog):
|
||||||
__gtype_name__ = 'DisplayDistanceDialog'
|
__gtype_name__ = 'DisplayDistanceDialog'
|
||||||
|
|
||||||
show_full_scale_button = Gtk.Template.Child()
|
|
||||||
save_button = Gtk.Template.Child()
|
save_button = Gtk.Template.Child()
|
||||||
|
|
||||||
def __init__(self, settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit):
|
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.on_save_callback = on_save_callback
|
||||||
self.set_title(title)
|
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.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):
|
def _on_save_callback(self, prev_distance, distance):
|
||||||
self.on_save_callback(prev_distance, distance)
|
self.on_save_callback(prev_distance, distance)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from gi.repository import Gtk, Gio
|
from gi.repository import Gtk, Gio
|
||||||
from .settingsmanager import SettingsManager
|
from .settingsmanager import SettingsManager
|
||||||
|
from .statemanager import StateManager
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
|
|
||||||
|
|
@ -14,7 +15,7 @@ class DisplayDistanceDialogContent(Gtk.Box):
|
||||||
display_distance_scale = Gtk.Template.Child()
|
display_distance_scale = Gtk.Template.Child()
|
||||||
display_distance_adjustment = 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__()
|
super(Gtk.Box, self).__init__()
|
||||||
self.init_template()
|
self.init_template()
|
||||||
|
|
||||||
|
|
@ -22,37 +23,28 @@ class DisplayDistanceDialogContent(Gtk.Box):
|
||||||
|
|
||||||
self.on_save_callback = on_save_callback
|
self.on_save_callback = on_save_callback
|
||||||
self.settings = SettingsManager.get_instance().settings
|
self.settings = SettingsManager.get_instance().settings
|
||||||
|
self.state_manager = StateManager.get_instance()
|
||||||
self.prev_distance = self.settings.get_double('display-distance')
|
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.display_distance_scale.set_format_value_func(lambda scale, val: self._format_distance(val))
|
||||||
self.upper_limit_orig = self.display_distance_adjustment.get_upper()
|
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())
|
||||||
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)
|
|
||||||
save_button.connect('clicked', self._on_save_button_clicked)
|
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):
|
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>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</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>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwActionRow">
|
||||||
<property name="title" translatable="yes"><!-- adjustment slider -->Follow threshold</property>
|
<property name="title" translatable="yes"><!-- adjustment slider -->Follow threshold</property>
|
||||||
|
|
@ -554,6 +584,35 @@
|
||||||
<object class="AdwPreferencesGroup">
|
<object class="AdwPreferencesGroup">
|
||||||
<property name="title" translatable="yes"><!-- section heading for the advanced settings -->Advanced Settings</property>
|
<property name="title" translatable="yes"><!-- section heading for the advanced settings -->Advanced Settings</property>
|
||||||
<property name="width-request">450</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>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwActionRow">
|
||||||
<property name="title" translatable="yes"><!-- feature that tries to the find best-fit monitor config -->Find optimal display config</property>
|
<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="has-origin">false</property>
|
||||||
<property name="adjustment">
|
<property name="adjustment">
|
||||||
<object class="GtkAdjustment" id="display_distance_adjustment">
|
<object class="GtkAdjustment" id="display_distance_adjustment">
|
||||||
<property name="lower">0.2</property>
|
<property name="lower">0.1</property>
|
||||||
<property name="upper">2.5</property>
|
<property name="upper">1.5</property>
|
||||||
<property name="step-increment">0.01</property>
|
<property name="step-increment">0.01</property>
|
||||||
<property name="value">1.05</property>
|
<property name="value">1.05</property>
|
||||||
</object>
|
</object>
|
||||||
</property>
|
</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>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,6 @@
|
||||||
<template class="DisplayDistanceDialog" parent="GtkDialog">
|
<template class="DisplayDistanceDialog" parent="GtkDialog">
|
||||||
<property name="modal">1</property>
|
<property name="modal">1</property>
|
||||||
<property name="use-header-bar">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">
|
<child type="action">
|
||||||
<object class="GtkButton" id="save_button">
|
<object class="GtkButton" id="save_button">
|
||||||
<property name="label" translatable="yes">Done</property>
|
<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),
|
'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),
|
'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),
|
'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
|
_instance = None
|
||||||
|
|
@ -59,6 +61,8 @@ class StateManager(GObject.GObject):
|
||||||
self.license_present = False
|
self.license_present = False
|
||||||
self.enabled_features = []
|
self.enabled_features = []
|
||||||
self.device_supports_sbs = False
|
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._running = True
|
||||||
self._refresh_state()
|
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('device-supports-sbs', self.state.get('sbs_mode_supported', False))
|
||||||
self.set_property('widescreen-mode', self.state.get('sbs_mode_enabled', 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()
|
if self._running: threading.Timer(1.0, self._refresh_state).start()
|
||||||
|
|
||||||
def do_set_property(self, prop, value):
|
def do_set_property(self, prop, value):
|
||||||
|
|
@ -115,6 +127,10 @@ class StateManager(GObject.GObject):
|
||||||
self.enabled_features = value
|
self.enabled_features = value
|
||||||
if prop.name == 'device-supports-sbs':
|
if prop.name == 'device-supports-sbs':
|
||||||
self.device_supports_sbs = value
|
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):
|
def do_get_property(self, prop):
|
||||||
if prop.name == 'driver-running':
|
if prop.name == 'driver-running':
|
||||||
|
|
@ -131,3 +147,7 @@ class StateManager(GObject.GObject):
|
||||||
return self.enabled_features
|
return self.enabled_features
|
||||||
if prop.name == 'device-supports-sbs':
|
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