Merge branch 'main' into gnome-44-max
This commit is contained in:
commit
10268f638d
15
README.md
15
README.md
|
|
@ -13,7 +13,7 @@ There are two installations at the moment. **Note: Only install one of these at
|
|||
* [Breezy Vulkan](#breezy-vulkan) primarily for gaming but would work with pretty much any application that uses Vulkan rendering.
|
||||
|
||||
## Breezy GNOME
|
||||
Breezy GNOME is a virtual workspace solution for Linux desktops that use the GNOME desktop environment (requires GNOME 45+ on an x86_64 system); see [non-GNOME setup](#non-gnome-setup) if you want to try it without a GNOME desktop environment. It currently supports one virtual monitor and multiple physical monitors, but it will soon support multiple virtual monitors. See [upcoming features](#upcoming-features) for more improvements on the horizon.
|
||||
Breezy GNOME is a virtual workspace solution for Linux desktops that use the GNOME desktop environment (supports GNOME versions 42-46 on an x86_64 system); see [non-GNOME setup](#non-gnome-setup) if you want to try it without a GNOME desktop environment. It currently supports one virtual monitor and multiple physical monitors, but it will soon support multiple virtual monitors. See [upcoming features](#upcoming-features) for more improvements on the horizon.
|
||||
|
||||
### GNOME Setup
|
||||
|
||||
|
|
@ -21,18 +21,21 @@ For the best performance, ensure you have the latest graphics drivers installed
|
|||
|
||||
#### Arch Linux
|
||||
|
||||
*Note: if you've previously installed Breezy GNOME using the setup script, you must uninstall it first: `~/.local/bin/breezy_gnome_uninstall`*
|
||||
|
||||
Breezy GNOME is in AUR (but not pacman, yet). To install: `yay -S breezy-desktop-gnome-git` and, once that succeeds, `systemctl --user enable --now xreal-air-driver.service`
|
||||
Breezy GNOME is in AUR (but not pacman, yet). To install, run these commands from a terminal:
|
||||
1. If you've previously installed Breezy GNOME using the setup script, you must uninstall it first with `breezy_gnome_uninstall`
|
||||
2. `yay -S breezy-desktop-gnome-git`
|
||||
3. `systemctl --user enable --now xr-driver.service`
|
||||
|
||||
#### All other distros
|
||||
|
||||
1. Download the Breezy GNOME [setup script](https://github.com/wheaney/breezy-desktop/releases/latest/download/breezy_gnome_setup) and set the execute flag (e.g. from the terminal: `chmod +x ~/Downloads/breezy_gnome_setup`)
|
||||
2. Run the setup script (e.g. `~/Downloads/breezy_gnome_setup`)
|
||||
2. Run the setup script:
|
||||
* For **GNOME 45+**: `~/Downloads/breezy_gnome_setup`
|
||||
* For **GNOME 42-44**: `~/Downloads/breezy_gnome_setup --tag gnome-44-max-beta-1`
|
||||
|
||||
### Non-GNOME Setup
|
||||
A workable solution (with some [QoL improvements needed](#upcoming-features)) is to use your preferred desktop environment with a GNOME window open in nested mode. To do this:
|
||||
1. Install `gnome-shell` using your distros package manager (e.g. apt-get, pacman, dnf, etc...). This will currently only work with GNOME Shell versions 45+, so check that using `gnome-shell --version`
|
||||
1. Install `gnome-shell` using your distros package manager (e.g. apt-get, pacman, dnf, etc...). This will currently only work with GNOME Shell versions 42-46, so check that using `gnome-shell --version`
|
||||
2. Run the [GNOME setup](#gnome-setup) steps. You shouldn't need to log out and back in since GNOME will be running nested.
|
||||
3. Launch the nested GNOME Shell using `MUTTER_DEBUG_DUMMY_MODE_SPECS="1920x1080@60" dbus-run-session -- gnome-shell --nested`
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,11 @@ pushd gnome/src
|
|||
GNOME_MANIFEST_LINE=$(find -L . -type f ! -name "*.compiled" -exec sha256sum {} \; | sort | sha256sum | sed 's/ .*//')
|
||||
popd
|
||||
|
||||
ui/bin/package $ARCH
|
||||
cp ui/out/com.xronlinux.BreezyDesktop-$ARCH.flatpak $PACKAGE_DIR/com.xronlinux.BreezyDesktop.flatpak
|
||||
FLATPAK_BUILD_ARTIFACT=ui/out/com.xronlinux.BreezyDesktop-$ARCH.flatpak
|
||||
if [ ! -e "$FLATPAK_BUILD_ARTIFACT" ] || [ "$1" == "--rebuild-flatpak" ]; then
|
||||
ui/bin/package $ARCH
|
||||
fi
|
||||
cp $FLATPAK_BUILD_ARTIFACT $PACKAGE_DIR/com.xronlinux.BreezyDesktop.flatpak
|
||||
|
||||
# create manifest file for verifying installed file checksums against the originally packaged versions
|
||||
# include any file that doesn't get modified during setup (e.g. vkBasalt.json files)
|
||||
|
|
|
|||
|
|
@ -96,24 +96,30 @@ class BreezyDesktopExtension {
|
|||
var target_monitor = this._target_monitor;
|
||||
var is_effect_running = this._is_effect_running;
|
||||
this._running_poller_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, (() => {
|
||||
if (is_effect_running) {
|
||||
this._running_poller_id = undefined;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
const is_driver_running = this._check_driver_running();
|
||||
if (is_driver_running && target_monitor) {
|
||||
// Don't enable the effect yet if monitor updates are needed.
|
||||
// _setup will be triggered again since a !ready result means it will trigger monitor changes,
|
||||
// so we can remove this timeout_add no matter what.
|
||||
if (this._target_monitor_ready(target_monitor)) {
|
||||
Globals.logger.log('Driver is running, supported monitor connected. Enabling XR effect.');
|
||||
this._effect_enable();
|
||||
try {
|
||||
if (is_effect_running) {
|
||||
this._running_poller_id = undefined;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
const is_driver_running = this._check_driver_running();
|
||||
if (is_driver_running && target_monitor) {
|
||||
// Don't enable the effect yet if monitor updates are needed.
|
||||
// _setup will be triggered again since a !ready result means it will trigger monitor changes,
|
||||
// so we can remove this timeout_add no matter what.
|
||||
if (this._target_monitor_ready(target_monitor)) {
|
||||
Globals.logger.log('Driver is running, supported monitor connected. Enabling XR effect.');
|
||||
this._effect_enable();
|
||||
}
|
||||
this._running_poller_id = undefined;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
} else {
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
}
|
||||
} catch (e) {
|
||||
Globals.logger.log(`ERROR: BreezyDesktopExtension _poll_for_ready ${e.message}\n${e.stack}`);
|
||||
this._running_poller_id = undefined;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
} else {
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
}
|
||||
}).bind(this));
|
||||
}
|
||||
|
|
@ -334,29 +340,37 @@ class BreezyDesktopExtension {
|
|||
}
|
||||
|
||||
_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(`${key}=${value}`, null);
|
||||
stream.close(null);
|
||||
try {
|
||||
const file = Gio.file_new_for_path('/dev/shm/xr_driver_control');
|
||||
const stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
|
||||
stream.write(`${key}=${value}`, null);
|
||||
stream.close(null);
|
||||
} catch (e) {
|
||||
Globals.logger.log(`ERROR: BreezyDesktopExtension _write_control ${e.message}\n${e.stack}`);
|
||||
}
|
||||
}
|
||||
|
||||
_read_state(keys) {
|
||||
const state = {};
|
||||
const file = Gio.file_new_for_path('/dev/shm/xr_driver_state');
|
||||
if (file.query_exists(null)) {
|
||||
const data = file.load_contents(null);
|
||||
|
||||
if (data[0]) {
|
||||
const bytes = new Uint8Array(data[1]);
|
||||
const decoder = new TextDecoder();
|
||||
const contents = decoder.decode(bytes);
|
||||
try {
|
||||
const file = Gio.file_new_for_path('/dev/shm/xr_driver_state');
|
||||
if (file.query_exists(null)) {
|
||||
const data = file.load_contents(null);
|
||||
|
||||
if (data[0]) {
|
||||
const bytes = new Uint8Array(data[1]);
|
||||
const decoder = new TextDecoder();
|
||||
const contents = decoder.decode(bytes);
|
||||
|
||||
const lines = contents.split('\n');
|
||||
for (const line of lines) {
|
||||
const [k, v] = line.split('=');
|
||||
if (keys.includes(k)) state[k] = v;
|
||||
const lines = contents.split('\n');
|
||||
for (const line of lines) {
|
||||
const [k, v] = line.split('=');
|
||||
if (keys.includes(k)) state[k] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Globals.logger.log(`ERROR: BreezyDesktopExtension _read_state ${e.message}\n${e.stack}`);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
|
@ -378,13 +392,14 @@ class BreezyDesktopExtension {
|
|||
this._write_control('sbs_mode', value ? 'enable' : 'disable');
|
||||
if (!this._sbs_mode_update_timeout) {
|
||||
var attempts = 0;
|
||||
this._sbs_mode_update_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 10000, (() => {
|
||||
this._sbs_mode_update_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 3000, (() => {
|
||||
if (attempts++ < 3) {
|
||||
this._write_control('sbs_mode', value ? 'enable' : 'disable');
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
// the state never updated to reflect our request, revert the setting
|
||||
Globals.logger.log('Failed to update sbs_mode state, reverting setting');
|
||||
this.settings.set_boolean('widescreen-mode', !value);
|
||||
this._sbs_mode_update_timeout = undefined;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
|
@ -404,6 +419,7 @@ class BreezyDesktopExtension {
|
|||
_update_widescreen_mode_from_state(effect, _pspec) {
|
||||
// kill our state checker if it's running
|
||||
if (this._sbs_mode_update_timeout) {
|
||||
Globals.logger.log_debug('BreezyDesktopExtension _update_widescreen_mode_from_state - clearing timeout');
|
||||
GLib.source_remove(this._sbs_mode_update_timeout);
|
||||
this._sbs_mode_update_timeout = undefined;
|
||||
}
|
||||
|
|
@ -449,8 +465,9 @@ class BreezyDesktopExtension {
|
|||
this._is_effect_running = false;
|
||||
|
||||
if (this._running_poller_id) {
|
||||
GLib.source_remove(this._running_poller_id);
|
||||
const poller_id = this._running_poller_id;
|
||||
this._running_poller_id = undefined;
|
||||
GLib.source_remove(poller_id);
|
||||
}
|
||||
|
||||
Main.wm.removeKeybinding('recenter-display-shortcut');
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPri
|
|||
let ourMonitor = undefined;
|
||||
let monitorToModeIdMap = {};
|
||||
let bestFitMode = undefined;
|
||||
const skipScaleUpdate = !!properties['global-scale-required'];
|
||||
for (let monitor of monitors) {
|
||||
const [details, availableModes, monProperties] = monitor;
|
||||
const [connector, vendor, product, monitorSerial] = details;
|
||||
|
|
@ -160,7 +161,8 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPri
|
|||
const updatedLogicalMonitors = logicalMonitors.map((logicalMonitor) => {
|
||||
const [x, y, scale, transform, primary, monitors, logMonProperties] = logicalMonitor;
|
||||
const hasOurMonitor = !!monitors.some((monitor) => monitor[0] === connectorName);
|
||||
anyMonitorsChanged |= hasOurMonitor && bestFitMode.bestScale !== scale;
|
||||
const newScale = (!skipScaleUpdate && hasOurMonitor) ? bestFitMode.bestScale : scale;
|
||||
anyMonitorsChanged |= newScale !== 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
|
||||
|
|
@ -168,7 +170,7 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPri
|
|||
return [
|
||||
x,
|
||||
y,
|
||||
hasOurMonitor ? bestFitMode.bestScale : scale,
|
||||
newScale,
|
||||
transform,
|
||||
headsetAsPrimary ? hasOurMonitor : primary,
|
||||
monitors.map((monitor) => {
|
||||
|
|
@ -361,6 +363,7 @@ var MonitorManager = GObject.registerClass({
|
|||
}
|
||||
this._asyncRequestsInFlight++;
|
||||
getMonitorConfig(this._displayConfigProxy, ((result, error) => {
|
||||
this._asyncRequestsInFlight--;
|
||||
if (error) {
|
||||
Globals.logger.log(error);
|
||||
return;
|
||||
|
|
@ -385,7 +388,7 @@ var MonitorManager = GObject.registerClass({
|
|||
}
|
||||
this._monitorProperties = monitorProperties;
|
||||
if (!!this._changeHookFn) {
|
||||
if (--this._asyncRequestsInFlight === 0) {
|
||||
if (this._asyncRequestsInFlight === 0) {
|
||||
this._changeHookFn();
|
||||
} else {
|
||||
Globals.logger.log_debug(`MonitorManager _on_monitors_change: ${this._asyncRequestsInFlight} requests still pending, skipping change hook`);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit b0080ca844e057d31aae0e70aa6d026059ea304f
|
||||
Subproject commit 9066a1d1c6b63a71f1d39e4b946ae06cc5c673a6
|
||||
|
|
@ -21,11 +21,15 @@ SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
|||
|
||||
TMP_DIR=$(mktemp -d --tmpdir=$SCRIPT_DIR/.. -t .breezy-ui-flatpak-XXXXXXXXXX)
|
||||
OUT_DIR=$SCRIPT_DIR/../out
|
||||
rm -rf $OUT_DIR
|
||||
mkdir -p $OUT_DIR
|
||||
|
||||
BUILD_ARTIFACT=$OUT_DIR/com.xronlinux.BreezyDesktop-$ARCH.flatpak
|
||||
if [ -e "$BUILD_ARTIFACT" ]; then
|
||||
rm $BUILD_ARTIFACT
|
||||
fi
|
||||
|
||||
flatpak-builder --arch $ARCH --disable-rofiles-fuse --disable-cache --force-clean --delete-build-dirs --user $TMP_DIR/build $SCRIPT_DIR/../com.xronlinux.BreezyDesktop.json
|
||||
flatpak build-export --arch $ARCH $TMP_DIR/export $TMP_DIR/build
|
||||
flatpak build-bundle --arch $ARCH $TMP_DIR/export $OUT_DIR/com.xronlinux.BreezyDesktop-$ARCH.flatpak com.xronlinux.BreezyDesktop --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak build-bundle --arch $ARCH $TMP_DIR/export $BUILD_ARTIFACT com.xronlinux.BreezyDesktop --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
rm -rf "$TMP_DIR"
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit a96fdeb1557d8cd24e73cb8e9e2559adfa46e3aa
|
||||
Subproject commit 3f23409b6be154c9c9a7035c6213558a7ef6c84e
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
<file preprocess="xml-stripblanks">gtk/failed-verification.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/license-dialog.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/no-device.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/no-driver.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/no-extension.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/no-license.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/shortcut-dialog.ui</file>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<template class="NoDriver" parent="GtkBox">
|
||||
<property name="orientation">1</property>
|
||||
<property name="margin-top">20</property>
|
||||
<property name="margin-bottom">20</property>
|
||||
<property name="margin-start">20</property>
|
||||
<property name="margin-end">20</property>
|
||||
<property name="spacing">20</property>
|
||||
<child>
|
||||
<object class="AdwStatusPage">
|
||||
<property name="title" translatable="true">No driver running</property>
|
||||
<property name="description" translatable="true">
|
||||
If you installed via AUR, make sure you ran the recommended post-install command:
|
||||
systemctl --user enable --now xr-driver.service
|
||||
|
||||
Otherwise, please file an issue on GitHub, or create a new thread in the #troubleshooting channel on Discord.
|
||||
</property>
|
||||
<property name="width-request">650</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
@ -38,6 +38,7 @@ breezydesktop_sources = [
|
|||
'licensetierrow.py',
|
||||
'main.py',
|
||||
'nodevice.py',
|
||||
'nodriver.py',
|
||||
'noextension.py',
|
||||
'nolicense.py',
|
||||
'settingsmanager.py',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
from gi.repository import Gtk
|
||||
|
||||
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/no-driver.ui')
|
||||
class NoDriver(Gtk.Box):
|
||||
__gtype_name__ = "NoDriver"
|
||||
|
|
@ -20,6 +20,7 @@ class StateManager(GObject.GObject):
|
|||
}
|
||||
|
||||
__gproperties__ = {
|
||||
'driver-running': (bool, 'Driver Running', 'Whether the driver is running', 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),
|
||||
'widescreen-mode': (bool, 'Widescreen Mode', 'Whether widescreen mode is enabled', False, GObject.ParamFlags.READWRITE),
|
||||
|
|
@ -53,6 +54,7 @@ class StateManager(GObject.GObject):
|
|||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
self.ipc = XRDriverIPC.get_instance()
|
||||
self.driver_running = False
|
||||
self.connected_device_name = None
|
||||
self.license_action_needed = False
|
||||
self.license_action_needed_seconds = 0
|
||||
|
|
@ -71,6 +73,8 @@ class StateManager(GObject.GObject):
|
|||
|
||||
def _refresh_state(self):
|
||||
self.state = self.ipc.retrieve_driver_state()
|
||||
self.set_property('driver-running', self.state['ui_view'].get('driver_running'))
|
||||
|
||||
new_device_name = StateManager.device_name(self.state)
|
||||
if self.connected_device_name != new_device_name:
|
||||
self.connected_device_name = new_device_name
|
||||
|
|
@ -94,12 +98,14 @@ class StateManager(GObject.GObject):
|
|||
elif self.license_present:
|
||||
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'))
|
||||
self.set_property('follow-mode', self.state.get('breezy_desktop_smooth_follow_enabled', False))
|
||||
self.set_property('widescreen-mode', self.state.get('sbs_mode_enabled', False))
|
||||
|
||||
if self.running: threading.Timer(1.0, self._refresh_state).start()
|
||||
|
||||
def do_set_property(self, prop, value):
|
||||
if prop.name == 'driver-running':
|
||||
self.driver_running = value
|
||||
if prop.name == 'follow-mode':
|
||||
self.follow_mode = value
|
||||
if prop.name == 'widescreen-mode':
|
||||
|
|
@ -112,6 +118,8 @@ class StateManager(GObject.GObject):
|
|||
self.enabled_features = value
|
||||
|
||||
def do_get_property(self, prop):
|
||||
if prop.name == 'driver-running':
|
||||
return self.driver_running
|
||||
if prop.name == 'follow-mode':
|
||||
return self.follow_mode
|
||||
if prop.name == 'widescreen-mode':
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ from .statemanager import StateManager
|
|||
from .connecteddevice import ConnectedDevice
|
||||
from .failedverification import FailedVerification
|
||||
from .nodevice import NoDevice
|
||||
from .nodriver import NoDriver
|
||||
from .noextension import NoExtension
|
||||
from .nolicense import NoLicense
|
||||
from .verify import verify_installation
|
||||
|
|
@ -49,6 +50,7 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
self.connected_device = ConnectedDevice()
|
||||
self.failed_verification = FailedVerification()
|
||||
self.no_device = NoDevice()
|
||||
self.no_driver = NoDriver()
|
||||
self.no_extension = NoExtension()
|
||||
self.no_license = NoLicense()
|
||||
|
||||
|
|
@ -77,15 +79,17 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
|
|||
if not verify_installation():
|
||||
self.main_content.append(self.failed_verification)
|
||||
|
||||
if not self.state_manager.get_property('license-present'):
|
||||
if not self.state_manager.driver_running:
|
||||
self.main_content.append(self.no_driver)
|
||||
elif not state_manager.connected_device_name:
|
||||
self.main_content.append(self.no_device)
|
||||
elif not self.state_manager.license_present:
|
||||
self.main_content.append(self.no_license)
|
||||
elif not ExtensionsManager.get_instance().is_installed():
|
||||
self.main_content.append(self.no_extension)
|
||||
elif state_manager.connected_device_name:
|
||||
else:
|
||||
self.main_content.append(self.connected_device)
|
||||
self.connected_device.set_device_name(state_manager.connected_device_name)
|
||||
else:
|
||||
self.main_content.append(self.no_device)
|
||||
|
||||
def _on_license_button_clicked(self, widget):
|
||||
dialog = LicenseDialog()
|
||||
|
|
|
|||
Loading…
Reference in New Issue