Add refresh rate and fast SBS mode switching settings

This commit is contained in:
wheaney 2024-07-17 21:55:49 -07:00
parent 7369cb0551
commit 04edf2eecc
8 changed files with 118 additions and 17 deletions

View File

@ -1,5 +1,4 @@
import Clutter from 'gi://Clutter'; import Clutter from 'gi://Clutter';
import GLib from 'gi://GLib';
import Meta from 'gi://Meta'; import Meta from 'gi://Meta';
import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js'; import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js';
import { MouseSpriteContent } from './cursor.js'; import { MouseSpriteContent } from './cursor.js';

View File

@ -71,6 +71,7 @@ export default class BreezyDesktopExtension extends Extension {
this._monitor_manager = new MonitorManager({ this._monitor_manager = new MonitorManager({
use_optimal_monitor_config: this.settings.get_boolean('use-optimal-monitor-config'), use_optimal_monitor_config: this.settings.get_boolean('use-optimal-monitor-config'),
headset_as_primary: this.settings.get_boolean('headset-as-primary'), 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 extension_path: this.path
}); });
this._monitor_manager.setChangeHook(this._handle_monitor_change.bind(this)); 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( const target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find(
monitor => SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product)); monitor => SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product));
if (target_monitor !== undefined) { 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 { return {
monitor: this._monitor_manager.getMonitors()[target_monitor.index], monitor: this._monitor_manager.getMonitors()[target_monitor.index],
connector: target_monitor.connector, 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 // A false result means we'll expect _handle_monitor_change to be triggered, so active polling
// can be disabled. // can be disabled.
_target_monitor_ready(target_monitor) { _target_monitor_ready(target_monitor) {
return target_monitor.is_dummy || if (target_monitor.is_dummy) return true;
!this._monitor_manager.needsOptimalModeCheck(target_monitor.connector);
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() { _setup() {
@ -213,7 +217,7 @@ export default class BreezyDesktopExtension extends Extension {
const widescreen_setting_enabled = this.settings.get_boolean('widescreen-mode'); const widescreen_setting_enabled = this.settings.get_boolean('widescreen-mode');
if (widescreen_setting_enabled !== sbs_enabled) { if (widescreen_setting_enabled !== sbs_enabled) {
Globals.logger.log_debug('BreezyDesktopExtension _needs_widescreen_monitor_update - true'); 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; return true;
} }
@ -259,7 +263,10 @@ export default class BreezyDesktopExtension extends Extension {
}); });
this._update_follow_threshold(this.settings); 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._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)); 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); 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) { _update_widescreen_mode_from_settings(settings, event) {
const value = settings.get_boolean('widescreen-mode'); const value = settings.get_boolean('widescreen-mode');
Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_settings ${value}`); Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_settings ${value}`);
if (value !== undefined && value !== this._xr_effect.widescreen_mode_state) if (value !== undefined && value !== this._xr_effect.widescreen_mode_state) {
this._write_control('sbs_mode', value ? 'enable' : 'disable'); this._request_sbs_mode_change(value);
else } else
Globals.logger.log_debug('effect.widescreen_mode_state already matched setting'); Globals.logger.log_debug('effect.widescreen_mode_state already matched setting');
} }
_update_widescreen_mode_from_state(effect, _pspec) { _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; const value = effect.widescreen_mode_state;
Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_state ${value}`); Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_state ${value}`);
if (value !== this.settings.get_boolean('widescreen-mode')) if (value !== this.settings.get_boolean('widescreen-mode'))

View File

@ -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 // 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}`); Globals.logger.log_debug(`monitormanager.js performOptimalModeCheck for ${connectorName}`);
displayConfigProxy.GetCurrentStateRemote((result, error) => { displayConfigProxy.GetCurrentStateRemote((result, error) => {
if (error) { if (error) {
@ -101,10 +101,19 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPri
let monitorToModeIdMap = {}; let monitorToModeIdMap = {};
let bestFitMode = undefined; let bestFitMode = undefined;
for (let monitor of monitors) { for (let monitor of monitors) {
const [details, modes, monProperties] = monitor; const [details, availableModes, monProperties] = monitor;
const [connector, vendor, product, monitorSerial] = details; const [connector, vendor, product, monitorSerial] = details;
const isOurMonitor = connector == connectorName; 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) { for (let mode of modes) {
const [modeId, width, height, refreshRate, preferredScale, supportedScales, modeProperites] = mode; const [modeId, width, height, refreshRate, preferredScale, supportedScales, modeProperites] = mode;
@ -199,6 +208,13 @@ export const MonitorManager = GObject.registerClass({
GObject.ParamFlags.READWRITE, GObject.ParamFlags.READWRITE,
true 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': GObject.ParamSpec.boolean(
'headset-as-primary', 'headset-as-primary',
'Use headset as primary monitor', 'Use headset as primary monitor',
@ -272,7 +288,7 @@ export const MonitorManager = GObject.registerClass({
} }
if (this._needsConfigCheck) { 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; this._needsConfigCheck = false;
if (error) { if (error) {
Globals.logger.log(`Failed to switch to optimal mode for monitor ${monitorConnector}: ${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++) { for (let i = 0; i < result.length; i++) {
const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i]; const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i];
const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName); 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) { if (monitorIndex >= 0) {
monitorProperties[monitorIndex] = { monitorProperties[monitorIndex] = {
index: monitorIndex, index: monitorIndex,

View File

@ -16,12 +16,12 @@ check_command "flatpak-builder"
# https://stackoverflow.com/a/246128 # https://stackoverflow.com/a/246128
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) 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 OUT_DIR=$SCRIPT_DIR/../out
rm -rf $OUT_DIR rm -rf $OUT_DIR
mkdir -p $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-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 flatpak build-bundle $TMP_DIR/export $OUT_DIR/com.xronlinux.BreezyDesktop.flatpak com.xronlinux.BreezyDesktop --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo

View File

@ -118,6 +118,24 @@
Automatically set the headset as the primary display upon connection Automatically set the headset as the primary display upon connection
</description> </description>
</key> </key>
<key name="use-highest-refresh-rate" type="b">
<default>
true
</default>
<summary>Use highest refresh rate</summary>
<description>
Automatically set the highest refresh rate upon connection
</description>
</key>
<key name="fast-sbs-mode-switching" type="b">
<default>
true
</default>
<summary>Fast SBS mode switching</summary>
<description>
Enable fast SBS mode switching
</description>
</key>
<key name="disable-anti-aliasing" type="b"> <key name="disable-anti-aliasing" type="b">
<default> <default>
false false

View File

@ -3,7 +3,13 @@
<id>com.xronlinux.BreezyDesktop.desktop</id> <id>com.xronlinux.BreezyDesktop.desktop</id>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-or-later</project_license> <project_license>GPL-3.0-or-later</project_license>
<name>Breezy Desktop</name>
<summary>XR Desktop Control Panel</summary>
<description> <description>
<p>No description</p> <p>XR Desktop Control Panel</p>
</description> </description>
<categories>
<category>Office</category>
<category>Development</category>
</categories>
</component> </component>

View File

@ -31,6 +31,8 @@ class ConnectedDevice(Gtk.Box):
toggle_follow_shortcut_label = Gtk.Template.Child() toggle_follow_shortcut_label = Gtk.Template.Child()
headset_as_primary_switch = Gtk.Template.Child() headset_as_primary_switch = Gtk.Template.Child()
use_optimal_monitor_config_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_scale = Gtk.Template.Child()
movement_look_ahead_adjustment = 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.reassign_toggle_follow_shortcut_button,
self.headset_as_primary_switch, self.headset_as_primary_switch,
self.use_optimal_monitor_config_switch, self.use_optimal_monitor_config_switch,
self.use_highest_refresh_rate_switch,
self.fast_sbs_mode_switch,
self.movement_look_ahead_scale 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('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('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-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) self.settings.bind('look-ahead-override', self.movement_look_ahead_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
bind_shortcut_settings(self.get_parent(), [ bind_shortcut_settings(self.get_parent(), [
@ -136,8 +142,10 @@ class ConnectedDevice(Gtk.Box):
def _refresh_use_optimal_monitor_config(self, switch, param): def _refresh_use_optimal_monitor_config(self, switch, param):
self.headset_as_primary_switch.set_sensitive(switch.get_active()) 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(): if not switch.get_active():
self.headset_as_primary_switch.set_active(False) self.headset_as_primary_switch.set_active(False)
self.use_highest_refresh_rate_switch.set_active(False)
def set_device_name(self, name): def set_device_name(self, name):
self.device_label.set_markup(f"<b>{name}</b>") self.device_label.set_markup(f"<b>{name}</b>")

View File

@ -328,6 +328,17 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Use highest refresh rate</property>
<property name="subtitle" translatable="true">Refresh rate may affect performance, disable this to set it manually.</property>
<child>
<object class="GtkSwitch" id="use_highest_refresh_rate_switch">
<property name="valign">3</property>
</object>
</child>
</object>
</child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow">
<property name="title" translatable="true">Always primary display</property> <property name="title" translatable="true">Always primary display</property>
@ -339,6 +350,17 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Fast SBS mode switching</property>
<property name="subtitle" translatable="true">Switches glasses to SBS mode immediately when plugged in, if widescreen mode is on. May cause instability.</property>
<child>
<object class="GtkSwitch" id="fast_sbs_mode_switch">
<property name="valign">3</property>
</object>
</child>
</object>
</child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow">
<property name="title" translatable="true">Movement look-ahead</property> <property name="title" translatable="true">Movement look-ahead</property>