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._show_banner_connection = null;
this._distance_connection = null;
this._display_size_connection = null;
this._focused_monitor_distance_connection = null;
this._follow_threshold_connection = null;
this._breezy_desktop_running_connection = null;
@ -241,6 +242,7 @@ export default class BreezyDesktopExtension extends Extension {
viewport_offset_x: this.settings.get_double('viewport-offset-x'),
viewport_offset_y: this.settings.get_double('viewport-offset-y'),
display_distance: this.settings.get_double('display-distance'),
display_size: this.settings.get_double('display-size'),
toggle_display_distance_start: this.settings.get_double('toggle-display-distance-start'),
toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'),
framerate_cap: this.settings.get_double('framerate-cap'),
@ -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._display_size_connection = this.settings.connect('changed::display-size', this._update_display_distance.bind(this));
this._focused_monitor_distance_connection =
this._virtual_displays_actor.connect('notify::focused-monitor-details', this._update_display_distance.bind(this));
this._follow_threshold_connection = this.settings.connect('changed::follow-threshold', this._update_follow_threshold.bind(this));
@ -398,17 +401,18 @@ export default class BreezyDesktopExtension extends Extension {
}
_update_display_distance(object, event) {
const value = this.settings.get_double('display-distance');
Globals.logger.log_debug(`BreezyDesktopExtension _update_display_distance ${value}`);
if (value !== undefined) {
const distance = this.settings.get_double('display-distance');
const size = this.settings.get_double('display-size');
Globals.logger.log_debug(`BreezyDesktopExtension _update_display_distance ${distance} ${size}`);
if (distance !== undefined && size !== undefined) {
let focusedMonitorSizeAdjustment = 1.0;
if (this._virtual_displays_actor?.focused_monitor_details && this._target_monitor) {
const fovMonitor = this._target_monitor.monitor;
const focusedMonitor = this._virtual_displays_actor.focused_monitor_details;
focusedMonitorSizeAdjustment =
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._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) {
this._virtual_displays_actor.disconnect(this._focused_monitor_distance_connection);
this._focused_monitor_distance_connection = null;

View File

@ -176,12 +176,21 @@ export const VirtualDisplayEffect = GObject.registerClass({
GObject.ParamFlags.READWRITE,
true
),
'display-size': GObject.ParamSpec.double(
'display-size',
'Display size',
'Size of the display',
GObject.ParamFlags.READWRITE,
0.1,
2.5,
1.0
),
'display-distance': GObject.ParamSpec.double(
'display-distance',
'Display Distance',
'Distance of the display from the camera',
GObject.ParamFlags.READWRITE,
0.0,
0.1,
2.5,
1.0
),
@ -190,7 +199,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
'Display distance default',
'Distance to use when not explicitly set, or when reset',
GObject.ParamFlags.READWRITE,
0.2,
0.1,
2.5,
1.0
),
@ -253,6 +262,7 @@ export const VirtualDisplayEffect = GObject.registerClass({
this._use_smooth_follow_origin = false;
this.connect('notify::display-distance', this._update_display_distance.bind(this));
this.connect('notify::display-size', this._update_display_position.bind(this));
this.connect('notify::focused-monitor-index', this._update_display_distance.bind(this));
this.connect('notify::monitor-placements', this._update_display_position.bind(this));
this.connect('notify::show-banner', this._handle_banner_update.bind(this));
@ -389,7 +399,11 @@ export const VirtualDisplayEffect = GObject.registerClass({
finalPositionVector = noRotationVector.map(coord => coord * inverse_follow_ease);
finalPositionVector[0] = noRotationVector[0];
}
this._vertices = createVertexMesh(this.fov_details, this.monitor_details, finalPositionVector);
const resizedMonitorDetails = {
width: this.monitor_details.width * this.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;
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);
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);
console.log(`Breezy - Setting pose position: ${posePositionPixels}`);
} else {
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]);

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
const UNFOCUS_THRESHOLD = 1.1 / 2.0;
// returns how far the look vector is from the center of the monitor, as a percentage of the monitor's width
// returns how far the look vector is from the center of the monitor, as a percentage of the monitor's dimensions
function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVector, monitorDetails, upAngleToLength, westAngleToLength) {
const monitorAspectRatio = monitorDetails.width / monitorDetails.height;
// weight the up distance by the aspect ratio
const vectorUpPixels = upAngleToLength(
fovDetails.defaultDistanceVerticalRadians,
fovDetails.heightPixels,
@ -31,7 +28,7 @@ function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVec
monitorVector[2],
monitorVector[0]
);
const upDeltaPixels = (lookUpPixels - vectorUpPixels) * monitorAspectRatio;
const upPercentage = Math.abs(lookUpPixels - vectorUpPixels) / monitorDetails.height;
const vectorWestPixels = westAngleToLength(
fovDetails.defaultDistanceHorizontalRadians,
@ -40,11 +37,10 @@ function getMonitorDistance(fovDetails, lookUpPixels, lookWestPixels, monitorVec
monitorVector[1],
monitorVector[0]
);
const westDeltaPixels = lookWestPixels - vectorWestPixels;
const totalDeltaPixels = Math.sqrt(upDeltaPixels * upDeltaPixels + westDeltaPixels * westDeltaPixels);
const westPercentage = Math.abs(lookWestPixels - vectorWestPixels) / monitorDetails.width;
// threshold is a percentage of width, and height was already properly weighted
return totalDeltaPixels / monitorDetails.width;
// how close we are to any edge is the largest of the two percentages
return Math.max(upPercentage, westPercentage);
}
/**
@ -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
*/
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 rotatedLookVector = applyQuaternionToVector(lookVector, quaternion);
@ -82,9 +80,6 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
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
if (currentFocusedIndex !== -1) {
const focusedDistance = getMonitorDistance(
@ -97,9 +92,12 @@ function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, foc
westConversionFns.angleToLength
) * focusedMonitorDistance;
if (smoothFollowEnabled || focusedDistance < UNFOCUS_THRESHOLD) return currentFocusedIndex;
if (focusedDistance < UNFOCUS_THRESHOLD) return currentFocusedIndex;
}
let closestIndex = -1;
let closestDistance = Infinity;
// find the vector closest to the rotated look vector
monitorVectors.forEach((monitorVector, index) => {
if (index === currentFocusedIndex) return;
@ -499,7 +497,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
'Display size',
'Size of the display',
GObject.ParamFlags.READWRITE,
0.2,
0.1,
2.5,
1.0
),
@ -515,7 +513,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
'Display Distance',
'Distance of the display from the camera',
GObject.ParamFlags.READWRITE,
0.2,
0.1,
2.5,
1.05
),
@ -537,7 +535,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
'Display distance start',
'Start distance when using the "change distance" shortcut.',
GObject.ParamFlags.READWRITE,
0.2,
0.1,
2.5,
1.05
),
@ -546,7 +544,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
'Display distance end',
'End distance when using the "change distance" shortcut.',
GObject.ParamFlags.READWRITE,
0.2,
0.1,
2.5,
1.05
),
@ -578,7 +576,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
constructor(params = {}) {
super(params);
this._all_monitors = [
this._all_monitors_unmodified = [
this.target_monitor,
...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-end', 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-spacing', 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('framerate-cap', this._handle_frame_rate_cap_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_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}`);
this._all_monitors.forEach(((monitor, index) => {
this._all_monitors_unmodified.forEach(((monitor, index) => {
Globals.logger.log_debug(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);
const containerActor = new Clutter.Actor({
@ -711,6 +711,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
monitor_placements: this.monitor_placements,
fov_details: this.fov_details,
target_monitor: this.target_monitor,
display_size: this.display_size,
display_distance: this.display_distance,
display_distance_default: this._display_distance_default(),
actor_to_display_ratios: actorToDisplayRatios,
@ -735,6 +736,7 @@ export const VirtualDisplaysActor = GObject.registerClass({
[
'monitor-placements',
'display-size',
'fov-details',
'imu-snapshots',
'smooth-follow-enabled',
@ -827,6 +829,10 @@ export const VirtualDisplaysActor = GObject.registerClass({
this._redraw_timeline.start();
}
_size_adjusted_target_monitor() {
return this._all_monitors[0];
}
_display_distance_default() {
return Math.max(this.display_distance, this.toggle_display_distance_start, this.toggle_display_distance_end);
}
@ -869,7 +875,8 @@ export const VirtualDisplaysActor = GObject.registerClass({
const minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
const maxY = Math.max(...this._all_monitors.map(monitor => monitor.y + monitor.height));
if ((maxX - minX) / this.target_monitor.width >= (maxY - minY) / this.target_monitor.height) {
const targetMonitor = this._size_adjusted_target_monitor();
if ((maxX - minX) / targetMonitor.width >= (maxY - minY) / targetMonitor.height) {
return 'horizontal';
} else {
return 'vertical';
@ -878,17 +885,19 @@ export const VirtualDisplaysActor = GObject.registerClass({
_update_monitor_placements() {
try {
const targetMonitor = this._size_adjusted_target_monitor();
const minX = Math.min(...this._all_monitors.map(monitor => monitor.x));
const maxX = Math.max(...this._all_monitors.map(monitor => monitor.x + monitor.width));
const minY = Math.min(...this._all_monitors.map(monitor => monitor.y));
const maxY = Math.max(...this._all_monitors.map(monitor => monitor.y + monitor.height));
// the beginning edges of the viewport if it's centered on all displays
const allDisplaysCenterXBegin = (minX + maxX) / 2 - this.target_monitor.width / 2;
const allDisplaysCenterYBegin = (minY + maxY) / 2 - this.target_monitor.height / 2;
const allDisplaysCenterXBegin = (minX + maxX) / 2 - targetMonitor.width / 2;
const allDisplaysCenterYBegin = (minY + maxY) / 2 - targetMonitor.height / 2;
const viewportXBegin = this.headset_display_as_viewport_center ? this.target_monitor.x : allDisplaysCenterXBegin;
const viewportYBegin = this.headset_display_as_viewport_center ? this.target_monitor.y : allDisplaysCenterYBegin;
const viewportXBegin = this.headset_display_as_viewport_center ? targetMonitor.x : allDisplaysCenterXBegin;
const viewportYBegin = this.headset_display_as_viewport_center ? targetMonitor.y : allDisplaysCenterYBegin;
this.fov_details = this._fov_details();
this.lens_vector = [0.0, 0.0, -this.fov_details.lensDistancePixels];
@ -897,8 +906,8 @@ export const VirtualDisplaysActor = GObject.registerClass({
// shift all monitors so they center around the viewport center, then adjusted by the offsets
this._all_monitors.map(monitor => ({
x: monitor.x - viewportXBegin - this.viewport_offset_x * this.target_monitor.width,
y: monitor.y - viewportYBegin + this.viewport_offset_y * this.target_monitor.height,
x: monitor.x - viewportXBegin - this.viewport_offset_x * targetMonitor.width,
y: monitor.y - viewportYBegin + this.viewport_offset_y * targetMonitor.height,
width: monitor.width,
height: monitor.height
})),
@ -916,6 +925,16 @@ export const VirtualDisplaysActor = GObject.registerClass({
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() {
if (this.bannerActor) {
if (this.show_banner) {