Add support for display-size in the effect

This commit is contained in:
wheaney 2025-10-24 15:40:09 -07:00
parent e826292d3e
commit 9e15a500b8
3 changed files with 77 additions and 35 deletions

View File

@ -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;
@ -241,6 +242,7 @@ 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'),
@ -299,6 +301,7 @@ 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._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 +401,18 @@ 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}`);
if (distance !== undefined && size !== undefined) {
let focusedMonitorSizeAdjustment = 1.0; let focusedMonitorSizeAdjustment = 1.0;
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(size * focusedMonitor.width / fovMonitor.width, size * focusedMonitor.height / fovMonitor.height);
} }
this._write_control('breezy_desktop_display_distance', value / focusedMonitorSizeAdjustment); this._write_control('breezy_desktop_display_distance', distance / focusedMonitorSizeAdjustment);
} }
} }
@ -586,6 +590,10 @@ 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._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;

View File

@ -176,12 +176,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 +199,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 +262,7 @@ 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-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 +399,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.display_size,
height: this.monitor_details.height * this.display_size
};
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) {
@ -574,6 +588,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
const posePositionPixels = this.imu_snapshots.pose_position.map(coord => coord * this.fov_details.completeScreenDistancePixels); const posePositionPixels = this.imu_snapshots.pose_position.map(coord => coord * this.fov_details.completeScreenDistancePixels);
this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.pose_orientation); this.set_uniform_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);
console.log(`Breezy - Setting pose position: ${posePositionPixels}`);
} else { } else {
this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.smooth_follow_origin); this.set_uniform_matrix(this.get_uniform_location("u_pose_orientation"), false, 4, this.imu_snapshots.smooth_follow_origin);
this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, [0.0, 0.0, 0.0]); this.set_uniform_float(this.get_uniform_location("u_pose_position"), 3, [0.0, 0.0, 0.0]);

View File

@ -19,11 +19,8 @@ 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;
// weight the up distance by the aspect ratio
const vectorUpPixels = upAngleToLength( const vectorUpPixels = upAngleToLength(
fovDetails.defaultDistanceVerticalRadians, fovDetails.defaultDistanceVerticalRadians,
fovDetails.heightPixels, fovDetails.heightPixels,
@ -31,7 +28,7 @@ function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVec
monitorVector[2], monitorVector[2],
monitorVector[0] monitorVector[0]
); );
const upDeltaPixels = (lookUpPixels - vectorUpPixels) * monitorAspectRatio; const upPercentage = Math.abs(lookUpPixels - vectorUpPixels) / monitorDetails.height;
const vectorWestPixels = westAngleToLength( const vectorWestPixels = westAngleToLength(
fovDetails.defaultDistanceHorizontalRadians, fovDetails.defaultDistanceHorizontalRadians,
@ -40,11 +37,10 @@ function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVec
monitorVector[1], monitorVector[1],
monitorVector[0] monitorVector[0]
); );
const westDeltaPixels = lookWestPixels - vectorWestPixels; const westPercentage = Math.abs(lookWestPixels - 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);
} }
/** /**
@ -60,6 +56,8 @@ function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVec
* @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, 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,9 +80,6 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
rotatedLookVector[0] rotatedLookVector[0]
); );
let closestIndex = -1;
let closestDistance = Infinity;
// 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) {
const focusedDistance = getMonitorDistance( const focusedDistance = getMonitorDistance(
@ -97,9 +92,12 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
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;
@ -499,7 +497,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 +513,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 +535,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 +544,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 +576,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
]; ];
@ -653,6 +651,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
notifyToFunction('toggle-display-distance-start', this._handle_display_distance_properties_change); notifyToFunction('toggle-display-distance-start', this._handle_display_distance_properties_change);
notifyToFunction('toggle-display-distance-end', this._handle_display_distance_properties_change); notifyToFunction('toggle-display-distance-end', this._handle_display_distance_properties_change);
notifyToFunction('display-distance', this._handle_display_distance_properties_change); notifyToFunction('display-distance', this._handle_display_distance_properties_change);
notifyToFunction('display-size', this._handle_display_size_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,6 +662,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_size_change(false);
this._handle_display_distance_properties_change(); this._handle_display_distance_properties_change();
this._handle_frame_rate_cap_change(); this._handle_frame_rate_cap_change();
@ -680,8 +680,8 @@ 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({
@ -711,6 +711,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
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,6 +736,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
[ [
'monitor-placements', 'monitor-placements',
'display-size',
'fov-details', 'fov-details',
'imu-snapshots', 'imu-snapshots',
'smooth-follow-enabled', 'smooth-follow-enabled',
@ -827,6 +829,10 @@ 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);
} }
@ -869,7 +875,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,17 +885,19 @@ 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 = [0.0, 0.0, -this.fov_details.lensDistancePixels];
@ -897,8 +906,8 @@ export const VirtualDisplaysActor = GObject.registerClass({
// 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
})), })),
@ -916,6 +925,16 @@ export const VirtualDisplaysActor = GObject.registerClass({
this._update_monitor_placements(); this._update_monitor_placements();
} }
_handle_display_size_change(update_placements = true) {
this._all_monitors = this._all_monitors_unmodified.map(monitor => ({
x: monitor.x * this.display_size,
y: monitor.y * this.display_size,
width: monitor.width * this.display_size,
height: monitor.height * this.display_size
}));
if (update_placements) this._update_monitor_placements();
}
_handle_banner_update() { _handle_banner_update() {
if (this.bannerActor) { if (this.bannerActor) {
if (this.show_banner) { if (this.show_banner) {