From 57a659b2a34d50be27bae1df723ee0cd16680879 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:12:30 -0700 Subject: [PATCH] Fix widescreen mode syncing with settings, and remove the restrictions between distance and size --- gnome/src/cursormanager.js | 18 +++++++++- gnome/src/extension.js | 69 +++++++++++++++++++++----------------- gnome/src/xrEffect.js | 17 ++++++++-- modules/sombrero | 2 +- ui/src/connecteddevice.py | 1 + ui/src/statemanager.py | 2 +- 6 files changed, 74 insertions(+), 35 deletions(-) diff --git a/gnome/src/cursormanager.js b/gnome/src/cursormanager.js index 852029c..d996dd0 100644 --- a/gnome/src/cursormanager.js +++ b/gnome/src/cursormanager.js @@ -1,4 +1,5 @@ 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'; @@ -27,6 +28,7 @@ export class CursorManager { this._cursorWatch = null; this._cursorChangedConnection = null; this._cursorVisibilityChangedConnection = null; + this._moveToTopTimeout = null; } enable() { @@ -132,6 +134,15 @@ export class CursorManager { this._cursorChangedConnection = this._cursorTracker.connect('cursor-changed', this._updateMouseSprite.bind(this)); this._cursorVisibilityChangedConnection = this._cursorTracker.connect('visibility-changed', this._handleVisibilityChanged.bind(this)); + // Some elements will occasionally appear above the cursor, so we periodically reset the actor stacking. + // This could theoretically be fixed "better" by attaching to all events that might affect actor ordering, + // but finding a comprehensive list is difficult and not future proof. So this ugly solution helps us + // catch everything. + this._moveToTopTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, (() => { + this._moveToTop() + return GLib.SOURCE_CONTINUE; + }).bind(this)); + const interval = 1000 / this._refreshRate; this._cursorWatch = this._cursorWatcher.addWatch(interval, this._updateMousePosition.bind(this)); @@ -178,6 +189,11 @@ export class CursorManager { } else { this._mainActor.remove_actor(this._cursorActor); } + + if (this._moveToTopTimeout) { + GLib.source_remove(this._moveToTopTimeout); + this._moveToTopTimeout = null; + } } if (this._cursorUnfocusInhibited) { @@ -217,7 +233,7 @@ export class CursorManager { this._cursorTrackerSetPointerVisibleBound(false); } - handleStageChildAdded() { + _moveToTop() { this._mainActor.set_child_above_sibling(this._cursorActor, null); } } \ No newline at end of file diff --git a/gnome/src/extension.js b/gnome/src/extension.js index da464f1..c557c50 100644 --- a/gnome/src/extension.js +++ b/gnome/src/extension.js @@ -40,10 +40,10 @@ export default class BreezyDesktopExtension extends Extension { this._distance_binding = null; this._distance_connection = null; this._follow_threshold_connection = null; - this._widescreen_mode_connection = null; + this._widescreen_mode_settings_connection = null; + this._widescreen_mode_effect_state_connection = null; this._start_binding = null; this._end_binding = null; - this._stage_child_connection = null; this._curved_display_binding = null; this._display_size_binding = null; @@ -69,7 +69,7 @@ export default class BreezyDesktopExtension extends Extension { this._setup(); } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension enable ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension enable ${e.message}\n${e.stack}`); } } @@ -113,7 +113,7 @@ export default class BreezyDesktopExtension extends Extension { return null; } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension _find_supported_monitor ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension _find_supported_monitor ${e.message}\n${e.stack}`); return null; } } @@ -129,7 +129,9 @@ export default class BreezyDesktopExtension extends Extension { // if target_monitor isn't set, do nothing and wait for MonitorManager to call this again if (target_monitor && this._running_poller_id === undefined) { this._target_monitor = target_monitor.monitor; - this._refresh_rate = target_monitor.refreshRate; + + // have everything target a slightly higher refresh rate to avoid stuttering + this._refresh_rate = target_monitor.refreshRate * 1.05; if (this._check_driver_running()) { Globals.logger.log('Ready, enabling XR effect'); @@ -145,7 +147,7 @@ export default class BreezyDesktopExtension extends Extension { if (!Globals.ipc_file) Globals.ipc_file = Gio.file_new_for_path(IPC_FILE_PATH); return Globals.ipc_file.query_exists(null); } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension _check_driver_running ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension _check_driver_running ${e.message}\n${e.stack}`); return false; } } @@ -187,24 +189,19 @@ export default class BreezyDesktopExtension extends Extension { toggle_display_distance_start: this.settings.get_double('toggle-display-distance-start'), toggle_display_distance_end: this.settings.get_double('toggle-display-distance-end'), }); + this._widescreen_mode_effect_state_connection = this._xr_effect.connect('notify::widescreen-mode-state', this._update_widescreen_mode_from_state.bind(this)); + this._update_widescreen_mode_from_state(this._xr_effect, this._xr_effect.widescreen_mode_state); - this._update_display_distance(this.settings); this._update_follow_threshold(this.settings); - this._update_widescreen_mode(this.settings); this._distance_binding = this.settings.bind('display-distance', this._xr_effect, '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)) - this._widescreen_mode_connection = this.settings.connect('changed::widescreen-mode', this._update_widescreen_mode.bind(this)) + this._widescreen_mode_settings_connection = this.settings.connect('changed::widescreen-mode', this._update_widescreen_mode_from_settings.bind(this)) this._start_binding = this.settings.bind('toggle-display-distance-start', this._xr_effect, 'toggle-display-distance-start', Gio.SettingsBindFlags.DEFAULT) 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('widescreen-display-size', this._xr_effect, 'widescreen-display-size', Gio.SettingsBindFlags.DEFAULT) - if (Clutter.Container === undefined) { - this._stage_child_connection = global.stage.connect('child-added', this._cursor_manager.handleStageChildAdded); - } else { - this._stage_child_connection = global.stage.connect('actor-added', this._cursor_manager.handleStageChildAdded); - } + this._display_size_binding = this.settings.bind('widescreen-display-size', this._xr_effect, 'widescreen-display-size', Gio.SettingsBindFlags.DEFAULT); this._overlay.add_effect_with_name('xr-desktop', this._xr_effect); Meta.disable_unredirect_for_display(global.display); @@ -213,7 +210,7 @@ export default class BreezyDesktopExtension extends Extension { this._add_settings_keybinding('toggle-display-distance-shortcut', this._xr_effect._change_distance.bind(this._xr_effect)); this._add_settings_keybinding('toggle-follow-shortcut', this._toggle_follow_mode.bind(this)); } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension _effect_enable ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension _effect_enable ${e.message}\n${e.stack}`); this._effect_disable(); } } @@ -244,11 +241,11 @@ export default class BreezyDesktopExtension extends Extension { bind_to_function ); } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension _add_settings_keybinding settings binding lambda ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension _add_settings_keybinding settings binding lambda ${e.message}\n${e.stack}`); } }); } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension _add_settings_keybinding ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension _add_settings_keybinding ${e.message}\n${e.stack}`); } } @@ -271,10 +268,22 @@ export default class BreezyDesktopExtension extends Extension { if (value !== undefined) this._write_control('breezy_desktop_follow_threshold', value); } - _update_widescreen_mode(settings, event) { + _update_widescreen_mode_from_settings(settings, event) { const value = settings.get_boolean('widescreen-mode'); - Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode ${value}`); - if (value !== undefined) this._write_control('sbs_mode', value ? 'enable' : 'disable'); + 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 + Globals.logger.log_debug('effect.widescreen_mode_state already matched setting'); + } + + _update_widescreen_mode_from_state(effect, _pspec) { + 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')) + this.settings.set_boolean('widescreen-mode', value); + else + Globals.logger.log_debug('widescreen-mode setting already matched state'); } _recenter_display() { @@ -318,9 +327,9 @@ export default class BreezyDesktopExtension extends Extension { this.settings.disconnect(this._follow_threshold_connection); this._follow_threshold_connection = null; } - if (this._widescreen_mode_connection) { - this.settings.disconnect(this._widescreen_mode_connection); - this._widescreen_mode_connection = null; + if (this._widescreen_mode_settings_connection) { + this.settings.disconnect(this._widescreen_mode_settings_connection); + this._widescreen_mode_settings_connection = null; } if (this._start_binding) { this.settings.unbind(this._start_binding); @@ -338,11 +347,11 @@ export default class BreezyDesktopExtension extends Extension { this.settings.unbind(this._display_size_binding); this._display_size_binding = null; } - if (this._stage_child_connection) { - global.stage.disconnect(this._stage_child_connection); - this._stage_child_connection = null; - } if (this._xr_effect) { + if (this._widescreen_mode_effect_state_connection) { + this._xr_effect.disconnect(this._widescreen_mode_effect_state_connection); + this._widescreen_mode_effect_state_connection = null; + } this._xr_effect.cleanup(); this._xr_effect = null; } @@ -352,7 +361,7 @@ export default class BreezyDesktopExtension extends Extension { this._cursor_manager = null; } } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension _effect_disable ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension _effect_disable ${e.message}\n${e.stack}`); } } @@ -366,7 +375,7 @@ export default class BreezyDesktopExtension extends Extension { this._monitor_manager = null; } } catch (e) { - Globals.logger.log(`ERROR: BreezyDesktopExtension disable ${e.message}`, e.stack); + Globals.logger.log(`ERROR: BreezyDesktopExtension disable ${e.message}\n${e.stack}`); } } } diff --git a/gnome/src/xrEffect.js b/gnome/src/xrEffect.js index 41f9ea3..1b1867a 100644 --- a/gnome/src/xrEffect.js +++ b/gnome/src/xrEffect.js @@ -147,12 +147,16 @@ function setIntermittentUniformVariables() { setSingleFloat(this, 'curved_display', this.curved_display ? 1.0 : 0.0); } + // update the widescreen property if the state changes, so notify events are triggered + const sbsEnabled = dataViewUint8(dataView, SBS_ENABLED) !== 0; + if (this.widescreen_mode_state !== sbsEnabled) this.widescreen_mode_state = sbsEnabled; + // these variables are always in play, even if enabled is false setSingleFloat(this, 'enabled', enabled ? 1.0 : 0.0); setSingleFloat(this, 'show_banner', imuResetState ? 1.0 : 0.0); setSingleFloat(this, 'sbs_content', 0.0); // TODO - drive from settings setSingleFloat(this, 'sbs_mode_stretched', 1.0); // content always fills the whole display - setSingleFloat(this, 'sbs_enabled', dataViewUint8(dataView, SBS_ENABLED) !== 0 ? 1.0 : 0.0); + setSingleFloat(this, 'sbs_enabled', sbsEnabled ? 1.0 : 0.0); setSingleFloat(this, 'custom_banner_enabled', dataViewUint8(dataView, CUSTOM_BANNER_ENABLED) !== 0 ? 1.0 : 0.0); this.set_uniform_float(shaderUniformLocations['display_resolution'], 2, displayRes); @@ -210,6 +214,13 @@ export const XREffect = GObject.registerClass({ GObject.ParamFlags.READWRITE, false ), + 'widescreen-mode-state': GObject.ParamSpec.boolean( + 'widescreen-mode-state', + 'Widescreen mode state', + 'The state of widescreen mode from the perspective of the driver', + GObject.ParamFlags.READWRITE, + false + ), 'widescreen-display-size': GObject.ParamSpec.double( 'widescreen-display-size', 'Widescreen display size', @@ -224,7 +235,9 @@ export const XREffect = GObject.registerClass({ constructor(params = {}) { super(params); - this._frametime = Math.floor(1000 / this.target_framerate); + // target a slightly higher framerate than the monitor's refresh rate to prevent stuttering + const frameTimeFramerate = this.target_framerate; + this._frametime = Math.floor(1000 / frameTimeFramerate); this._is_display_distance_at_end = false; this._distance_ease_timeline = null; diff --git a/modules/sombrero b/modules/sombrero index 3f712c2..7eea3e0 160000 --- a/modules/sombrero +++ b/modules/sombrero @@ -1 +1 @@ -Subproject commit 3f712c2aeffcae41d238e146acc97c145ab9647b +Subproject commit 7eea3e01b4cd03dc07fa03f3f5903e31fc8dffbc diff --git a/ui/src/connecteddevice.py b/ui/src/connecteddevice.py index 69ae5f3..ac5ddfc 100644 --- a/ui/src/connecteddevice.py +++ b/ui/src/connecteddevice.py @@ -108,6 +108,7 @@ class ConnectedDevice(Gtk.Box): self.display_distance_row.set_visible(not widescreen_mode_enabled) for widget in [self.widescreen_display_distance_scale, self.widescreen_display_size_scale]: widget.set_sensitive(widescreen_mode_enabled) + def _refresh_inputs_for_enabled_state(self, switch, param): requesting_enabled = switch.get_active() diff --git a/ui/src/statemanager.py b/ui/src/statemanager.py index 8e43fa7..8c2497f 100644 --- a/ui/src/statemanager.py +++ b/ui/src/statemanager.py @@ -95,7 +95,7 @@ class StateManager(GObject.GObject): self.set_property('license-present', False) self.set_property('follow-mode', self.state.get('breezy_desktop_smooth_follow_enabled')) - self.set_property('widescreen-mode', self.state.get('sbs_mode_enabled') == 'true') + self.set_property('widescreen-mode', self.state.get('sbs_mode_enabled')) if self.running: threading.Timer(1.0, self._refresh_state).start()