diff --git a/gnome/src/cursormanager.js b/gnome/src/cursormanager.js
index c2f5e82..44b3f5f 100644
--- a/gnome/src/cursormanager.js
+++ b/gnome/src/cursormanager.js
@@ -1,5 +1,4 @@
import Clutter from 'gi://Clutter';
-import GLib from 'gi://GLib';
import Meta from 'gi://Meta';
import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js';
import { MouseSpriteContent } from './cursor.js';
diff --git a/gnome/src/extension.js b/gnome/src/extension.js
index 16958a5..a7edafd 100644
--- a/gnome/src/extension.js
+++ b/gnome/src/extension.js
@@ -71,6 +71,7 @@ export default class BreezyDesktopExtension extends Extension {
this._monitor_manager = new MonitorManager({
use_optimal_monitor_config: this.settings.get_boolean('use-optimal-monitor-config'),
headset_as_primary: this.settings.get_boolean('headset-as-primary'),
+ use_highest_refresh_rate: this.settings.get_boolean('use-highest-refresh-rate'),
extension_path: this.path
});
this._monitor_manager.setChangeHook(this._handle_monitor_change.bind(this));
@@ -120,7 +121,7 @@ export default class BreezyDesktopExtension extends Extension {
const target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find(
monitor => SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product));
if (target_monitor !== undefined) {
- Globals.logger.log_debug(`BreezyDesktopExtension _find_supported_monitor - Identified supported monitor: ${target_monitor.connector}`);
+ 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,
@@ -151,8 +152,11 @@ export default class BreezyDesktopExtension extends Extension {
// A false result means we'll expect _handle_monitor_change to be triggered, so active polling
// can be disabled.
_target_monitor_ready(target_monitor) {
- return target_monitor.is_dummy ||
- !this._monitor_manager.needsOptimalModeCheck(target_monitor.connector);
+ if (target_monitor.is_dummy) return true;
+
+ const needs_sbs_mode_switch = this.settings.get_boolean('fast-sbs-mode-switching') &&
+ this._needs_widescreen_monitor_update();
+ return !needs_sbs_mode_switch && !this._monitor_manager.needsOptimalModeCheck(target_monitor.connector);
}
_setup() {
@@ -213,7 +217,7 @@ export default class BreezyDesktopExtension extends Extension {
const widescreen_setting_enabled = this.settings.get_boolean('widescreen-mode');
if (widescreen_setting_enabled !== sbs_enabled) {
Globals.logger.log_debug('BreezyDesktopExtension _needs_widescreen_monitor_update - true');
- this._write_control('sbs_mode', widescreen_setting_enabled ? 'enable' : 'disable');
+ this._request_sbs_mode_change(widescreen_setting_enabled);
return true;
}
@@ -259,7 +263,10 @@ export default class BreezyDesktopExtension extends Extension {
});
this._update_follow_threshold(this.settings);
- this._update_widescreen_mode_from_settings(this.settings);
+
+ // this gets triggered before _effect_enable if in fast-sbs-mode-switching mode
+ if (!this.settings.get_boolean('fast-sbs-mode-switching'))
+ this._update_widescreen_mode_from_settings(this.settings);
this._widescreen_mode_effect_state_connection = this._xr_effect.connect('notify::widescreen-mode-state', this._update_widescreen_mode_from_state.bind(this));
this._supported_device_detected_connected = this._xr_effect.connect('notify::supported-device-detected', this._handle_supported_device_change.bind(this));
@@ -362,16 +369,41 @@ export default class BreezyDesktopExtension extends Extension {
if (value !== undefined) this._write_control('breezy_desktop_follow_threshold', value);
}
+ // requests sbs_mode change and monitors to ensure the state reflects the setting
+ _request_sbs_mode_change(value) {
+ this._write_control('sbs_mode', value ? 'enable' : 'disable');
+ if (!this._sbs_mode_update_timeout) {
+ var attempts = 0;
+ this._sbs_mode_update_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 10000, (() => {
+ if (attempts++ < 3) {
+ this._write_control('sbs_mode', value ? 'enable' : 'disable');
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ // the state never updated to reflect our request, revert the setting
+ this.settings.set_boolean('widescreen-mode', !value);
+ this._sbs_mode_update_timeout = undefined;
+ return GLib.SOURCE_REMOVE;
+ }).bind(this));
+ }
+ }
+
_update_widescreen_mode_from_settings(settings, event) {
const value = settings.get_boolean('widescreen-mode');
Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_settings ${value}`);
- if (value !== undefined && value !== this._xr_effect.widescreen_mode_state)
- this._write_control('sbs_mode', value ? 'enable' : 'disable');
- else
+ if (value !== undefined && value !== this._xr_effect.widescreen_mode_state) {
+ this._request_sbs_mode_change(value);
+ } else
Globals.logger.log_debug('effect.widescreen_mode_state already matched setting');
}
_update_widescreen_mode_from_state(effect, _pspec) {
+ // kill our state checker if it's running
+ if (this._sbs_mode_update_timeout) {
+ GLib.source_remove(this._sbs_mode_update_timeout);
+ this._sbs_mode_update_timeout = undefined;
+ }
+
const value = effect.widescreen_mode_state;
Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_state ${value}`);
if (value !== this.settings.get_boolean('widescreen-mode'))
diff --git a/gnome/src/monitormanager.js b/gnome/src/monitormanager.js
index 8126ecf..1d4b98c 100644
--- a/gnome/src/monitormanager.js
+++ b/gnome/src/monitormanager.js
@@ -86,7 +86,7 @@ function getMonitorConfig(displayConfigProxy, callback) {
}
// triggers callback with true result if an an async monitor config change was triggered, false if no config change needed
-function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPrimary, callback) {
+function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPrimary, useHighestRefreshRate, callback) {
Globals.logger.log_debug(`monitormanager.js performOptimalModeCheck for ${connectorName}`);
displayConfigProxy.GetCurrentStateRemote((result, error) => {
if (error) {
@@ -101,10 +101,19 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPri
let monitorToModeIdMap = {};
let bestFitMode = undefined;
for (let monitor of monitors) {
- const [details, modes, monProperties] = monitor;
+ const [details, availableModes, monProperties] = monitor;
const [connector, vendor, product, monitorSerial] = details;
const isOurMonitor = connector == connectorName;
- if (isOurMonitor) ourMonitor = monitor;
+ let modes = availableModes;
+ if (isOurMonitor) {
+ ourMonitor = monitor;
+ if (!useHighestRefreshRate) {
+ const currentMode = modes.find((mode) => !!mode[6]['is-current']);
+
+ // filter modes to only include the current refresh rate
+ modes = availableModes.filter((mode) => mode[3] === currentMode[3]);
+ }
+ }
for (let mode of modes) {
const [modeId, width, height, refreshRate, preferredScale, supportedScales, modeProperites] = mode;
@@ -199,6 +208,13 @@ export const MonitorManager = GObject.registerClass({
GObject.ParamFlags.READWRITE,
true
),
+ 'use-highest-refresh-rate': GObject.ParamSpec.boolean(
+ 'use-highest-refresh-rate',
+ 'Use highest refresh rate',
+ 'Set the highest refresh rate which choosing optimal configs',
+ GObject.ParamFlags.READWRITE,
+ true
+ ),
'headset-as-primary': GObject.ParamSpec.boolean(
'headset-as-primary',
'Use headset as primary monitor',
@@ -272,7 +288,7 @@ export const MonitorManager = GObject.registerClass({
}
if (this._needsConfigCheck) {
- performOptimalModeCheck(this._displayConfigProxy, monitorConnector, this.headset_as_primary, ((configChanged, error) => {
+ performOptimalModeCheck(this._displayConfigProxy, monitorConnector, this.headset_as_primary, this.use_highest_refresh_rate, ((configChanged, error) => {
this._needsConfigCheck = false;
if (error) {
Globals.logger.log(`Failed to switch to optimal mode for monitor ${monitorConnector}: ${error}`);
@@ -309,7 +325,7 @@ export const MonitorManager = GObject.registerClass({
for (let i = 0; i < result.length; i++) {
const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i];
const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName);
- Globals.logger.log(`Found monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}`);
+ Globals.logger.log_debug(`Found monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}`);
if (monitorIndex >= 0) {
monitorProperties[monitorIndex] = {
index: monitorIndex,
diff --git a/ui/bin/package b/ui/bin/package
index a3c6764..7b04f59 100755
--- a/ui/bin/package
+++ b/ui/bin/package
@@ -16,12 +16,12 @@ check_command "flatpak-builder"
# https://stackoverflow.com/a/246128
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
-TMP_DIR=$(mktemp -d -t breezy-ui-flatpak-XXXXXXXXXX)
+TMP_DIR=$(mktemp -d -t --tmpdir=$SCRIPT_DIR/.. breezy-ui-flatpak-XXXXXXXXXX)
OUT_DIR=$SCRIPT_DIR/../out
rm -rf $OUT_DIR
mkdir -p $OUT_DIR
-flatpak-builder --force-clean $TMP_DIR/build $SCRIPT_DIR/../com.xronlinux.BreezyDesktop.json
+flatpak-builder --force-clean --delete-build-dirs $TMP_DIR/build $SCRIPT_DIR/../com.xronlinux.BreezyDesktop.json
flatpak build-export $TMP_DIR/export $TMP_DIR/build
flatpak build-bundle $TMP_DIR/export $OUT_DIR/com.xronlinux.BreezyDesktop.flatpak com.xronlinux.BreezyDesktop --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
diff --git a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
index 7b33ad7..4372563 100644
--- a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
+++ b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
@@ -118,6 +118,24 @@
Automatically set the headset as the primary display upon connection
+
+
+ true
+
+ Use highest refresh rate
+
+ Automatically set the highest refresh rate upon connection
+
+
+
+
+ true
+
+ Fast SBS mode switching
+
+ Enable fast SBS mode switching
+
+
false
diff --git a/ui/data/com.xronlinux.BreezyDesktop.metainfo.xml.in b/ui/data/com.xronlinux.BreezyDesktop.metainfo.xml.in
index eab0852..b16137d 100644
--- a/ui/data/com.xronlinux.BreezyDesktop.metainfo.xml.in
+++ b/ui/data/com.xronlinux.BreezyDesktop.metainfo.xml.in
@@ -3,7 +3,13 @@
com.xronlinux.BreezyDesktop.desktop
CC0-1.0
GPL-3.0-or-later
+ Breezy Desktop
+ XR Desktop Control Panel
- No description
+ XR Desktop Control Panel
+
+ Office
+ Development
+
diff --git a/ui/src/connecteddevice.py b/ui/src/connecteddevice.py
index 241c4d5..133d2c2 100644
--- a/ui/src/connecteddevice.py
+++ b/ui/src/connecteddevice.py
@@ -31,6 +31,8 @@ class ConnectedDevice(Gtk.Box):
toggle_follow_shortcut_label = Gtk.Template.Child()
headset_as_primary_switch = Gtk.Template.Child()
use_optimal_monitor_config_switch = Gtk.Template.Child()
+ use_highest_refresh_rate_switch = Gtk.Template.Child()
+ fast_sbs_mode_switch = Gtk.Template.Child()
movement_look_ahead_scale = Gtk.Template.Child()
movement_look_ahead_adjustment = Gtk.Template.Child()
@@ -52,6 +54,8 @@ class ConnectedDevice(Gtk.Box):
self.reassign_toggle_follow_shortcut_button,
self.headset_as_primary_switch,
self.use_optimal_monitor_config_switch,
+ self.use_highest_refresh_rate_switch,
+ self.fast_sbs_mode_switch,
self.movement_look_ahead_scale
]
@@ -66,6 +70,8 @@ class ConnectedDevice(Gtk.Box):
self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('headset-as-primary', self.headset_as_primary_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('use-optimal-monitor-config', self.use_optimal_monitor_config_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
+ 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)
bind_shortcut_settings(self.get_parent(), [
@@ -136,8 +142,10 @@ class ConnectedDevice(Gtk.Box):
def _refresh_use_optimal_monitor_config(self, switch, param):
self.headset_as_primary_switch.set_sensitive(switch.get_active())
+ self.use_highest_refresh_rate_switch.set_sensitive(switch.get_active())
if not switch.get_active():
self.headset_as_primary_switch.set_active(False)
+ self.use_highest_refresh_rate_switch.set_active(False)
def set_device_name(self, name):
self.device_label.set_markup(f"{name}")
diff --git a/ui/src/gtk/connected-device.ui b/ui/src/gtk/connected-device.ui
index 215ca1c..b5953c3 100644
--- a/ui/src/gtk/connected-device.ui
+++ b/ui/src/gtk/connected-device.ui
@@ -328,6 +328,17 @@
+
+
+
Always primary display
@@ -339,6 +350,17 @@
+
+
+ Fast SBS mode switching
+ Switches glasses to SBS mode immediately when plugged in, if widescreen mode is on. May cause instability.
+
+
+ 3
+
+
+
+
Movement look-ahead