Add advanced settings
This commit is contained in:
parent
7936717ac7
commit
3433885cdd
|
|
@ -8,7 +8,7 @@ import St from 'gi://St';
|
|||
import { CursorManager } from './cursormanager.js';
|
||||
import Globals from './globals.js';
|
||||
import { Logger } from './logger.js';
|
||||
import MonitorManager from './monitormanager.js';
|
||||
import { MonitorManager } from './monitormanager.js';
|
||||
import { isValidKeepAlive } from './time.js';
|
||||
import { IPC_FILE_PATH, XREffect } from './xrEffect.js';
|
||||
|
||||
|
|
@ -48,6 +48,9 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this._end_binding = null;
|
||||
this._curved_display_binding = null;
|
||||
this._display_size_binding = null;
|
||||
this._look_ahead_override_binding = null;
|
||||
this._optimal_monitor_config_binding = null;
|
||||
this._headset_as_primary_binding = null;
|
||||
|
||||
if (!Globals.logger) {
|
||||
Globals.logger = new Logger({
|
||||
|
|
@ -65,10 +68,19 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
Globals.extension_dir = this.path;
|
||||
this.settings.bind('debug', Globals.logger, 'debug', Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
this._monitor_manager = new MonitorManager(this.path);
|
||||
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'),
|
||||
extension_path: this.path
|
||||
});
|
||||
this._monitor_manager.setChangeHook(this._handle_monitor_change.bind(this));
|
||||
this._monitor_manager.enable();
|
||||
|
||||
this._optimal_monitor_config_binding = this.settings.bind('use-optimal-monitor-config',
|
||||
this._monitor_manager, 'use-optimal-monitor-config', Gio.SettingsBindFlags.DEFAULT);
|
||||
this._headset_as_primary_binding = this.settings.bind('headset-as-primary',
|
||||
this._monitor_manager, 'headset-as-primary', Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
this._setup();
|
||||
} catch (e) {
|
||||
Globals.logger.log(`ERROR: BreezyDesktopExtension enable ${e.message}\n${e.stack}`);
|
||||
|
|
@ -207,6 +219,7 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
display_distance: this.settings.get_double('display-distance'),
|
||||
toggle_display_distance_start: this.settings.get_double('toggle-display-distance-start'),
|
||||
toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'),
|
||||
look_ahead_override: this.settings.get_double('look-ahead-override'),
|
||||
});
|
||||
|
||||
this._update_follow_threshold(this.settings);
|
||||
|
|
@ -223,6 +236,7 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this._end_binding = this.settings.bind('toggle-display-distance-end', this._xr_effect, 'toggle-display-distance-end', Gio.SettingsBindFlags.DEFAULT)
|
||||
this._curved_display_binding = this.settings.bind('curved-display', this._xr_effect, 'curved-display', Gio.SettingsBindFlags.DEFAULT)
|
||||
this._display_size_binding = this.settings.bind('display-size', this._xr_effect, 'display-size', Gio.SettingsBindFlags.DEFAULT);
|
||||
this._look_ahead_override_binding = this.settings.bind('look-ahead-override', this._xr_effect, 'look-ahead-override', Gio.SettingsBindFlags.DEFAULT);
|
||||
|
||||
this._overlay.add_effect_with_name('xr-desktop', this._xr_effect);
|
||||
Meta.disable_unredirect_for_display(global.display);
|
||||
|
|
@ -384,6 +398,10 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this.settings.unbind(this._display_size_binding);
|
||||
this._display_size_binding = null;
|
||||
}
|
||||
if (this._look_ahead_override_binding) {
|
||||
this.settings.unbind(this._look_ahead_override_binding);
|
||||
this._look_ahead_override_binding = null;
|
||||
}
|
||||
if (this._xr_effect) {
|
||||
if (this._widescreen_mode_effect_state_connection) {
|
||||
this._xr_effect.disconnect(this._widescreen_mode_effect_state_connection);
|
||||
|
|
@ -412,6 +430,15 @@ export default class BreezyDesktopExtension extends Extension {
|
|||
this._effect_disable();
|
||||
this._target_monitor = null;
|
||||
if (this._monitor_manager) {
|
||||
if (this._optimal_monitor_config_binding) {
|
||||
this.settings.unbind(this._optimal_monitor_config_binding);
|
||||
this._optimal_monitor_config_binding = null
|
||||
}
|
||||
if (this._headset_as_primary_binding) {
|
||||
this.settings.unbind(this._headset_as_primary_binding);
|
||||
this._headset_as_primary_binding = null;
|
||||
}
|
||||
|
||||
this._monitor_manager.disable();
|
||||
this._monitor_manager = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
|
||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
|
||||
|
|
@ -85,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, callback) {
|
||||
function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPrimary, callback) {
|
||||
Globals.logger.log_debug(`monitormanager.js performOptimalModeCheck for ${connectorName}`);
|
||||
displayConfigProxy.GetCurrentStateRemote((result, error) => {
|
||||
if (error) {
|
||||
|
|
@ -139,12 +140,16 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, callback) {
|
|||
const [x, y, scale, transform, primary, monitors, logMonProperties] = logicalMonitor;
|
||||
const hasOurMonitor = !!monitors.some((monitor) => monitor[0] === connectorName);
|
||||
anyMonitorsChanged |= hasOurMonitor && bestFitMode.bestScale !== scale;
|
||||
|
||||
// there can only be one primary monitor, so we need to set all other monitors to not primary and glasses to primary,
|
||||
// if headsetAsPrimary is true
|
||||
anyMonitorsChanged |= headsetAsPrimary && ((hasOurMonitor && !primary) || (!hasOurMonitor && primary));
|
||||
return [
|
||||
x,
|
||||
y,
|
||||
hasOurMonitor ? bestFitMode.bestScale : scale,
|
||||
transform,
|
||||
primary, // TODO - user setting should dictate if we want to set ours primary
|
||||
headsetAsPrimary ? hasOurMonitor : primary,
|
||||
monitors.map((monitor) => {
|
||||
const monitorConnector = monitor[0];
|
||||
const isOurMonitor = monitorConnector === connectorName;
|
||||
|
|
@ -185,22 +190,46 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, callback) {
|
|||
}
|
||||
|
||||
// Monitor change handling
|
||||
export default class MonitorManager {
|
||||
constructor(extPath) {
|
||||
this._extPath = extPath;
|
||||
export const MonitorManager = GObject.registerClass({
|
||||
Properties: {
|
||||
'use-optimal-monitor-config': GObject.ParamSpec.boolean(
|
||||
'use-optimal-monitor-config',
|
||||
'Use optimal monitor configuration',
|
||||
'Automatically set the optimal monitor configuration upon connection',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
true
|
||||
),
|
||||
'headset-as-primary': GObject.ParamSpec.boolean(
|
||||
'headset-as-primary',
|
||||
'Use headset as primary monitor',
|
||||
'Automatically set the headset as the primary display upon connection',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
true
|
||||
),
|
||||
'extension-path': GObject.ParamSpec.string(
|
||||
'extension-path',
|
||||
'Extension path',
|
||||
'Path to the extension directory',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
''
|
||||
)
|
||||
}
|
||||
}, class MonitorManager extends GObject.Object {
|
||||
constructor(params = {}) {
|
||||
super(params);
|
||||
|
||||
this._monitorsChangedConnection = null;
|
||||
this._displayConfigProxy = null;
|
||||
this._backendManager = null;
|
||||
this._monitorProperties = null;
|
||||
this._changeHookFn = null;
|
||||
this._needsConfigCheck = true;
|
||||
this._needsConfigCheck = this.use_optimal_monitor_config;
|
||||
}
|
||||
|
||||
enable() {
|
||||
Globals.logger.log_debug('MonitorManager enable');
|
||||
this._backendManager = global.backend.get_monitor_manager();
|
||||
newDisplayConfig(this._extPath, ((proxy, error) => {
|
||||
newDisplayConfig(this.extension_path, ((proxy, error) => {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -243,7 +272,7 @@ export default class MonitorManager {
|
|||
}
|
||||
|
||||
if (this._needsConfigCheck) {
|
||||
performOptimalModeCheck(this._displayConfigProxy, monitorConnector, ((configChanged, error) => {
|
||||
performOptimalModeCheck(this._displayConfigProxy, monitorConnector, this.headset_as_primary, ((configChanged, error) => {
|
||||
this._needsConfigCheck = false;
|
||||
if (error) {
|
||||
Globals.logger.log(`Failed to switch to optimal mode for monitor ${monitorConnector}: ${error}`);
|
||||
|
|
@ -269,7 +298,7 @@ export default class MonitorManager {
|
|||
if (this._displayConfigProxy == null) {
|
||||
return;
|
||||
}
|
||||
this._needsConfigCheck = true;
|
||||
this._needsConfigCheck = this.use_optimal_monitor_config;
|
||||
getMonitorConfig(this._displayConfigProxy, ((result, error) => {
|
||||
if (error) {
|
||||
return;
|
||||
|
|
@ -297,4 +326,4 @@ export default class MonitorManager {
|
|||
}
|
||||
}).bind(this));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -258,6 +258,15 @@ export const XREffect = GObject.registerClass({
|
|||
'The state of widescreen mode from the perspective of the driver',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
false
|
||||
),
|
||||
'look-ahead-override': GObject.ParamSpec.int(
|
||||
'look-ahead-override',
|
||||
'Look ahead override',
|
||||
'Override the look ahead value',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
-1,
|
||||
45,
|
||||
-1
|
||||
)
|
||||
}
|
||||
}, class XREffect extends Shell.GLSLEffect {
|
||||
|
|
@ -342,7 +351,8 @@ export const XREffect = GObject.registerClass({
|
|||
if (this._dataView.byteLength === DATA_VIEW_LENGTH) {
|
||||
if (checkParityByte(this._dataView)) {
|
||||
setSingleFloat(this, 'display_north_offset', this.display_distance);
|
||||
setSingleFloat(this, 'look_ahead_ms', lookAheadMS(this._dataView));
|
||||
setSingleFloat(this, 'look_ahead_ms',
|
||||
this.look_ahead_override === -1 ? lookAheadMS(this._dataView) : this.look_ahead_override);
|
||||
setUniformMatrix(this, 'imu_quat_data', 4, this._dataView, IMU_QUAT_DATA);
|
||||
setSingleFloat(this, 'display_size', this.display_size);
|
||||
success = true;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,33 @@
|
|||
Enable curved display mode
|
||||
</description>
|
||||
</key>
|
||||
<key name="look-ahead-override" type="i">
|
||||
<default>
|
||||
-1
|
||||
</default>
|
||||
<summary>Look-ahead override</summary>
|
||||
<description>
|
||||
Manually override the look-ahead calculation
|
||||
</description>
|
||||
</key>
|
||||
<key name="use-optimal-monitor-config" type="b">
|
||||
<default>
|
||||
true
|
||||
</default>
|
||||
<summary>Use optimal monitor configuration</summary>
|
||||
<description>
|
||||
Automatically set the optimal monitor configuration upon connection
|
||||
</description>
|
||||
</key>
|
||||
<key name="headset-as-primary" type="b">
|
||||
<default>
|
||||
true
|
||||
</default>
|
||||
<summary>Headset as primary</summary>
|
||||
<description>
|
||||
Automatically set the headset as the primary display upon connection
|
||||
</description>
|
||||
</key>
|
||||
<key name="developer-mode" type="b">
|
||||
<default>
|
||||
false
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ class ConnectedDevice(Gtk.Box):
|
|||
toggle_display_distance_shortcut_label = Gtk.Template.Child()
|
||||
reassign_toggle_follow_shortcut_button = Gtk.Template.Child()
|
||||
toggle_follow_shortcut_label = Gtk.Template.Child()
|
||||
headset_as_primary_switch = Gtk.Template.Child()
|
||||
use_optimal_monitor_config_switch = Gtk.Template.Child()
|
||||
movement_look_ahead_scale = Gtk.Template.Child()
|
||||
movement_look_ahead_adjustment = Gtk.Template.Child()
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super(Gtk.Box, self).__init__()
|
||||
|
|
@ -44,7 +49,10 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.set_toggle_display_distance_end_button,
|
||||
self.reassign_recenter_display_shortcut_button,
|
||||
self.reassign_toggle_display_distance_shortcut_button,
|
||||
self.reassign_toggle_follow_shortcut_button
|
||||
self.reassign_toggle_follow_shortcut_button,
|
||||
self.headset_as_primary_switch,
|
||||
self.use_optimal_monitor_config_switch,
|
||||
self.movement_look_ahead_scale
|
||||
]
|
||||
|
||||
self.settings = SettingsManager.get_instance().settings
|
||||
|
|
@ -56,6 +64,9 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.settings.bind('follow-threshold', self.follow_threshold_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
|
||||
self.settings.bind('widescreen-mode', self.widescreen_mode_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('use-optimal-monitor-config', self.use_optimal_monitor_config_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(), [
|
||||
[self.reassign_recenter_display_shortcut_button, self.recenter_display_shortcut_label],
|
||||
|
|
@ -78,8 +89,11 @@ class ConnectedDevice(Gtk.Box):
|
|||
self.effect_enable_switch.set_active(self._is_config_enabled(self.ipc.retrieve_config()) and self.extensions_manager.is_enabled())
|
||||
self.effect_enable_switch.connect('notify::active', self._refresh_inputs_for_enabled_state)
|
||||
|
||||
self.use_optimal_monitor_config_switch.connect('notify::active', self._refresh_use_optimal_monitor_config)
|
||||
|
||||
self._handle_enabled_features(self.state_manager, None)
|
||||
self._refresh_inputs_for_enabled_state(self.effect_enable_switch, None)
|
||||
self._refresh_use_optimal_monitor_config(self.use_optimal_monitor_config_switch, None)
|
||||
self.extensions_manager.bind_property('breezy-enabled', self.effect_enable_switch, 'active', GObject.BindingFlags.BIDIRECTIONAL)
|
||||
|
||||
self.connect("destroy", self._on_widget_destroy)
|
||||
|
|
@ -120,6 +134,11 @@ class ConnectedDevice(Gtk.Box):
|
|||
'enable_breezy_desktop_smooth_follow': switch.get_active()
|
||||
})
|
||||
|
||||
def _refresh_use_optimal_monitor_config(self, switch, param):
|
||||
self.headset_as_primary_switch.set_sensitive(switch.get_active())
|
||||
if not switch.get_active():
|
||||
self.headset_as_primary_switch.set_active(False)
|
||||
|
||||
def set_device_name(self, name):
|
||||
self.device_label.set_markup(f"<b>{name}</b>")
|
||||
|
||||
|
|
|
|||
|
|
@ -209,14 +209,13 @@
|
|||
<child>
|
||||
<object class="AdwViewStackPage">
|
||||
<property name="name">shortcuts</property>
|
||||
<property name="title">Shortcuts</property>
|
||||
<property name="title">Keyboard Shortcuts</property>
|
||||
<property name="icon-name">preferences-desktop-keyboard-shortcuts-symbolic</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="true">Shortcuts</property>
|
||||
<property name="description" translatable="true">Modify keyboard shortcuts and how they work.</property>
|
||||
<property name="title" translatable="true">Keyboard Shortcuts</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="true">Re-center display shortcut</property>
|
||||
|
|
@ -310,6 +309,75 @@
|
|||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwViewStackPage">
|
||||
<property name="name">advanced</property>
|
||||
<property name="title">Advanced Settings</property>
|
||||
<property name="icon-name">applications-system-symbolic</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="title" translatable="true">Advanced Settings</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="true">Find optimal display config</property>
|
||||
<property name="subtitle" translatable="true">Automatically modify the glasses display configuration for maximum resolution and best scaling when plugged in.</property>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="use_optimal_monitor_config_switch">
|
||||
<property name="valign">3</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="true">Always primary display</property>
|
||||
<property name="subtitle" translatable="true">Automatically set the glasses as the primary display when plugged in.</property>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="headset_as_primary_switch">
|
||||
<property name="valign">3</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="true">Movement look-ahead</property>
|
||||
<property name="subtitle" translatable="true">Counteracts input lag by predicting head-tracking position ahead of render time. Stick with default unless virtual display drags behind your head movements, jumps ahead, or is very shaky.</property>
|
||||
<child>
|
||||
<object class="GtkScale" id="movement_look_ahead_scale">
|
||||
<property name="valign">3</property>
|
||||
<property name="draw-value">false</property>
|
||||
<property name="value-pos">0</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="width-request">350</property>
|
||||
<property name="has-origin">false</property>
|
||||
<property name="adjustment">
|
||||
<object class="GtkAdjustment" id="movement_look_ahead_adjustment">
|
||||
<property name="lower">-1</property>
|
||||
<property name="upper">40</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="value">-1</property>
|
||||
</object>
|
||||
</property>
|
||||
<marks>
|
||||
<mark value="-1" position="bottom">Default</mark>
|
||||
<mark value="10" position="bottom">10ms</mark>
|
||||
<mark value="20" position="bottom">20ms</mark>
|
||||
<mark value="30" position="bottom">30ms</mark>
|
||||
<mark value="40" position="bottom">40ms</mark>
|
||||
</marks>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
|
|
|||
Loading…
Reference in New Issue