diff --git a/gnome/src/devicedatastream.js b/gnome/src/devicedatastream.js
index 75b426a..161e9ff 100644
--- a/gnome/src/devicedatastream.js
+++ b/gnome/src/devicedatastream.js
@@ -80,7 +80,7 @@ export const DeviceDataStream = GObject.registerClass({
this.breezy_desktop_running = false;
this._ipc_file = Gio.file_new_for_path(IPC_FILE_PATH);
this._running = false;
- this._device_data = null;
+ this.device_data = null;
}
start() {
@@ -105,9 +105,9 @@ export const DeviceDataStream = GObject.registerClass({
// hasn't been checked within KEEPALIVE_REFRESH_INTERVAL_SEC.
refresh_data(keepalive_only = false) {
if (this._ipc_file.query_exists(null) && (
- !this._device_data?.imuData ||
+ !this.device_data?.imuData ||
!keepalive_only ||
- getEpochSec() - toSec(this._device_data?.imuDateMs ?? 0) > KEEPALIVE_REFRESH_INTERVAL_SEC
+ getEpochSec() - toSec(this.device_data?.imuDateMs ?? 0) > KEEPALIVE_REFRESH_INTERVAL_SEC
)) {
let data = this._ipc_file.load_contents(null);
if (data[0]) {
@@ -125,8 +125,8 @@ export const DeviceDataStream = GObject.registerClass({
// update the widescreen property if the state changes while still enabled, trigger "notify::" events
if (enabled && this.widescreen_mode_state !== sbsEnabled) this.widescreen_mode_state = sbsEnabled;
- if (!this._device_data) {
- this._device_data = {
+ if (!this.device_data) {
+ this.device_data = {
version,
enabled,
imuResetState,
@@ -142,8 +142,8 @@ export const DeviceDataStream = GObject.registerClass({
while (!success && attempts < 3) {
if (dataView.byteLength === DATA_VIEW_LENGTH) {
if (checkParityByte(dataView)) {
- this._device_data.imuData = imuData;
- this._device_data.imuDateMs = imuDateMs;
+ this.device_data.imuData = imuData;
+ this.device_data.imuDateMs = imuDateMs;
this.imu_snapshots = {
imu_data: imuData,
timestamp_ms: imuDateMs
diff --git a/gnome/src/extension.js b/gnome/src/extension.js
index 94bb3b7..6a86e20 100644
--- a/gnome/src/extension.js
+++ b/gnome/src/extension.js
@@ -48,6 +48,9 @@ export default class BreezyDesktopExtension extends Extension {
this._target_monitor = null;
this._is_effect_running = false;
this._distance_binding = null;
+ this._monitor_wrapping_scheme_binding = null;
+ this._viewport_offset_x_binding = null;
+ this._viewport_offset_y_binding = null;
this._distance_connection = null;
this._follow_threshold_connection = null;
this._widescreen_mode_settings_connection = null;
@@ -139,7 +142,7 @@ export default class BreezyDesktopExtension extends Extension {
this._running_poller_id = undefined;
return GLib.SOURCE_REMOVE;
} else {
- Globals.logger.log_debug(`BreezyDesktopExtension _poll_for_ready - device connected: ${Globals.data_stream.breezy_desktop_running}, target_monitor: ${!!target_monitor}`);
+ Globals.logger.log_debug(`BreezyDesktopExtension _poll_for_ready - breezy enabled: ${Globals.data_stream.breezy_desktop_running}, target_monitor: ${!!target_monitor}`);
return GLib.SOURCE_CONTINUE;
}
} catch (e) {
@@ -175,31 +178,30 @@ export default class BreezyDesktopExtension extends Extension {
try {
Globals.logger.log_debug('BreezyDesktopExtension _find_supported_monitor');
- const target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find(
+ let target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find(
monitor => monitor && (SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product) ||
this.settings.get_string('custom-monitor-product') === monitor.product));
+ let is_dummy = target_monitor?.product === NESTED_MONITOR_PRODUCT;
+
+ if (target_monitor === undefined && this.settings.get_boolean('developer-mode')) {
+ Globals.logger.log_debug('BreezyDesktopExtension _find_supported_monitor - Using dummy monitor');
+ // find the first of the physical monitors
+ target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find(
+ monitor => monitor && monitor.product !== VIRTUAL_MONITOR_PRODUCT);
+ is_dummy = true;
+ }
+
if (target_monitor !== undefined) {
Globals.logger.log(`Identified supported monitor: ${target_monitor.product} on ${target_monitor.connector}`);
return {
monitor: this._monitor_manager.getMonitors()[target_monitor.index],
connector: target_monitor.connector,
refreshRate: target_monitor.refreshRate,
- is_dummy: target_monitor.product === NESTED_MONITOR_PRODUCT,
+ is_dummy: is_dummy,
is_virtual: target_monitor.product === VIRTUAL_MONITOR_PRODUCT
};
}
- if (this.settings.get_boolean('developer-mode')) {
- Globals.logger.log_debug('BreezyDesktopExtension _find_supported_monitor - Using dummy monitor');
- // allow testing XR devices with just USB, no video needed
- return {
- monitor: this._monitor_manager.getMonitors()[0],
- connector: 'dummy',
- refreshRate: 60,
- is_dummy: true
- };
- }
-
Globals.logger.log_debug('BreezyDesktopExtension _find_supported_monitor - No supported monitor found');
return null;
} catch (e) {
@@ -292,7 +294,9 @@ export default class BreezyDesktopExtension extends Extension {
Globals.data_stream.refresh_data();
this._overlay_content = new VirtualMonitorsActor({
monitors: virtualMonitors,
- fov_degrees: 46.0,
+ monitor_wrapping_scheme: this.settings.get_string('monitor-wrapping-scheme'),
+ viewport_offset_x: this.settings.get_double('viewport-offset-x'),
+ viewport_offset_y: this.settings.get_double('viewport-offset-y'),
target_monitor: targetMonitor,
display_distance: this.settings.get_double('display-distance'),
toggle_display_distance_start: this.settings.get_double('toggle-display-distance-start'),
@@ -326,6 +330,9 @@ export default class BreezyDesktopExtension extends Extension {
this._breezy_desktop_running_connection = Globals.data_stream.connect('notify::breezy-desktop-running', this._handle_breezy_desktop_running_change.bind(this));
this._overlay_content.renderMonitors();
+ this._monitor_wrapping_scheme_binding = this.settings.bind('monitor-wrapping-scheme', this._overlay_content, 'monitor-wrapping-scheme', Gio.SettingsBindFlags.DEFAULT);
+ this._viewport_offset_x_binding = this.settings.bind('viewport-offset-x', this._overlay_content, 'viewport-offset-x', Gio.SettingsBindFlags.DEFAULT);
+ this._viewport_offset_y_binding = this.settings.bind('viewport-offset-y', this._overlay_content, 'viewport-offset-y', Gio.SettingsBindFlags.DEFAULT);
this._distance_binding = this.settings.bind('display-distance', this._overlay_content, 'display-distance', Gio.SettingsBindFlags.DEFAULT);
this._distance_connection = this.settings.connect('changed::display-distance', this._update_display_distance.bind(this));
this._follow_threshold_connection = this.settings.connect('changed::follow-threshold', this._update_follow_threshold.bind(this));
@@ -591,6 +598,18 @@ export default class BreezyDesktopExtension extends Extension {
this.settings.unbind(this._distance_binding);
this._distance_binding = null;
}
+ if (this._viewport_offset_x_binding) {
+ this.settings.unbind(this._viewport_offset_x_binding);
+ this._viewport_offset_x_binding = null;
+ }
+ if (this._viewport_offset_y_binding) {
+ this.settings.unbind(this._viewport_offset_y_binding);
+ this._viewport_offset_y_binding = null;
+ }
+ if (this._monitor_wrapping_scheme_binding) {
+ this.settings.unbind(this._monitor_wrapping_scheme_binding);
+ this._monitor_wrapping_scheme_binding = null;
+ }
if (this._distance_connection) {
this.settings.disconnect(this._distance_connection);
this._distance_connection = null;
diff --git a/gnome/src/virtualmonitorsactor.js b/gnome/src/virtualmonitorsactor.js
index d45a2ba..f005379 100644
--- a/gnome/src/virtualmonitorsactor.js
+++ b/gnome/src/virtualmonitorsactor.js
@@ -31,7 +31,6 @@ function findClosestVector(quaternion, vectors, previousClosestIndex) {
const lookVector = [1.0, 0.0, 0.0]; // NWU vector pointing to the center of the screen
const rotatedLookVector = applyQuaternionToVector(lookVector, quaternion);
- // Globals.logger.log(`\t\t\tRotated look vector: ${rotatedLookVector}`);
let closestIndex = -1;
let closestDistance = Infinity;
@@ -54,8 +53,6 @@ function findClosestVector(quaternion, vectors, previousClosestIndex) {
}
});
- // Globals.logger.log(`\t\t\tClosest monitor: ${closestIndex}, distance: ${closestDistance}`);
-
// only switch if the closest monitor is greater than the previous closest by 25%
if (previousClosestIndex !== undefined && closestIndex !== previousClosestIndex && closestDistance * 1.25 > previousDistance) {
return previousClosestIndex;
@@ -68,10 +65,6 @@ function degreesToRadians(degrees) {
return degrees * Math.PI / 180.0;
}
-function radiansToDegrees(radians) {
- return radians * 180.0 / Math.PI;
-}
-
/***
* @returns {Object} - containing `start`, `center`, and `end` radians for rotating the given monitor
*/
@@ -247,19 +240,18 @@ export const VirtualMonitorEffect = GObject.registerClass({
GObject.ParamFlags.READWRITE,
0, 100, 0
),
+ 'monitor-placements': GObject.ParamSpec.jsobject(
+ 'monitor-placements',
+ 'Monitor Placements',
+ 'Target and virtual monitor placement details, as relevant to rendering',
+ GObject.ParamFlags.READWRITE
+ ),
'imu-snapshots': GObject.ParamSpec.jsobject(
'imu-snapshots',
'IMU Snapshots',
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
GObject.ParamFlags.READWRITE
),
- 'fov-degrees': GObject.ParamSpec.double(
- 'fov-degrees',
- 'FOV Degrees',
- 'Field of view in degrees',
- GObject.ParamFlags.READWRITE,
- 30.0, 100.0, 46.0
- ),
'width': GObject.ParamSpec.int(
'width',
'Width',
@@ -281,13 +273,6 @@ export const VirtualMonitorEffect = GObject.registerClass({
GObject.ParamFlags.READWRITE,
'horizontal', ['horizontal', 'vertical', 'none']
),
- 'monitor-wrapping-rotation-radians': GObject.ParamSpec.double(
- 'monitor-wrapping-rotation-radians',
- 'Monitor Wrapping Rotation Radians',
- 'Rotation of the monitor wrapping around the viewport',
- GObject.ParamFlags.READWRITE,
- -360.0, 360.0, 0.0
- ),
'focused-monitor-index': GObject.ParamSpec.int(
'focused-monitor-index',
'Focused Monitor Index',
@@ -347,6 +332,8 @@ export const VirtualMonitorEffect = GObject.registerClass({
this.connect('notify::display-distance', 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_uniforms.bind(this));
+ this.connect('notify::monitor-wrapping-scheme', this._update_display_position_uniforms.bind(this));
}
_is_focused() {
@@ -361,8 +348,6 @@ export const VirtualMonitorEffect = GObject.registerClass({
this._distance_ease_timeline.stop();
}
-
- const mid_distance = (this.display_distance_default + desired_distance) / 2;
// if we're the focused display, we'll double the timeline and wait for the first half to let other
// displays ease out first
@@ -390,11 +375,28 @@ export const VirtualMonitorEffect = GObject.registerClass({
this._current_display_distance = this._distance_ease_start +
progress * (this._distance_ease_target - this._distance_ease_start);
+ this._update_display_position_uniforms();
}).bind(this));
this._distance_ease_timeline.start();
}
+ _update_display_position_uniforms() {
+ // this is in NWU coordinates
+ const noRotationVector = this.monitor_placements[this.monitor_index].topLeftNoRotate;
+ Globals.logger.log_debug(`\t\t\tMonitor ${this.monitor_index} vectors: ${JSON.stringify(this.monitor_placements[this.monitor_index])}`);
+
+ // convert to CoGL's east-down-south coordinates and apply display distance
+ this.set_uniform_float(this.get_uniform_location("u_display_position"), 3,
+ [-noRotationVector[1], -noRotationVector[2], this._current_display_distance * -noRotationVector[0]]);
+
+ const rotation_radians = this.monitor_placements[this.monitor_index].rotationAngleRadians;
+ this.set_uniform_float(this.get_uniform_location("u_rotation_x_radians"), 1,
+ [this.monitor_wrapping_scheme === 'vertical' ? rotation_radians : 0.0]);
+ this.set_uniform_float(this.get_uniform_location("u_rotation_y_radians"), 1,
+ [this.monitor_wrapping_scheme === 'horizontal' ? rotation_radians : 0.0]);
+ }
+
perspective(fovDiagonalRadians, aspect, near, far) {
// compute horizontal fov given diagonal fov and aspect ratio
const h = Math.sqrt(aspect * aspect + 1);
@@ -516,22 +518,20 @@ export const VirtualMonitorEffect = GObject.registerClass({
if (!this._initialized) {
const aspect = this.get_actor().width / this.get_actor().height;
const projection_matrix = this.perspective(
- this.fov_degrees * Math.PI / 180.0,
+ Globals.data_stream.device_data.displayFov * Math.PI / 180.0,
aspect,
0.0001,
1000.0
);
this.set_uniform_matrix(this.get_uniform_location("u_projection_matrix"), false, 4, projection_matrix);
- this.set_uniform_float(this.get_uniform_location("u_rotation_x_radians"), 1, [this.monitor_wrapping_scheme === 'vertical' ? this.monitor_wrapping_rotation_radians : 0.0]);
- this.set_uniform_float(this.get_uniform_location("u_rotation_y_radians"), 1, [this.monitor_wrapping_scheme === 'horizontal' ? this.monitor_wrapping_rotation_radians : 0.0]);
this.set_uniform_float(this.get_uniform_location("u_display_resolution"), 2, [this.get_actor().width, this.get_actor().height]);
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._update_display_position_uniforms();
this._initialized = true;
}
- this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [lookAheadMS(this.imu_snapshots.timestamp_ms, 0)]);
- this.set_uniform_float(this.get_uniform_location("u_display_position"), 3, [this.display_position[0], this.display_position[1], this._current_display_distance * this.display_position[2]]);
+ this.set_uniform_float(this.get_uniform_location('u_look_ahead_ms'), 1, [lookAheadMS(this.imu_snapshots.timestamp_ms, 5)]);
this.set_uniform_matrix(this.get_uniform_location("u_imu_data"), false, 4, this.imu_snapshots.imu_data);
this.get_pipeline().set_layer_filters(
@@ -552,25 +552,45 @@ export const VirtualMonitorsActor = GObject.registerClass({
'Array of monitor indexes',
GObject.ParamFlags.READWRITE
),
+ 'monitor-wrapping-scheme': GObject.ParamSpec.string(
+ 'monitor-wrapping-scheme',
+ 'Monitor Wrapping Scheme',
+ 'How the monitors are wrapped around the viewport',
+ GObject.ParamFlags.READWRITE,
+ 'horizontal', ['horizontal', 'vertical', 'none']
+ ),
'target-monitor': GObject.ParamSpec.jsobject(
'target-monitor',
'Target Monitor',
'Details about the monitor being used as a viewport',
GObject.ParamFlags.READWRITE
),
+ 'viewport-offset-x': GObject.ParamSpec.double(
+ 'viewport-offset-x',
+ 'Viewport Offset x',
+ 'Offset to apply to the viewport',
+ GObject.ParamFlags.READWRITE,
+ -2.5, 2.5, 0.0
+ ),
+ 'viewport-offset-y': GObject.ParamSpec.double(
+ 'viewport-offset-y',
+ 'Viewport Offset y',
+ 'Offset to apply to the viewport',
+ GObject.ParamFlags.READWRITE,
+ -2.5, 2.5, 0.0
+ ),
+ 'monitor-placements': GObject.ParamSpec.jsobject(
+ 'monitor-placements',
+ 'Monitor Placements',
+ 'Target and virtual monitor placement details, as relevant to rendering',
+ GObject.ParamFlags.READWRITE
+ ),
'imu-snapshots': GObject.ParamSpec.jsobject(
'imu-snapshots',
'IMU Snapshots',
'Latest IMU quaternion snapshots and epoch timestamp for when it was collected',
GObject.ParamFlags.READWRITE
),
- 'fov-degrees': GObject.ParamSpec.double(
- 'fov-degrees',
- 'FOV Degrees',
- 'Field of view in degrees',
- GObject.ParamFlags.READWRITE,
- 30.0, 100.0, 46.0
- ),
'focused-monitor-index': GObject.ParamSpec.int(
'focused-monitor-index',
'Focused Monitor Index',
@@ -629,34 +649,16 @@ export const VirtualMonitorsActor = GObject.registerClass({
this.width = this.target_monitor.width;
this.height = this.target_monitor.height;
this._frametime_ms = Math.floor(1000 / (this.target_framerate ?? 60.0));
+
this._all_monitors = [
this.target_monitor,
...this.monitors
- ];
+ ]
}
renderMonitors() {
- this._monitorPlacements = monitorsToPlacements(
- {
- fovDegrees: this.fov_degrees,
- widthPixels: this.width,
- heightPixels: this.height
- },
- this._all_monitors.map(monitor => ({
- x: monitor.x,
- y: monitor.y,
- width: monitor.width,
- height: monitor.height
- })),
- 'horizontal'
- );
+ this._update_monitor_placements();
- // normalize the center vectors
- this._monitorsAsNormalizedVectors = this._monitorPlacements.map(monitorVectors => {
- const vector = monitorVectors.center;
- const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
- return [vector[0] / length, vector[1] / length, vector[2] / length];
- });
const actorToDisplayRatios = [
global.stage.width / this.width,
global.stage.height / this.height
@@ -673,14 +675,8 @@ export const VirtualMonitorsActor = GObject.registerClass({
Globals.logger.log_debug(`\t\t\tActor to display ratios: ${actorToDisplayRatios}, offsets: ${actorToDisplayOffsets}`);
this._all_monitors.forEach(((monitor, index) => {
- // if (index === 0) return;
Globals.logger.log(`\t\t\tMonitor ${index}: ${monitor.x}, ${monitor.y}, ${monitor.width}, ${monitor.height}`);
-
- // this is in NWU coordinates
- const noRotationVector = this._monitorPlacements[index].topLeftNoRotate;
- Globals.logger.log_debug(`\t\t\tMonitor ${index} vectors: ${JSON.stringify(this._monitorPlacements[index])}`);
- // actor coordinates are east-up-south
const containerActor = new Clutter.Actor({
width: this.width,
height: this.height,
@@ -700,18 +696,18 @@ export const VirtualMonitorsActor = GObject.registerClass({
containerActor.add_child(monitorClone);
const effect = new VirtualMonitorEffect({
imu_snapshots: this.imu_snapshots,
- fov_degrees: this.fov_degrees,
monitor_index: index,
- display_position: [-noRotationVector[1], -noRotationVector[2], -noRotationVector[0]],
+ monitor_placements: this.monitor_placements,
display_distance: this.display_distance,
display_distance_default: Math.max(this.toggle_display_distance_start, this.toggle_display_distance_end),
- monitor_wrapping_scheme: 'horizontal',
- monitor_wrapping_rotation_radians: this._monitorPlacements[index].rotationAngleRadians,
+ monitor_wrapping_scheme: this.monitor_wrapping_scheme,
actor_to_display_ratios: actorToDisplayRatios,
actor_to_display_offsets: actorToDisplayOffsets
});
containerActor.add_effect_with_name('viewport-effect', effect);
this.add_child(containerActor);
+ this.bind_property('monitor-placements', effect, 'monitor-placements', GObject.BindingFlags.DEFAULT);
+ this.bind_property('monitor-wrapping-scheme', effect, 'monitor-wrapping-scheme', GObject.BindingFlags.DEFAULT);
this.bind_property('imu-snapshots', effect, 'imu-snapshots', GObject.BindingFlags.DEFAULT);
this.bind_property('focused-monitor-index', effect, 'focused-monitor-index', GObject.BindingFlags.DEFAULT);
this.bind_property('display-distance', effect, 'display-distance', GObject.BindingFlags.DEFAULT);
@@ -729,7 +725,6 @@ export const VirtualMonitorsActor = GObject.registerClass({
this._monitorsAsNormalizedVectors, this.closestMonitorIndex
);
- // only switch if the closest monitor is greater than the previous closest by 25%
if (closestMonitorIndex !== -1 && (this.focused_monitor_index === undefined || this.focused_monitor_index !== closestMonitorIndex)) {
Globals.logger.log(`Switching to monitor ${closestMonitorIndex}`);
this.focused_monitor_index = closestMonitorIndex;
@@ -756,8 +751,37 @@ export const VirtualMonitorsActor = GObject.registerClass({
this.connect('notify::toggle-display-distance-start', this._handle_display_distance_properties_change.bind(this));
this.connect('notify::toggle-display-distance-end', this._handle_display_distance_properties_change.bind(this));
this.connect('notify::display-distance', this._handle_display_distance_properties_change.bind(this));
+ this.connect('notify::viewport-offset-x', this._update_monitor_placements.bind(this));
+ this.connect('notify::viewport-offset-y', this._update_monitor_placements.bind(this));
this._handle_display_distance_properties_change();
}
+
+ _update_monitor_placements() {
+ Globals.logger.log_debug(`\t\t\tUpdating monitor placements ${this.viewport_offset_x}, ${this.viewport_offset_y} ${Globals.data_stream.device_data.displayFov}`);
+ this.monitor_placements = monitorsToPlacements(
+ {
+ fovDegrees: Globals.data_stream.device_data.displayFov,
+ widthPixels: this.width,
+ heightPixels: this.height
+ },
+
+ // shift all monitors so they center around the target monitor, then adjusted by the offsets
+ this._all_monitors.map(monitor => ({
+ x: monitor.x - this.target_monitor.x - this.viewport_offset_x * this.target_monitor.width,
+ y: monitor.y - this.target_monitor.y - this.viewport_offset_y * this.target_monitor.height,
+ width: monitor.width,
+ height: monitor.height
+ })),
+ this.monitor_wrapping_scheme
+ );
+
+ // normalize the center vectors
+ this._monitorsAsNormalizedVectors = this.monitor_placements.map(monitorVectors => {
+ const vector = monitorVectors.center;
+ const length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
+ return [vector[0] / length, vector[1] / length, vector[2] / length];
+ });
+ }
_handle_display_distance_properties_change() {
const distance_from_end = Math.abs(this.display_distance - this.toggle_display_distance_end);
diff --git a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
index b5aafdd..71cabf2 100644
--- a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
+++ b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
@@ -91,6 +91,33 @@
The size of the display
+
+
+ 0.0
+
+ Viewport offset x
+
+ How far to offset the viewport from the target monitor in the x direction
+
+
+
+
+ 0.0
+
+ Viewport offset y
+
+ How far to offset the viewport from the target monitor in the y direction
+
+
+
+
+ "automatic"
+
+ Monitor wrapping scheme
+
+ How the monitors are wrapped around the viewport
+
+
false
diff --git a/ui/src/connecteddevice.py b/ui/src/connecteddevice.py
index efad3f6..9ab3523 100644
--- a/ui/src/connecteddevice.py
+++ b/ui/src/connecteddevice.py
@@ -51,6 +51,7 @@ class ConnectedDevice(Gtk.Box):
movement_look_ahead_adjustment = Gtk.Template.Child()
text_scaling_scale = Gtk.Template.Child()
text_scaling_adjustment = Gtk.Template.Child()
+ monitor_wrapping_scheme_menu = Gtk.Template.Child()
def __init__(self):
@@ -66,7 +67,8 @@ class ConnectedDevice(Gtk.Box):
# self.add_virtual_display_button,
self.set_toggle_display_distance_start_button,
self.set_toggle_display_distance_end_button,
- self.movement_look_ahead_scale
+ self.movement_look_ahead_scale,
+ self.monitor_wrapping_scheme_menu
]
self.settings = SettingsManager.get_instance().settings
@@ -84,7 +86,10 @@ class ConnectedDevice(Gtk.Box):
self.settings.bind('use-highest-refresh-rate', self.use_highest_refresh_rate_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('fast-sbs-mode-switching', self.fast_sbs_mode_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('look-ahead-override', self.movement_look_ahead_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
+ self.settings.connect('changed::monitor-wrapping-scheme', self._handle_monitor_wrapping_scheme_setting_changed)
self.desktop_settings.bind('text-scaling-factor', self.text_scaling_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
+ self.monitor_wrapping_scheme_menu.connect('changed', self._handle_monitor_wrapping_scheme_menu_changed)
+ self._handle_monitor_wrapping_scheme_setting_changed()
bind_shortcut_settings(self.get_parent(), [
[self.reassign_toggle_xr_effect_shortcut_button, self.toggle_xr_effect_shortcut_label],
@@ -121,6 +126,13 @@ class ConnectedDevice(Gtk.Box):
self.connect("destroy", self._on_widget_destroy)
+ def _handle_monitor_wrapping_scheme_setting_changed(self):
+ current_scheme = self.settings.get_string('monitor-wrapping-scheme')
+ self.monitor_wrapping_scheme_menu.set_active_id(current_scheme)
+
+ def _handle_monitor_wrapping_scheme_menu_changed(self, widget):
+ self.settings.set_string('monitor-wrapping-scheme', widget.get_active_id())
+
def _handle_enabled_features(self, state_manager, val):
enabled_breezy_features = [feature for feature in state_manager.get_property('enabled-features-list') if feature in BREEZY_GNOME_FEATURES]
breezy_features_granted = len(enabled_breezy_features) > 0
diff --git a/ui/src/gtk/connected-device.ui b/ui/src/gtk/connected-device.ui
index d83ad11..3b73751 100644
--- a/ui/src/gtk/connected-device.ui
+++ b/ui/src/gtk/connected-device.ui
@@ -152,6 +152,30 @@
+
+
+