diff --git a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
index 6704f6e..7a9fc9a 100644
--- a/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
+++ b/ui/data/com.xronlinux.BreezyDesktop.gschema.xml
@@ -100,6 +100,15 @@
The size of the display
+
+
+ "cm"
+
+ Measurement units
+
+ Units to display for physical measurements: "cm" or "in"
+
+
0.0
diff --git a/ui/modules/PyXRLinuxDriverIPC b/ui/modules/PyXRLinuxDriverIPC
index da173bd..1655c2b 160000
--- a/ui/modules/PyXRLinuxDriverIPC
+++ b/ui/modules/PyXRLinuxDriverIPC
@@ -1 +1 @@
-Subproject commit da173bd9e0392aaeb2cb68a332e5d4a20dd4dae1
+Subproject commit 1655c2bb03a4e75bf2c9a9815a96b71342396fbd
diff --git a/ui/src/connecteddevice.py b/ui/src/connecteddevice.py
index ee1442c..271cf76 100644
--- a/ui/src/connecteddevice.py
+++ b/ui/src/connecteddevice.py
@@ -33,6 +33,8 @@ class ConnectedDevice(Gtk.Box):
effect_enable_switch = Gtk.Template.Child()
disable_physical_displays_switch = Gtk.Template.Child()
display_zoom_on_focus_switch = Gtk.Template.Child()
+ display_size_scale = Gtk.Template.Child()
+ display_size_adjustment = Gtk.Template.Child()
follow_threshold_scale = Gtk.Template.Child()
follow_threshold_adjustment = Gtk.Template.Child()
follow_mode_switch = Gtk.Template.Child()
@@ -83,6 +85,7 @@ class ConnectedDevice(Gtk.Box):
viewport_offset_x_adjustment = Gtk.Template.Child()
viewport_offset_y_scale = Gtk.Template.Child()
viewport_offset_y_adjustment = Gtk.Template.Child()
+ units_menu = Gtk.Template.Child()
def __init__(self):
super(Gtk.Box, self).__init__()
@@ -90,7 +93,7 @@ class ConnectedDevice(Gtk.Box):
self.active = True
self.all_enabled_state_inputs = [
self.display_zoom_on_focus_switch,
- # self.display_size_scale,
+ self.display_size_scale,
self.follow_mode_switch,
self.follow_threshold_scale,
self.curved_display_switch,
@@ -115,7 +118,7 @@ class ConnectedDevice(Gtk.Box):
self.settings.bind('disable-physical-displays', self.disable_physical_displays_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.connect('changed::display-distance', self._handle_display_distance)
- # self.settings.bind('display-size', self.display_size_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
+ self.settings.bind('display-size', self.display_size_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
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)
@@ -136,6 +139,10 @@ class ConnectedDevice(Gtk.Box):
self.monitor_wrapping_scheme_menu.connect('changed', self._handle_monitor_wrapping_scheme_menu_changed)
self._handle_monitor_wrapping_scheme_setting_changed(self.settings, self.settings.get_string('monitor-wrapping-scheme'))
+ current_units = self.settings.get_string('units')
+ self.units_menu.set_active_id(current_units)
+ self.units_menu.connect('changed', self._handle_units_menu_changed)
+
bind_shortcut_settings(self.get_parent(), [
[self.reassign_toggle_xr_effect_shortcut_button, self.toggle_xr_effect_shortcut_label],
[self.reassign_recenter_display_shortcut_button, self.recenter_display_shortcut_label],
@@ -176,6 +183,11 @@ class ConnectedDevice(Gtk.Box):
self.follow_mode_switch.connect('notify::active', self._refresh_follow_mode)
self.effect_enable_switch.connect('notify::active', self._handle_switch_enabled_state)
+ self.display_size_scale.set_format_value_func(lambda scale, val: self._format_size(val))
+ self.state_manager.connect('notify::connected-device-full-size-cm', self._handle_metric_change)
+ self.state_manager.connect('notify::connected-device-full-distance-cm', self._handle_metric_change)
+ self.settings.connect('changed::units', self._handle_units_changed)
+
self.config_manager = ConfigManager.get_instance()
self.config_manager.connect('notify::breezy-desktop-enabled', self._handle_enabled_config)
self._bind_switch_to_config(self.enable_multi_tap_switch, 'multi-tap-enabled')
@@ -238,6 +250,27 @@ class ConnectedDevice(Gtk.Box):
elif not widget.get_active() and is_zoom_on_focus_already_enabled:
self.settings.set_double('display-distance', toggle_display_distance_end)
+ def _handle_units_menu_changed(self, widget):
+ active_id = widget.get_active_id() or 'cm'
+ self.settings.set_string('units', active_id)
+
+ def _handle_units_changed(self, *args):
+ self._refresh_display_size_scale_value()
+ self._set_all_displays_distance(self.settings.get_double('toggle-display-distance-end'))
+ self._set_focused_display_distance(self.settings.get_double('toggle-display-distance-start'))
+
+ def _handle_metric_change(self, *args):
+ self._refresh_display_size_scale_value()
+ self._set_all_displays_distance(self.settings.get_double('toggle-display-distance-end'))
+ self._set_focused_display_distance(self.settings.get_double('toggle-display-distance-start'))
+
+ def _refresh_display_size_scale_value(self):
+ if self.display_size_scale.get_draw_value():
+ self.display_size_scale.set_draw_value(False)
+ self.display_size_scale.set_draw_value(True)
+ else:
+ self.display_size_scale.queue_draw()
+
def _handle_monitor_wrapping_scheme_setting_changed(self, settings, val):
self.monitor_wrapping_scheme_menu.set_active_id(val)
@@ -314,17 +347,45 @@ class ConnectedDevice(Gtk.Box):
self.display_zoom_on_focus_switch.set_active(should_zoom_on_focus_be_enabled)
def _set_focused_display_distance(self, distance):
- self.focused_display_distance_label.set_markup(f"{_('Focused display')}: {distance}")
+ self.focused_display_distance_label.set_markup(f"{_('Focused display')}: {self._format_distance(distance)}")
self.settings.set_double('toggle-display-distance-start', distance)
self.display_zoom_on_focus_switch.set_sensitive(distance != self.settings.get_double('toggle-display-distance-end'))
def _set_all_displays_distance(self, distance):
- self.all_displays_distance_label.set_markup(f"{_('All displays')}: {distance}")
+ self.all_displays_distance_label.set_markup(f"{_('All displays')}: {self._format_distance(distance)}")
self.settings.set_double('toggle-display-distance-end', distance)
self.display_zoom_on_focus_switch.set_active(False)
self.display_zoom_on_focus_switch.set_sensitive(distance != self.settings.get_double('toggle-display-distance-start'))
+ def _get_units(self):
+ units = self.settings.get_string('units')
+ return units if units in ['cm', 'in'] else 'cm'
+
+ def _format_distance(self, normalized):
+ sm = getattr(self, 'state_manager', None) or StateManager.get_instance()
+ full_cm = float(sm.get_property('connected-device-full-distance-cm') or 0.0)
+ if full_cm <= 0:
+ # Fallback to normalized display if metric unknown
+ return f"{round(normalized, 2)}"
+ cm = normalized * full_cm
+ if self._get_units() == 'in':
+ inches = cm / 2.54
+ return f"{inches:.2f} in"
+ return f"{cm:.1f} cm"
+
+ def _format_size(self, normalized):
+ sm = getattr(self, 'state_manager', None) or StateManager.get_instance()
+ full_cm = float(sm.get_property('connected-device-full-size-cm') or 0.0)
+ if full_cm <= 0:
+ # Fallback to normalized display if metric unknown
+ return f"{round(normalized, 2)}"
+ cm = normalized * full_cm
+ if self._get_units() == 'in':
+ inches = cm / 2.54
+ return f"{inches:.2f} in"
+ return f"{cm:.1f} cm"
+
def _on_display_distance_preset_change_button_clicked(self, widget, settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit):
dialog = DisplayDistanceDialog(settings_key, on_save_callback, title, subtitle, lower_limit, upper_limit)
dialog.set_transient_for(widget.get_ancestor(Gtk.Window))
diff --git a/ui/src/displaydistancedialogcontent.py b/ui/src/displaydistancedialogcontent.py
index 3b81e72..22555f9 100644
--- a/ui/src/displaydistancedialogcontent.py
+++ b/ui/src/displaydistancedialogcontent.py
@@ -1,5 +1,6 @@
from gi.repository import Gtk, Gio
from .settingsmanager import SettingsManager
+from .statemanager import StateManager
import gettext
@@ -22,6 +23,7 @@ class DisplayDistanceDialogContent(Gtk.Box):
self.on_save_callback = on_save_callback
self.settings = SettingsManager.get_instance().settings
+ self.state_manager = StateManager.get_instance()
self.prev_distance = self.settings.get_double('display-distance')
self.lower_limit_orig = self.display_distance_adjustment.get_lower()
@@ -31,6 +33,10 @@ class DisplayDistanceDialogContent(Gtk.Box):
self.settings.bind('display-distance', self.display_distance_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
+ self.display_distance_scale.set_format_value_func(lambda scale, val: self._format_distance(val))
+ self.state_manager.connect('notify::connected-device-full-distance-cm', lambda *args: self.display_distance_scale.queue_draw())
+ self.settings.connect('changed::units', lambda *args: self.display_distance_scale.queue_draw())
+
show_full_scale_button.connect('clicked', self._on_show_full_scale_button_clicked)
save_button.connect('clicked', self._on_save_button_clicked)
@@ -55,4 +61,18 @@ class DisplayDistanceDialogContent(Gtk.Box):
self.display_distance_adjustment.set_upper(self.upper_limit_orig)
def _on_save_button_clicked(self, button):
- self.on_save_callback(self.prev_distance, self.display_distance_adjustment.get_value())
\ No newline at end of file
+ self.on_save_callback(self.prev_distance, self.display_distance_adjustment.get_value())
+
+ def _get_units(self):
+ units = self.settings.get_string('units')
+ return units if units in ['cm', 'in'] else 'cm'
+
+ def _format_distance(self, normalized):
+ full_cm = float(self.state_manager.get_property('connected-device-full-distance-cm') or 0.0)
+ if full_cm <= 0:
+ return f"{round(normalized, 2)}"
+ cm = normalized * full_cm
+ if self._get_units() == 'in':
+ inches = cm / 2.54
+ return f"{inches:.2f} in"
+ return f"{cm:.1f} cm"
\ No newline at end of file
diff --git a/ui/src/gtk/connected-device.ui b/ui/src/gtk/connected-device.ui
index daec778..8b25582 100644
--- a/ui/src/gtk/connected-device.ui
+++ b/ui/src/gtk/connected-device.ui
@@ -229,6 +229,36 @@
+
+
+
Follow threshold
@@ -554,6 +584,35 @@
Advanced Settings
450
+
+
+ Units
+ Choose measurement units for size and distance displays.
+
+
+ 30
+ 150
+ 30
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
Find optimal display config
diff --git a/ui/src/gtk/display-distance-dialog-content.ui b/ui/src/gtk/display-distance-dialog-content.ui
index 704b9c4..0160d84 100644
--- a/ui/src/gtk/display-distance-dialog-content.ui
+++ b/ui/src/gtk/display-distance-dialog-content.ui
@@ -25,7 +25,7 @@
false
- 0.2
+ 0.1
2.5
0.01
1.05
diff --git a/ui/src/statemanager.py b/ui/src/statemanager.py
index efb03c6..d7dc688 100644
--- a/ui/src/statemanager.py
+++ b/ui/src/statemanager.py
@@ -21,6 +21,8 @@ class StateManager(GObject.GObject):
'license-present': (bool, 'License Present', 'Whether a license is present', False, GObject.ParamFlags.READWRITE),
'enabled-features-list': (object, 'Enabled Features List', 'A list of the enabled features', GObject.ParamFlags.READWRITE),
'device-supports-sbs': (bool, 'Device Supports SBS', 'Whether the connected device supports SBS', False, GObject.ParamFlags.READWRITE),
+ 'connected-device-full-distance-cm': (float, 'Full Distance (cm)', 'Device full distance in cm', 0.0, 10000.0, 0.0, GObject.ParamFlags.READWRITE),
+ 'connected-device-full-size-cm': (float, 'Full Size (cm)', 'Device full display size in cm', 0.0, 10000.0, 0.0, GObject.ParamFlags.READWRITE),
}
_instance = None
@@ -59,6 +61,8 @@ class StateManager(GObject.GObject):
self.license_present = False
self.enabled_features = []
self.device_supports_sbs = False
+ self.connected_device_full_distance_cm = 0.0
+ self.connected_device_full_size_cm = 0.0
self._running = True
self._refresh_state()
@@ -98,6 +102,14 @@ class StateManager(GObject.GObject):
self.set_property('device-supports-sbs', self.state.get('sbs_mode_supported', False))
self.set_property('widescreen-mode', self.state.get('sbs_mode_enabled', False))
+ full_distance = self.state.get('connected_device_full_distance_cm') or 0.0
+ if full_distance != self.connected_device_full_distance_cm:
+ self.set_property('connected-device-full-distance-cm', full_distance)
+
+ full_size = self.state.get('connected_device_full_size_cm') or 0.0
+ if full_size != self.connected_device_full_size_cm:
+ self.set_property('connected-device-full-size-cm', full_size)
+
if self._running: threading.Timer(1.0, self._refresh_state).start()
def do_set_property(self, prop, value):
@@ -115,6 +127,10 @@ class StateManager(GObject.GObject):
self.enabled_features = value
if prop.name == 'device-supports-sbs':
self.device_supports_sbs = value
+ if prop.name == 'connected-device-full-distance-cm':
+ self.connected_device_full_distance_cm = value
+ if prop.name == 'connected-device-full-size-cm':
+ self.connected_device_full_size_cm = value
def do_get_property(self, prop):
if prop.name == 'driver-running':
@@ -130,4 +146,8 @@ class StateManager(GObject.GObject):
if prop.name == 'enabled-features-list':
return self.enabled_features
if prop.name == 'device-supports-sbs':
- return self.device_supports_sbs
\ No newline at end of file
+ return self.device_supports_sbs
+ if prop.name == 'connected-device-full-distance-cm':
+ return self.connected_device_full_distance_cm
+ if prop.name == 'connected-device-full-size-cm':
+ return self.connected_device_full_size_cm
\ No newline at end of file