From e50a3f1c2b2f4f0d599088dc7d641740f042ec42 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Thu, 9 May 2024 09:55:58 -0700 Subject: [PATCH] Add follow mode support --- .gitignore | 1 + .../breezydesktop@xronlinux.com/extension.js | 13 +++++- ui/build-aux/start-breezy-desktop.sh | 3 ++ .../com.xronlinux.BreezyDesktop.desktop.in | 2 +- .../com.xronlinux.BreezyDesktop.gschema.xml | 9 +++++ ui/src/connecteddevice.py | 25 +++++++++++- ui/src/gtk/connected-device.ui | 40 +++++++++++++++++++ ui/src/statemanager.py | 20 ++++++++-- ui/src/window.py | 2 +- ui/src/xrdriveripc.py | 23 +++++++++-- 10 files changed, 126 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 988ea1b..a5ee540 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /build/ __pycache__ *.zip +gschemas.compiled diff --git a/gnome/breezydesktop@xronlinux.com/extension.js b/gnome/breezydesktop@xronlinux.com/extension.js index a5876d6..cfe2aba 100644 --- a/gnome/breezydesktop@xronlinux.com/extension.js +++ b/gnome/breezydesktop@xronlinux.com/extension.js @@ -155,6 +155,7 @@ export default class BreezyDesktopExtension extends Extension { this._add_settings_keybinding('recenter-display-shortcut', this._recenter_display.bind(this)); 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) { console.error('Error enabling XR effect', e); this._effect_disable(); @@ -187,13 +188,21 @@ export default class BreezyDesktopExtension extends Extension { }); } - _recenter_display() { + _write_control(key, value) { const file = Gio.file_new_for_path('/dev/shm/xr_driver_control'); const stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); - stream.write('recenter_screen=true', null); + stream.write(`${key}=${value}`, null); stream.close(null); } + _recenter_display() { + this._write_control('recenter_screen', 'true'); + } + + _toggle_follow_mode() { + this._write_control('toggle_breezy_desktop_smooth_follow', 'true'); + } + _effect_disable() { this._is_effect_running = false; diff --git a/ui/build-aux/start-breezy-desktop.sh b/ui/build-aux/start-breezy-desktop.sh index 33cc604..988ec3f 100644 --- a/ui/build-aux/start-breezy-desktop.sh +++ b/ui/build-aux/start-breezy-desktop.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# stolen from dconf-editor: +# https://gitlab.gnome.org/GNOME/dconf-editor/-/blob/master/build-aux/start-dconf-editor.sh + IFS=: read -ra host_data_dirs < <(flatpak-spawn --host sh -c 'echo "$XDG_DATA_DIRS"') # To avoid potentially muddying up $XDG_DATA_DIRS too much, we link the schema paths diff --git a/ui/data/com.xronlinux.BreezyDesktop.desktop.in b/ui/data/com.xronlinux.BreezyDesktop.desktop.in index f9c4d24..3955dac 100644 --- a/ui/data/com.xronlinux.BreezyDesktop.desktop.in +++ b/ui/data/com.xronlinux.BreezyDesktop.desktop.in @@ -1,5 +1,5 @@ [Desktop Entry] -Name=breezydesktop +Name=Breezy Desktop Exec=start-breezy-desktop Icon=com.xronlinux.BreezyDesktop Terminal=false diff --git a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml index 5061c16..17f5bf7 100644 --- a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml +++ b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml @@ -28,6 +28,15 @@ Shortcut to change the display distance. + + + 0']]]> + + Toggle follow mode + + Shortcut to toggle follow mode. + + 1.05 diff --git a/ui/src/connecteddevice.py b/ui/src/connecteddevice.py index 6fd0ab7..c973905 100644 --- a/ui/src/connecteddevice.py +++ b/ui/src/connecteddevice.py @@ -1,6 +1,8 @@ -from gi.repository import Gio, Gtk +from gi.repository import Gio, Gtk, GObject from .settingsmanager import SettingsManager from .shortcutdialog import bind_shortcut_settings +from .statemanager import StateManager +from .xrdriveripc import XRDriverIPC @Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/connected-device.ui') class ConnectedDevice(Gtk.Box): @@ -8,6 +10,7 @@ class ConnectedDevice(Gtk.Box): effect_enable_switch = Gtk.Template.Child() display_distance_scale = Gtk.Template.Child() + follow_mode_switch = Gtk.Template.Child() device_label = Gtk.Template.Child() set_toggle_display_distance_start_button = Gtk.Template.Child() set_toggle_display_distance_end_button = Gtk.Template.Child() @@ -15,18 +18,22 @@ class ConnectedDevice(Gtk.Box): recenter_display_shortcut_label = Gtk.Template.Child() reassign_toggle_display_distance_shortcut_button = Gtk.Template.Child() toggle_display_distance_shortcut_label = Gtk.Template.Child() + reassign_toggle_follow_shortcut_button = Gtk.Template.Child() + toggle_follow_shortcut_label = Gtk.Template.Child() def __init__(self): super(Gtk.Box, self).__init__() self.init_template() self.settings = SettingsManager.get_instance().settings + self.ipc = XRDriverIPC.get_instance() self.settings.bind('display-distance', self.display_distance_scale, 'value', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('effect-enable', self.effect_enable_switch, 'active', Gio.SettingsBindFlags.DEFAULT) bind_shortcut_settings(self.get_parent(), [ [self.reassign_recenter_display_shortcut_button, self.recenter_display_shortcut_label], - [self.reassign_toggle_display_distance_shortcut_button, self.toggle_display_distance_shortcut_label] + [self.reassign_toggle_display_distance_shortcut_button, self.toggle_display_distance_shortcut_label], + [self.reassign_toggle_follow_shortcut_button, self.toggle_follow_shortcut_label] ]) self.bind_set_distance_toggle([ @@ -34,6 +41,20 @@ class ConnectedDevice(Gtk.Box): self.set_toggle_display_distance_end_button ]) + self.state_manager = StateManager.get_instance() + self.state_manager.bind_property('follow-mode', self.follow_mode_switch, 'active', GObject.BindingFlags.DEFAULT) + + self.follow_mode_switch.set_active(self.state_manager.follow_mode) + self.follow_mode_switch.connect('notify::active', self._request_follow_mode) + + def _request_follow_mode(self, switch, param): + if (self.state_manager.follow_mode == switch.get_active()): + return + + self.ipc.write_control_flags({ + 'enable_breezy_desktop_smooth_follow': switch.get_active() + }) + 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 6b911dd..ffd8680 100644 --- a/ui/src/gtk/connected-device.ui +++ b/ui/src/gtk/connected-device.ui @@ -65,6 +65,17 @@ + + + Follow enabled + Keep the virtual display near the center of your view + + + 3 + + + + @@ -155,6 +166,35 @@ + + + Toggle follow mode shortcut + Quickly toggle follow mode + 2 + + + 30 + 30 + + + 3 + + + + + + + toggle-follow-shortcut + 3 + Change + + + + + + diff --git a/ui/src/statemanager.py b/ui/src/statemanager.py index a96a893..9c4c79d 100644 --- a/ui/src/statemanager.py +++ b/ui/src/statemanager.py @@ -11,7 +11,11 @@ class Logger: class StateManager(GObject.GObject): __gsignals__ = { - 'device_update': (GObject.SIGNAL_RUN_FIRST, None, (str,)) + 'device-update': (GObject.SIGNAL_RUN_FIRST, None, (str,)) + } + + __gproperties__ = { + 'follow-mode': (bool, 'Follow Mode', 'Whether the follow mode is enabled', False, GObject.ParamFlags.READWRITE) } _instance = None @@ -32,7 +36,7 @@ class StateManager(GObject.GObject): def __init__(self): GObject.GObject.__init__(self) - self.ipc = XRDriverIPC(logger = Logger(), user="wayne", user_home="/home/wayne") + self.ipc = XRDriverIPC.get_instance() self.connected_device_name = None self.start() @@ -49,6 +53,16 @@ class StateManager(GObject.GObject): new_device_name = StateManager.device_name(self.state) if self.connected_device_name != new_device_name: self.connected_device_name = new_device_name - self.emit('device_update', self.connected_device_name) + self.emit('device-update', self.connected_device_name) + + self.set_property('follow-mode', self.state.get('breezy_desktop_smooth_follow_enabled')) if self.running: threading.Timer(1.0, self._refresh_state).start() + + def do_set_property(self, prop, value): + if prop.name == 'follow-mode': + self.follow_mode = value + + def do_get_property(self, prop): + if prop.name == 'follow-mode': + return self.follow_mode diff --git a/ui/src/window.py b/ui/src/window.py index d7e0206..181f3af 100644 --- a/ui/src/window.py +++ b/ui/src/window.py @@ -30,7 +30,7 @@ class BreezydesktopWindow(Gtk.ApplicationWindow): super().__init__(**kwargs) state_manager = StateManager.get_instance() - state_manager.connect('device_update', self._handle_device_update) + state_manager.connect('device-update', self._handle_device_update) self.connected_device = ConnectedDevice() self.no_device = NoDevice() diff --git a/ui/src/xrdriveripc.py b/ui/src/xrdriveripc.py index 996a13b..58a9d57 100644 --- a/ui/src/xrdriveripc.py +++ b/ui/src/xrdriveripc.py @@ -11,7 +11,7 @@ CONTROL_FLAGS_FILE_PATH = '/dev/shm/xr_driver_control' # read-only file that the driver writes (but never reads) to with its current state DRIVER_STATE_FILE_PATH = '/dev/shm/xr_driver_state' -CONTROL_FLAGS = ['recenter_screen', 'recalibrate', 'sbs_mode', 'refresh_device_license'] +CONTROL_FLAGS = ['recenter_screen', 'recalibrate', 'sbs_mode', 'refresh_device_license', 'enable_breezy_desktop_smooth_follow'] SBS_MODE_VALUES = ['unset', 'enable', 'disable'] MANAGED_EXTERNAL_MODES = ['virtual_display', 'sideview', 'none'] VR_LITE_OUTPUT_MODES = ['mouse', 'joystick'] @@ -58,8 +58,24 @@ CONFIG_ENTRIES = { 'sideview_smooth_follow_enabled': [parse_boolean, False] } +class Logger: + def info(self, message): + print(message) + + def error(self, message): + print(message) + class XRDriverIPC: - def __init__(self, logger, user=None, user_home=None): + _instance = None + + @staticmethod + def get_instance(): + if not XRDriverIPC._instance: + XRDriverIPC._instance = XRDriverIPC() + + return XRDriverIPC._instance + + def __init__(self, logger=Logger(), user=None, user_home=None): self.breezy_installed = False self.breezy_installing = False self.user = user if user else pwd.getpwuid( os.getuid() )[0] @@ -218,6 +234,7 @@ class XRDriverIPC: state['sbs_mode_supported'] = False state['firmware_update_recommended'] = False state['device_license'] = {} + state['breezy_desktop_smooth_follow_enabled'] = False try: with open(DRIVER_STATE_FILE_PATH, 'r') as f: @@ -232,7 +249,7 @@ class XRDriverIPC: state[key] = parse_int(value, 0) elif key in ['calibration_setup', 'calibration_state', 'connected_device_brand', 'connected_device_model']: state[key] = value - elif key in ['sbs_mode_enabled', 'sbs_mode_supported', 'firmware_update_recommended']: + elif key in ['sbs_mode_enabled', 'sbs_mode_supported', 'firmware_update_recommended', 'breezy_desktop_smooth_follow_enabled']: state[key] = parse_boolean(value, False) elif key == 'device_license': state[key] = json.loads(value)