Compare commits

...

3 Commits

Author SHA1 Message Date
wheaney 3049221d61 Attempt to improve mouse cloning 2024-06-13 10:34:15 -07:00
wheaney e522069584 Add SBS controls 2024-06-11 22:54:37 -07:00
wheaney f2aa65c37d Initial attempt at GNOME SBS support 2024-06-11 13:40:06 -07:00
10 changed files with 250 additions and 44 deletions

1
.gitmodules vendored
View File

@ -8,6 +8,7 @@
[submodule "modules/sombrero"] [submodule "modules/sombrero"]
path = modules/sombrero path = modules/sombrero
url = https://github.com/wheaney/sombrero.git url = https://github.com/wheaney/sombrero.git
branch = breezy_sbs
[submodule "ui/modules/PyXRLinuxDriverIPC"] [submodule "ui/modules/PyXRLinuxDriverIPC"]
path = ui/modules/PyXRLinuxDriverIPC path = ui/modules/PyXRLinuxDriverIPC
url = https://github.com/wheaney/PyXRLinuxDriverIPC.git url = https://github.com/wheaney/PyXRLinuxDriverIPC.git

View File

@ -6,6 +6,7 @@ SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
if [ -z "$XDG_DATA_HOME" ]; then if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share" XDG_DATA_HOME="$USER_HOME/.local/share"
fi fi
DATA_DIR="$XDG_DATA_HOME/breezy_gnome"
# if $XDG_DATA_HOME/gnome-shell/extensions/breezydesktop@xronlinux.com exists # if $XDG_DATA_HOME/gnome-shell/extensions/breezydesktop@xronlinux.com exists
extension_path="$XDG_DATA_HOME/gnome-shell/extensions/breezydesktop@xronlinux.com" extension_path="$XDG_DATA_HOME/gnome-shell/extensions/breezydesktop@xronlinux.com"
@ -17,4 +18,12 @@ fi
# recursively copy the $SCRIPT_DIR/../../src to extension_path, don't preserve symlinks # recursively copy the $SCRIPT_DIR/../../src to extension_path, don't preserve symlinks
cp -rL $SCRIPT_DIR/../../src $extension_path cp -rL $SCRIPT_DIR/../../src $extension_path
glib-compile-schemas $extension_path/schemas glib-compile-schemas $extension_path/schemas
pushd $extension_path
GNOME_MANIFEST_LINE=$(find -L . -type f ! -name "*.compiled" -exec sha256sum {} \; | sort | sha256sum | sed 's/ .*//')
popd
pushd $DATA_DIR
echo -e "$GNOME_MANIFEST_LINE breezydesktop@xronlinux.com" > manifest
popd

View File

@ -6,8 +6,9 @@ import Globals from './globals.js';
// Taken from https://github.com/jkitching/soft-brightness-plus // Taken from https://github.com/jkitching/soft-brightness-plus
export class CursorManager { export class CursorManager {
constructor(mainActor) { constructor(mainActor, refreshRate) {
this._mainActor = mainActor; this._mainActor = mainActor;
this._refreshRate = refreshRate;
this._changeHookFn = null; this._changeHookFn = null;
@ -26,7 +27,6 @@ export class CursorManager {
this._cursorWatch = null; this._cursorWatch = null;
this._cursorChangedConnection = null; this._cursorChangedConnection = null;
this._cursorVisibilityChangedConnection = null; this._cursorVisibilityChangedConnection = null;
this._cursorPositionInvalidatedConnection = null;
} }
enable() { enable() {
@ -130,10 +130,9 @@ export class CursorManager {
this._mainActor.add_actor(this._cursorActor); this._mainActor.add_actor(this._cursorActor);
} }
this._cursorChangedConnection = this._cursorTracker.connect('cursor-changed', this._updateMouseSprite.bind(this)); this._cursorChangedConnection = this._cursorTracker.connect('cursor-changed', this._updateMouseSprite.bind(this));
this._cursorVisibilityChangedConnection = this._cursorTracker.connect('visibility-changed', this._updateMouseSprite.bind(this)); this._cursorVisibilityChangedConnection = this._cursorTracker.connect('visibility-changed', this._handleVisibilityChanged.bind(this));
this._cursorPositionInvalidatedConnection = this._cursorTracker.connect('position-invalidated', this._updateMouseSprite.bind(this));
const interval = 1000 / 250; const interval = 1000 / this._refreshRate;
this._cursorWatch = this._cursorWatcher.addWatch(interval, this._updateMousePosition.bind(this)); this._cursorWatch = this._cursorWatcher.addWatch(interval, this._updateMousePosition.bind(this));
const [x, y] = global.get_pointer(); const [x, y] = global.get_pointer();
@ -164,14 +163,15 @@ export class CursorManager {
this._cursorWatch.remove(); this._cursorWatch.remove();
this._cursorWatch = null; this._cursorWatch = null;
this._cursorTracker.disconnect(this._cursorChangedConnection); if (this._cursorChangedConnection) {
this._cursorChangedConnection = null; this._cursorTracker.disconnect(this._cursorChangedConnection);
this._cursorChangedConnection = null;
}
this._cursorTracker.disconnect(this._cursorVisibilityChangedConnection); if (this._cursorVisibilityChangedConnection) {
this._cursorVisibilityChangedConnection = null; this._cursorTracker.disconnect(this._cursorVisibilityChangedConnection);
this._cursorVisibilityChangedConnection = null;
this._cursorTracker.disconnect(this._cursorPositionInvalidatedConnection); }
this._cursorPositionInvalidatedConnection = null;
if (Clutter.Container === undefined) { if (Clutter.Container === undefined) {
this._mainActor.remove_child(this._cursorActor); this._mainActor.remove_child(this._cursorActor);
@ -180,10 +180,6 @@ export class CursorManager {
} }
} }
if (this._cursorTracker.set_keep_focus_while_hidden) {
this._cursorTracker.set_keep_focus_while_hidden(false);
}
if (this._cursorUnfocusInhibited) { if (this._cursorUnfocusInhibited) {
Globals.logger.log_debug('uninhibit_unfocus'); Globals.logger.log_debug('uninhibit_unfocus');
this._cursorSeat.uninhibit_unfocus(); this._cursorSeat.uninhibit_unfocus();
@ -209,8 +205,6 @@ export class CursorManager {
translation_x: -xHot, translation_x: -xHot,
translation_y: -yHot, translation_y: -yHot,
}); });
this._mainActor.set_child_above_sibling(this._cursorActor, null);
this._cursorTrackerSetPointerVisibleBound(false);
// some other processes are uninhibiting when they shouldn't, so we need to re-inhibit here // some other processes are uninhibiting when they shouldn't, so we need to re-inhibit here
if (!this._cursorSeat.is_unfocus_inhibited() && this._cursorUnfocusInhibited) { if (!this._cursorSeat.is_unfocus_inhibited() && this._cursorUnfocusInhibited) {
@ -218,4 +212,12 @@ export class CursorManager {
this._cursorSeat.inhibit_unfocus(); this._cursorSeat.inhibit_unfocus();
} }
} }
_handleVisibilityChanged() {
this._cursorTrackerSetPointerVisibleBound(false);
}
handleStageChildAdded() {
this._mainActor.set_child_above_sibling(this._cursorActor, null);
}
} }

View File

@ -40,8 +40,12 @@ export default class BreezyDesktopExtension extends Extension {
this._distance_binding = null; this._distance_binding = null;
this._distance_connection = null; this._distance_connection = null;
this._follow_threshold_connection = null; this._follow_threshold_connection = null;
this._widescreen_mode_connection = null;
this._start_binding = null; this._start_binding = null;
this._end_binding = null; this._end_binding = null;
this._stage_child_connection = null;
this._curved_display_binding = null;
this._display_size_binding = null;
if (!Globals.logger) { if (!Globals.logger) {
Globals.logger = new Logger({ Globals.logger = new Logger({
@ -153,7 +157,7 @@ export default class BreezyDesktopExtension extends Extension {
this._is_effect_running = true; this._is_effect_running = true;
try { try {
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup); this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, this._refresh_rate);
this._cursor_manager.enable(); this._cursor_manager.enable();
this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);'}); this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);'});
@ -186,12 +190,21 @@ export default class BreezyDesktopExtension extends Extension {
this._update_display_distance(this.settings); this._update_display_distance(this.settings);
this._update_follow_threshold(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_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._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._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._start_binding = this.settings.bind('toggle-display-distance-start', this._xr_effect, 'toggle-display-distance-start', Gio.SettingsBindFlags.DEFAULT) 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._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._overlay.add_effect_with_name('xr-desktop', this._xr_effect); this._overlay.add_effect_with_name('xr-desktop', this._xr_effect);
Meta.disable_unredirect_for_display(global.display); Meta.disable_unredirect_for_display(global.display);
@ -258,6 +271,12 @@ 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);
} }
_update_widescreen_mode(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');
}
_recenter_display() { _recenter_display() {
Globals.logger.log_debug('BreezyDesktopExtension _recenter_display'); Globals.logger.log_debug('BreezyDesktopExtension _recenter_display');
this._write_control('recenter_screen', 'true'); this._write_control('recenter_screen', 'true');
@ -299,6 +318,10 @@ export default class BreezyDesktopExtension extends Extension {
this.settings.disconnect(this._follow_threshold_connection); this.settings.disconnect(this._follow_threshold_connection);
this._follow_threshold_connection = null; this._follow_threshold_connection = null;
} }
if (this._widescreen_mode_connection) {
this.settings.disconnect(this._widescreen_mode_connection);
this._widescreen_mode_connection = null;
}
if (this._start_binding) { if (this._start_binding) {
this.settings.unbind(this._start_binding); this.settings.unbind(this._start_binding);
this._start_binding = null; this._start_binding = null;
@ -307,6 +330,18 @@ export default class BreezyDesktopExtension extends Extension {
this.settings.unbind(this._end_binding); this.settings.unbind(this._end_binding);
this._end_binding = null; this._end_binding = null;
} }
if (this._curved_display_binding) {
this.settings.unbind(this._curved_display_binding);
this._curved_display_binding = null;
}
if (this._display_size_binding) {
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._xr_effect) {
this._xr_effect.cleanup(); this._xr_effect.cleanup();
this._xr_effect = null; this._xr_effect = null;

View File

@ -50,20 +50,20 @@ const shaderUniformLocations = {
'imu_quat_data': null, 'imu_quat_data': null,
'look_ahead_cfg': null, 'look_ahead_cfg': null,
'look_ahead_ms': null, 'look_ahead_ms': null,
'stage_aspect_ratio': null,
'display_aspect_ratio': null,
'trim_width_percent': null, 'trim_width_percent': null,
'trim_height_percent': null, 'trim_height_percent': null,
'display_zoom': null, 'display_size': null,
'display_north_offset': null, 'display_north_offset': null,
'lens_distance_ratio': null, 'lens_distance_ratio': null,
'sbs_enabled': null, 'sbs_enabled': null,
'sbs_content': null, 'sbs_content': null,
'sbs_mode_stretched': null,
'custom_banner_enabled': null, 'custom_banner_enabled': null,
'half_fov_z_rads': null, 'half_fov_z_rads': null,
'half_fov_y_rads': null, 'half_fov_y_rads': null,
'screen_distance': null, 'source_resolution': null,
'display_res': null 'display_resolution': null,
'curved_display': null
}; };
function setUniformFloat(effect, locationName, dataViewInfo, value) { function setUniformFloat(effect, locationName, dataViewInfo, value) {
@ -119,22 +119,19 @@ function setIntermittentUniformVariables() {
const imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA); const imuData = dataViewFloatArray(dataView, IMU_QUAT_DATA);
const imuResetState = validKeepalive && imuData[0] === 0.0 && imuData[1] === 0.0 && imuData[2] === 0.0 && imuData[3] === 1.0; const imuResetState = validKeepalive && imuData[0] === 0.0 && imuData[1] === 0.0 && imuData[2] === 0.0 && imuData[3] === 1.0;
const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validKeepalive; const enabled = dataViewUint8(dataView, ENABLED) !== 0 && version === DATA_LAYOUT_VERSION && validKeepalive;
const displayRes = dataViewUintArray(dataView, DISPLAY_RES);
if (enabled) { if (enabled) {
const displayRes = dataViewUintArray(dataView, DISPLAY_RES);
const displayFov = dataViewFloat(dataView, DISPLAY_FOV); const displayFov = dataViewFloat(dataView, DISPLAY_FOV);
const lensDistanceRatio = dataViewFloat(dataView, LENS_DISTANCE_RATIO);
// compute these values once, they only change when the XR device changes // compute these values once, they only change when the XR device changes
const displayAspectRatio = displayRes[0] / displayRes[1]; const displayAspectRatio = displayRes[0] / displayRes[1];
const stageAspectRatio = this.target_monitor.width / this.target_monitor.height; const diagToVertRatio = Math.sqrt(Math.pow(displayAspectRatio, 2) + 1);
const diagToVertRatio = Math.sqrt(Math.pow(stageAspectRatio, 2) + 1);
const halfFovZRads = degreeToRadian(displayFov / diagToVertRatio) / 2; const halfFovZRads = degreeToRadian(displayFov / diagToVertRatio) / 2;
const halfFovYRads = halfFovZRads * stageAspectRatio; const halfFovYRads = halfFovZRads * displayAspectRatio;
const screenDistance = 1.0 - lensDistanceRatio;
// our overlay doesn't quite cover the full screen texture, which allows us to see some of the real desktop // our overlay doesn't quite cover the full screen texture, which allows us to see some of the real desktop
// underneath, so we trim two pixels around the entire edge of the texture // underneath, so we trim three pixels around the entire edge of the texture
const trimWidthPercent = 3.0 / this.target_monitor.width; const trimWidthPercent = 3.0 / this.target_monitor.width;
const trimHeightPercent = 3.0 / this.target_monitor.height; const trimHeightPercent = 3.0 / this.target_monitor.height;
@ -143,26 +140,23 @@ function setIntermittentUniformVariables() {
transferUniformFloat(this, 'lens_distance_ratio', dataView, LENS_DISTANCE_RATIO); transferUniformFloat(this, 'lens_distance_ratio', dataView, LENS_DISTANCE_RATIO);
// computed values with no dataViewInfo, so we set these manually // computed values with no dataViewInfo, so we set these manually
setSingleFloat(this, 'stage_aspect_ratio', stageAspectRatio);
setSingleFloat(this, 'display_aspect_ratio', displayAspectRatio);
setSingleFloat(this, 'trim_width_percent', trimWidthPercent); setSingleFloat(this, 'trim_width_percent', trimWidthPercent);
setSingleFloat(this, 'trim_height_percent', trimHeightPercent); setSingleFloat(this, 'trim_height_percent', trimHeightPercent);
setSingleFloat(this, 'half_fov_z_rads', halfFovZRads); setSingleFloat(this, 'half_fov_z_rads', halfFovZRads);
setSingleFloat(this, 'half_fov_y_rads', halfFovYRads); setSingleFloat(this, 'half_fov_y_rads', halfFovYRads);
setSingleFloat(this, 'screen_distance', screenDistance); setSingleFloat(this, 'curved_display', this.curved_display ? 1.0 : 0.0);
// TOOD - drive from settings
setSingleFloat(this, 'display_zoom', 1.0);
} }
// these variables are always in play, even if enabled is false // these variables are always in play, even if enabled is false
setSingleFloat(this, 'enabled', enabled ? 1.0 : 0.0); setSingleFloat(this, 'enabled', enabled ? 1.0 : 0.0);
setSingleFloat(this, 'show_banner', imuResetState ? 1.0 : 0.0); setSingleFloat(this, 'show_banner', imuResetState ? 1.0 : 0.0);
setSingleFloat(this, 'sbs_content', 0.0); // TOOD - drive from settings 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', dataViewUint8(dataView, SBS_ENABLED) !== 0 ? 1.0 : 0.0);
setSingleFloat(this, 'custom_banner_enabled', dataViewUint8(dataView, CUSTOM_BANNER_ENABLED) !== 0 ? 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_res'], 2, [this.target_monitor.width, this.target_monitor.height]); this.set_uniform_float(shaderUniformLocations['display_resolution'], 2, displayRes);
this.set_uniform_float(shaderUniformLocations['source_resolution'], 2, [this.target_monitor.width, this.target_monitor.height]);
} else if (dataView.byteLength !== 0) { } else if (dataView.byteLength !== 0) {
Globals.logger.log(`ERROR: Invalid dataView.byteLength: ${dataView.byteLength} !== ${DATA_VIEW_LENGTH}`) Globals.logger.log(`ERROR: Invalid dataView.byteLength: ${dataView.byteLength} !== ${DATA_VIEW_LENGTH}`)
} }
@ -208,6 +202,22 @@ export const XREffect = GObject.registerClass({
0.2, 0.2,
2.5, 2.5,
1.05 1.05
),
'curved-display': GObject.ParamSpec.boolean(
'curved-display',
'Curved Display',
'Whether the display is curved',
GObject.ParamFlags.READWRITE,
false
),
'widescreen-display-size': GObject.ParamSpec.double(
'widescreen-display-size',
'Widescreen display size',
'Size of the display when in widescreen/SBS mode',
GObject.ParamFlags.READWRITE,
0.2,
2.5,
1.0
) )
} }
}, class XREffect extends Shell.GLSLEffect { }, class XREffect extends Shell.GLSLEffect {
@ -295,6 +305,7 @@ export const XREffect = GObject.registerClass({
setSingleFloat(this, 'display_north_offset', this.display_distance); setSingleFloat(this, 'display_north_offset', this.display_distance);
setSingleFloat(this, 'look_ahead_ms', lookAheadMS(this._dataView)); setSingleFloat(this, 'look_ahead_ms', lookAheadMS(this._dataView));
setUniformMatrix(this, 'imu_quat_data', 4, this._dataView, IMU_QUAT_DATA); setUniformMatrix(this, 'imu_quat_data', 4, this._dataView, IMU_QUAT_DATA);
setSingleFloat(this, 'display_size', this.widescreen_display_size);
} else if (this._dataView.byteLength !== 0) { } else if (this._dataView.byteLength !== 0) {
Globals.logger.log(`ERROR: Invalid dataView.byteLength: ${this._dataView.byteLength} !== ${DATA_VIEW_LENGTH}`) Globals.logger.log(`ERROR: Invalid dataView.byteLength: ${this._dataView.byteLength} !== ${DATA_VIEW_LENGTH}`)
} }

@ -1 +1 @@
Subproject commit 26ece497d36bbe2a7445ea2f4e1cce31c35daa3a Subproject commit 3f712c2aeffcae41d238e146acc97c145ab9647b

View File

@ -64,6 +64,33 @@
End distance when using the "toggle display distance" shortcut. End distance when using the "toggle display distance" shortcut.
</description> </description>
</key> </key>
<key name="widescreen-mode" type="b">
<default>
false
</default>
<summary>Widescreen mode</summary>
<description>
Enable widescreen/SBS mode
</description>
</key>
<key name="widescreen-display-size" type="d">
<default>
1.0
</default>
<summary>Widescreen display size</summary>
<description>
The size of the display when in widescreen/SBS mode
</description>
</key>
<key name="curved-display" type="b">
<default>
false
</default>
<summary>Curved display</summary>
<description>
Enable curved display mode
</description>
</key>
<key name="developer-mode" type="b"> <key name="developer-mode" type="b">
<default> <default>
false false

View File

@ -10,13 +10,21 @@ from .xrdriveripc import XRDriverIPC
class ConnectedDevice(Gtk.Box): class ConnectedDevice(Gtk.Box):
__gtype_name__ = "ConnectedDevice" __gtype_name__ = "ConnectedDevice"
device_label = Gtk.Template.Child()
effect_enable_switch = Gtk.Template.Child() effect_enable_switch = Gtk.Template.Child()
display_distance_row = Gtk.Template.Child()
display_distance_scale = Gtk.Template.Child() display_distance_scale = Gtk.Template.Child()
display_distance_adjustment = Gtk.Template.Child() display_distance_adjustment = Gtk.Template.Child()
follow_threshold_scale = Gtk.Template.Child() follow_threshold_scale = Gtk.Template.Child()
follow_threshold_adjustment = Gtk.Template.Child() follow_threshold_adjustment = Gtk.Template.Child()
follow_mode_switch = Gtk.Template.Child() follow_mode_switch = Gtk.Template.Child()
device_label = Gtk.Template.Child() widescreen_mode_switch = Gtk.Template.Child()
widescreen_display_distance_row = Gtk.Template.Child()
widescreen_display_distance_scale = Gtk.Template.Child()
widescreen_display_distance_adjustment = Gtk.Template.Child()
widescreen_display_size_scale = Gtk.Template.Child()
widescreen_display_size_adjustment = Gtk.Template.Child()
curved_display_switch = Gtk.Template.Child()
set_toggle_display_distance_start_button = Gtk.Template.Child() set_toggle_display_distance_start_button = Gtk.Template.Child()
set_toggle_display_distance_end_button = Gtk.Template.Child() set_toggle_display_distance_end_button = Gtk.Template.Child()
reassign_recenter_display_shortcut_button = Gtk.Template.Child() reassign_recenter_display_shortcut_button = Gtk.Template.Child()
@ -33,6 +41,10 @@ class ConnectedDevice(Gtk.Box):
self.display_distance_scale, self.display_distance_scale,
self.follow_mode_switch, self.follow_mode_switch,
self.follow_threshold_scale, self.follow_threshold_scale,
self.widescreen_mode_switch,
self.widescreen_display_distance_scale,
self.widescreen_display_size_scale,
self.curved_display_switch,
self.set_toggle_display_distance_start_button, self.set_toggle_display_distance_start_button,
self.set_toggle_display_distance_end_button, self.set_toggle_display_distance_end_button,
self.reassign_recenter_display_shortcut_button, self.reassign_recenter_display_shortcut_button,
@ -45,7 +57,11 @@ class ConnectedDevice(Gtk.Box):
self.extensions_manager = ExtensionsManager.get_instance() self.extensions_manager = ExtensionsManager.get_instance()
self.settings.bind('display-distance', self.display_distance_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('display-distance', self.display_distance_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('display-distance', self.widescreen_display_distance_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('follow-threshold', self.follow_threshold_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('widescreen-display-size', self.widescreen_display_size_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
bind_shortcut_settings(self.get_parent(), [ bind_shortcut_settings(self.get_parent(), [
[self.reassign_recenter_display_shortcut_button, self.recenter_display_shortcut_label], [self.reassign_recenter_display_shortcut_button, self.recenter_display_shortcut_label],
@ -65,6 +81,8 @@ class ConnectedDevice(Gtk.Box):
self.follow_mode_switch.set_active(self.state_manager.follow_mode) self.follow_mode_switch.set_active(self.state_manager.follow_mode)
self.follow_mode_switch.connect('notify::active', self._refresh_follow_mode) self.follow_mode_switch.connect('notify::active', self._refresh_follow_mode)
self.widescreen_mode_switch.connect('notify::active', self._refresh_widescreen_mode)
self.effect_enable_switch.set_active(self._is_config_enabled(self.ipc.retrieve_config()) and self.extensions_manager.is_enabled()) 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.effect_enable_switch.connect('notify::active', self._refresh_inputs_for_enabled_state)
@ -83,6 +101,13 @@ class ConnectedDevice(Gtk.Box):
def _is_config_enabled(self, config): def _is_config_enabled(self, config):
return config.get('disabled') == False and 'breezy_desktop' in config.get('external_mode', []) return config.get('disabled') == False and 'breezy_desktop' in config.get('external_mode', [])
def _refresh_widescreen_mode(self, switch, param):
widescreen_mode_enabled = switch.get_active()
self.widescreen_display_distance_row.set_visible(widescreen_mode_enabled)
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): def _refresh_inputs_for_enabled_state(self, switch, param):
requesting_enabled = switch.get_active() requesting_enabled = switch.get_active()
@ -98,7 +123,9 @@ class ConnectedDevice(Gtk.Box):
for widget in self.all_enabled_state_inputs: for widget in self.all_enabled_state_inputs:
widget.set_sensitive(requesting_enabled) widget.set_sensitive(requesting_enabled)
if requesting_enabled: self._refresh_follow_mode(self.follow_mode_switch, None) if requesting_enabled:
self._refresh_follow_mode(self.follow_mode_switch, None)
self._refresh_widescreen_mode(self.widescreen_mode_switch, None)
def _refresh_follow_mode(self, switch, param): def _refresh_follow_mode(self, switch, param):
self.follow_threshold_scale.set_sensitive(switch.get_active()) self.follow_threshold_scale.set_sensitive(switch.get_active())
@ -121,6 +148,7 @@ class ConnectedDevice(Gtk.Box):
self.state_manager.unbind_property('follow-mode', self.follow_mode_switch, 'active') self.state_manager.unbind_property('follow-mode', self.follow_mode_switch, 'active')
self.settings.unbind('display-distance', self.display_distance_adjustment, 'value') self.settings.unbind('display-distance', self.display_distance_adjustment, 'value')
self.settings.unbind('follow-threshold', self.follow_threshold_adjustment, 'value') self.settings.unbind('follow-threshold', self.follow_threshold_adjustment, 'value')
self.settings.unbind('widescreen-mode', self.widescreen_mode_switch, 'active')
self.extensions_manager.unbind_property('breezy-enabled', self.effect_enable_switch, 'active') self.extensions_manager.unbind_property('breezy-enabled', self.effect_enable_switch, 'active')
def reload_display_distance_toggle_button(widget): def reload_display_distance_toggle_button(widget):

View File

@ -38,7 +38,7 @@
</object> </object>
</child> </child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow" id="display_distance_row">
<property name="title" translatable="true">Display distance</property> <property name="title" translatable="true">Display distance</property>
<child> <child>
<object class="GtkScale" id="display_distance_scale"> <object class="GtkScale" id="display_distance_scale">
@ -100,6 +100,93 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Curved display</property>
<property name="subtitle" translatable="true">Switch between flat and curved displays</property>
<property name="valign">2</property>
<child>
<object class="GtkSwitch" id="curved_display_switch">
<property name="valign">3</property>
</object>
</child>
</object>
</child>
</object>
</child><child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="true">Widescreen</property>
<property name="description" translatable="true">Widescreen mode switches your glasses into side-by-side mode and doubles the width of the display</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Enable widescreen</property>
<property name="subtitle" translatable="true">Switches your glasses into side-by-side mode</property>
<property name="valign">2</property>
<child>
<object class="GtkSwitch" id="widescreen_mode_switch">
<property name="valign">3</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="widescreen_display_distance_row">
<property name="title" translatable="true">Display distance</property>
<property name="subtitle" translatable="true">Move the screen closer or further using SBS depth</property>
<child>
<object class="GtkScale" id="widescreen_display_distance_scale">
<property name="valign">3</property>
<property name="draw-value">true</property>
<property name="value-pos">0</property>
<property name="digits">2</property>
<property name="width-request">350</property>
<property name="has-origin">false</property>
<property name="adjustment">
<object class="GtkAdjustment" id="widescreen_display_distance_adjustment">
<property name="lower">0.2</property>
<property name="upper">2.5</property>
<property name="step-increment">0.01</property>
<property name="value">1.05</property>
</object>
</property>
<marks>
<mark value="0.2" position="bottom"></mark>
<mark value="1.0" position="bottom"></mark>
<mark value="2.5" position="bottom"></mark>
</marks>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Display size</property>
<property name="subtitle" translatable="true">Combine with display distance to achieve a comfortable level of depth and size</property>
<child>
<object class="GtkScale" id="widescreen_display_size_scale">
<property name="valign">3</property>
<property name="draw-value">true</property>
<property name="value-pos">0</property>
<property name="digits">2</property>
<property name="width-request">350</property>
<property name="has-origin">false</property>
<property name="adjustment">
<object class="GtkAdjustment" id="widescreen_display_size_adjustment">
<property name="lower">0.2</property>
<property name="upper">2.5</property>
<property name="step-increment">0.01</property>
<property name="value">1.0</property>
</object>
</property>
<marks>
<mark value="0.2" position="bottom"></mark>
<mark value="1.0" position="bottom"></mark>
<mark value="2.5" position="bottom"></mark>
</marks>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>

View File

@ -22,6 +22,7 @@ class StateManager(GObject.GObject):
__gproperties__ = { __gproperties__ = {
'follow-mode': (bool, 'Follow Mode', 'Whether the follow mode is enabled', False, GObject.ParamFlags.READWRITE), 'follow-mode': (bool, 'Follow Mode', 'Whether the follow mode is enabled', False, GObject.ParamFlags.READWRITE),
'follow-threshold': (float, 'Follow Threshold', 'The follow threshold', 1.0, 45.0, 15.0, GObject.ParamFlags.READWRITE), 'follow-threshold': (float, 'Follow Threshold', 'The follow threshold', 1.0, 45.0, 15.0, GObject.ParamFlags.READWRITE),
'widescreen-mode': (bool, 'Widescreen Mode', 'Whether widescreen mode is enabled', False, GObject.ParamFlags.READWRITE),
'license-action-needed': (bool, 'License Action Needed', 'Whether the license needs attention', False, GObject.ParamFlags.READWRITE), 'license-action-needed': (bool, 'License Action Needed', 'Whether the license needs attention', False, GObject.ParamFlags.READWRITE),
'license-present': (bool, 'License Present', 'Whether a license is present', False, GObject.ParamFlags.READWRITE), '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), 'enabled-features-list': (object, 'Enabled Features List', 'A list of the enabled features', GObject.ParamFlags.READWRITE),
@ -94,12 +95,15 @@ class StateManager(GObject.GObject):
self.set_property('license-present', False) self.set_property('license-present', False)
self.set_property('follow-mode', self.state.get('breezy_desktop_smooth_follow_enabled')) 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')
if self.running: threading.Timer(1.0, self._refresh_state).start() if self.running: threading.Timer(1.0, self._refresh_state).start()
def do_set_property(self, prop, value): def do_set_property(self, prop, value):
if prop.name == 'follow-mode': if prop.name == 'follow-mode':
self.follow_mode = value self.follow_mode = value
if prop.name == 'widescreen-mode':
self.widescreen_mode = value
if prop.name == 'license-action-needed': if prop.name == 'license-action-needed':
self.license_action_needed = value self.license_action_needed = value
if prop.name == 'license-present': if prop.name == 'license-present':
@ -110,6 +114,8 @@ class StateManager(GObject.GObject):
def do_get_property(self, prop): def do_get_property(self, prop):
if prop.name == 'follow-mode': if prop.name == 'follow-mode':
return self.follow_mode return self.follow_mode
if prop.name == 'widescreen-mode':
return self.widescreen_mode
if prop.name == 'license-action-needed': if prop.name == 'license-action-needed':
return self.license_action_needed return self.license_action_needed
if prop.name == 'license-present': if prop.name == 'license-present':