Merge branch 'main' into gnome-44-max

This commit is contained in:
wheaney 2024-07-24 11:56:24 -07:00
commit 6d6a658353
25 changed files with 606 additions and 255 deletions

View File

@ -16,11 +16,19 @@ There are two installations at the moment. **Note: Only install one of these at
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 (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.
### GNOME Setup ### GNOME Setup
1. Ensure you have the latest graphics drivers installed for your distro.
2. 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`) For the best performance, ensure you have the latest graphics drivers installed for your distro. Also, double-check that your glasses are extending your workspace and not just mirroring your primary monitor by opening up the `Displays` settings dialog and choosing the `Join` option for multiple displays.
3. Run the setup script (e.g. `~/Downloads/breezy_gnome_setup`)
4. You'll have an application called `Breezy Desktop` installed. Launch that and follow any instructions. You will need to log out and back in at least once to get the GNOME extension working. #### Arch Linux
5. For a double-wide screen, enable "widescreen mode" using the toggle in the Breezy Desktop application. **Note: this can be significantly more resource intensive than non-widescreen, you may notice performance dips on older hardware**
*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`
#### 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`)
### Non-GNOME Setup ### 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: 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:
@ -29,7 +37,10 @@ A workable solution (with some [QoL improvements needed](#upcoming-features)) is
3. Launch the nested GNOME Shell using `MUTTER_DEBUG_DUMMY_MODE_SPECS="1920x1080@60" dbus-run-session -- gnome-shell --nested` 3. Launch the nested GNOME Shell using `MUTTER_DEBUG_DUMMY_MODE_SPECS="1920x1080@60" dbus-run-session -- gnome-shell --nested`
### Breezy GNOME Usage ### Breezy GNOME Usage
All controls are provided through the Breezy Desktop application. You can also configure keyboard shortcuts for the most common toggle actions. The Breezy Desktop app doesn't have to be running to use the virtual desktop or the keyboard shortcuts once you've configured everything to your liking.
After setup, you'll have an application called `Breezy Desktop` installed. Launch that and follow any instructions. You will need to log out and back in at least once to get the GNOME extension working. You can also configure keyboard shortcuts for the most common toggle actions. The Breezy Desktop app doesn't have to be running to use the virtual desktop or the keyboard shortcuts once you've configured everything to your liking.
For a double-wide screen, enable "widescreen mode" using the toggle in the Breezy Desktop application. **Note: this can be significantly more resource intensive than non-widescreen, you may notice performance dips on older hardware.**
### Upcoming Features ### Upcoming Features
1. Port to GNOME 43/44 1. Port to GNOME 43/44
@ -76,7 +87,7 @@ If you don't receive a token, you can request one in the `License Details` view
#### Steam Deck via Decky Loader #### Steam Deck via Decky Loader
For Steam Deck users, the driver is now available via the [Decky plugin loader](https://github.com/SteamDeckHomebrew/decky-loader). Just search "xreal" in the Decky store to install and use without leaving Gaming Mode. You can now enable or disable the driver and manage other driver settings via the Decky sidebar menu. For Steam Deck users, the driver is now available via the [Decky plugin loader](https://github.com/SteamDeckHomebrew/decky-loader). Just search "xr" in the Decky store to install and use without leaving Gaming Mode. You can now enable or disable the driver and manage other driver settings via the Decky sidebar menu.
You may still opt to do a manual installation using the instructions below if you enter Desktop Mode. You may still opt to do a manual installation using the instructions below if you enter Desktop Mode.
@ -91,13 +102,13 @@ See [XRLinuxDriver's supported devices](https://github.com/wheaney/XRLinuxDriver
### Usage ### Usage
Once installed, you'll want to make sure you've enabled the driver (`~/bin/xreal_driver_config -e`) and then you can go into whichever output mode you'd like using (`~/bin/xreal_driver_config -m`) where `-m` is for mouse mode, `-j` for joystick, `-vd` for virtual display, and `-sv` for sideview; note that these two commands can't be combined, they have to be done separately. From there, you should be able to launch any Vulkan game, plug in your glasses (at any point, not just after launching), and see a floating virtual display or a sideview screen (depending on which mode you've chosen). Once installed, you'll want to make sure you've enabled the driver (`xr_driver_cli -e`) and then you can go into whichever output mode you'd like using (`xr_driver_cli -m`) where `-m` is for mouse mode, `-j` for joystick, `-vd` for virtual display, and `-sv` for sideview; note that these two commands can't be combined, they have to be done separately. From there, you should be able to launch any Vulkan game, plug in your glasses (at any point, not just after launching), and see a floating virtual display or a sideview screen (depending on which mode you've chosen).
There's a wait period of 15 seconds after plugging in XREAL glasses where the screen will stay static to allow for the glasses to calibrate. Once ready, the screen will anchor to the space where you are looking. There's a wait period of 15 seconds after plugging in XREAL glasses where the screen will stay static to allow for the glasses to calibrate. Once ready, the screen will anchor to the space where you are looking.
### Configurations ### Configurations
To see all the configuration options available to you, type `~/bin/xreal_driver_config` with no parameters to get the usage statement. There are some things you can't trigger from the script, like re-centering the virtual display or entering SBS mode; you can achieve these things through multi-tap or through the physical buttons on the glasses, respectively. To see all the configuration options available to you, type `xr_driver_cli` with no parameters to get the usage statement. There are some things you can't trigger from the script, like re-centering the virtual display or entering SBS mode; you can achieve these things through multi-tap or through the physical buttons on the glasses, respectively.
#### Multi-tap to re-center or re-calibrate #### Multi-tap to re-center or re-calibrate
I've implemented an experimental multi-tap detection feature for screen **re-centering (2 taps)** and **re-calibrating the device (3 taps)**. To perform a multi-tap, you'll want to give decent taps on the top of the glasses. I tend to do this on the corner, right on top of the hinge. It should be a firm, sharp tap, and wait just a split second to do the second tap, as it needs to detect a slight pause in between (but it also shouldn't take more than a half a second between taps so don't wait too long). I've implemented an experimental multi-tap detection feature for screen **re-centering (2 taps)** and **re-calibrating the device (3 taps)**. To perform a multi-tap, you'll want to give decent taps on the top of the glasses. I tend to do this on the corner, right on top of the hinge. It should be a firm, sharp tap, and wait just a split second to do the second tap, as it needs to detect a slight pause in between (but it also shouldn't take more than a half a second between taps so don't wait too long).
@ -124,18 +135,18 @@ Features currently offered:
If you donate at least $10, you should immediately receive an email (to your Ko-fi email address) with a verification token. If you don't, request it using the config script: If you donate at least $10, you should immediately receive an email (to your Ko-fi email address) with a verification token. If you don't, request it using the config script:
```bash ```bash
~/bin/xreal_driver_config --request-token [emailAddress] xr_driver_cli --request-token [emailAddress]
``` ```
Once you have a token, verify it using: Once you have a token, verify it using:
```bash ```bash
~/bin/xreal_driver_config --verify-token [token] xr_driver_cli --verify-token [token]
~/bin/xreal_driver_config --refresh-license xr_driver_cli --refresh-license
``` ```
### Disabling ### Disabling
To disable the floating screen effect, either disable the driver (`~/bin/xreal_driver_config -d`), unplug the glasses, or hit the `Home` key (you'll need to bind this to your controller, if on Steam Deck). To disable the floating screen effect, either disable the driver (`xr_driver_cli -d`), unplug the glasses, or hit the `Home` key (you'll need to bind this to your controller, if on Steam Deck).
### Updating ### Updating
@ -143,7 +154,11 @@ Rerun the `breezy_vulkan_setup` script. No need to re-download this script, as i
### Uninstalling ### Uninstalling
If you wish to completely remove the installation, run the following: `sudo ~/bin/breezy_vulkan_uninstall`. This won't uninstall the base driver package, follow the instructions at the end of the uninstallation to do this manually. If you wish to completely remove the installation:
* For **Breezy GNOME**:
* If you installed *via the setup script* run the following: `~/.local/bin/breezy_gnome_uninstall`
* If you installed via `yay` run the following: `pacman -R breezy-desktop-gnome-git`, you may also want to uninstall the base driver with `pacman -R xr-driver-breezy-gnome-git`
* For **Breezy Vulkan** run the following: `~/.local/bin/breezy_vulkan_uninstall`. This won't uninstall the base driver package, follow the instructions at the end of the uninstallation to do this manually.
## Data Privacy Notice ## Data Privacy Notice

View File

@ -13,19 +13,21 @@ fi
start_dir=$(pwd) start_dir=$(pwd)
ARCH=$(uname -m)
# create temp directory # create temp directory
tmp_dir=$(mktemp -d -t breezy-gnome-XXXXXXXXXX) tmp_dir=$(mktemp -d -t breezy-gnome-XXXXXXXXXX)
pushd $tmp_dir > /dev/null pushd $tmp_dir > /dev/null
echo "Created temp directory: ${tmp_dir}" echo "Created temp directory: ${tmp_dir}"
binary_download_url="https://github.com/wheaney/breezy-desktop/releases/latest/download/breezyGNOME.tar.gz" binary_download_url="https://github.com/wheaney/breezy-desktop/releases/latest/download/breezyGNOME-$ARCH.tar.gz"
if [ "$1" = "-v" ] if [ "$1" = "-v" ]
then then
metrics_version="$2" metrics_version="$2"
binary_path_arg="$3" binary_path_arg="$3"
elif [ "$1" = "--tag" ] && [ -n "$2" ] elif [ "$1" = "--tag" ] && [ -n "$2" ]
then then
binary_download_url="https://github.com/wheaney/breezy-desktop/releases/download/$2/breezyGNOME.tar.gz" binary_download_url="https://github.com/wheaney/breezy-desktop/releases/download/$2/breezyGNOME-$ARCH.tar.gz"
else else
binary_path_arg="$1" binary_path_arg="$1"
fi fi
@ -33,8 +35,9 @@ fi
if [ -z "$binary_path_arg" ] if [ -z "$binary_path_arg" ]
then then
# download and unzip the binary # download and unzip the binary
echo "Downloading to: ${tmp_dir}/breezyGNOME.tar.gz" echo "Downloading to: ${tmp_dir}/breezyGNOME-$ARCH.tar.gz"
curl -L -O $binary_download_url curl -L -O $binary_download_url
binary_path_arg="breezyGNOME-$ARCH.tar.gz"
else else
if [[ "$binary_path_arg" = /* ]]; then if [[ "$binary_path_arg" = /* ]]; then
abs_path="$binary_path_arg" abs_path="$binary_path_arg"
@ -45,16 +48,8 @@ else
cp $abs_path $tmp_dir cp $abs_path $tmp_dir
fi fi
# if abs_path is present, grab the filename from it
if [ -n "$abs_path" ]
then
filename=$(basename $abs_path)
else
filename="breezyGNOME.tar.gz"
fi
echo "Extracting to: ${tmp_dir}/breezy_gnome" echo "Extracting to: ${tmp_dir}/breezy_gnome"
tar -xf $filename tar -xf $(basename $binary_path_arg)
pushd breezy_gnome > /dev/null pushd breezy_gnome > /dev/null

View File

@ -14,17 +14,25 @@ fi
start_dir=$(pwd) start_dir=$(pwd)
ARCH=$(uname -m)
if [ "$ARCH" != "x86_64" ]; then
echo "Breezy Vulkan only supports x86_64 currently"
exit 1
fi
# create temp directory # create temp directory
tmp_dir=$(mktemp -d -t breezy-vulkan-XXXXXXXXXX) tmp_dir=$(mktemp -d -t breezy-vulkan-XXXXXXXXXX)
pushd $tmp_dir > /dev/null pushd $tmp_dir > /dev/null
echo "Created temp directory: ${tmp_dir}" echo "Created temp directory: ${tmp_dir}"
# if the first argument is "-v" then the second argument is metrics version, and the third argument is binary path binary_download_url="https://github.com/wheaney/breezy-desktop/releases/latest/download/breezyVulkan-$ARCH.tar.gz"
# otherwise, if the first argument is present, it's the binary path
if [ "$1" = "-v" ] if [ "$1" = "-v" ]
then then
metrics_version="$2" metrics_version="$2"
binary_path_arg="$3" binary_path_arg="$3"
elif [ "$1" = "--tag" ] && [ -n "$2" ]
then
binary_download_url="https://github.com/wheaney/breezy-desktop/releases/download/$2/breezyVulkan-$ARCH.tar.gz"
else else
binary_path_arg="$1" binary_path_arg="$1"
fi fi
@ -32,8 +40,9 @@ fi
if [ -z "$binary_path_arg" ] if [ -z "$binary_path_arg" ]
then then
# download and unzip the latest driver # download and unzip the latest driver
echo "Downloading latest release to: ${tmp_dir}/breezyVulkan.tar.gz" echo "Downloading to: ${tmp_dir}/breezyVulkan-$ARCH.tar.gz"
curl -L -O https://github.com/wheaney/breezy-desktop/releases/latest/download/breezyVulkan.tar.gz curl -L -O $binary_download_url
binary_path_arg="breezyVulkan-$ARCH.tar.gz"
else else
if [[ "$binary_path_arg" = /* ]]; then if [[ "$binary_path_arg" = /* ]]; then
abs_path="$binary_path_arg" abs_path="$binary_path_arg"
@ -45,7 +54,7 @@ else
fi fi
echo "Extracting to: ${tmp_dir}/breezy_vulkan" echo "Extracting to: ${tmp_dir}/breezy_vulkan"
tar -xf breezyVulkan.tar.gz tar -xf $(basename $binary_path_arg)
pushd breezy_vulkan > /dev/null pushd breezy_vulkan > /dev/null

View File

@ -3,25 +3,28 @@
# exit when any command fails # exit when any command fails
set -e set -e
ARCH=${ARCH:-$(uname -m)}
echo "Building Breezy GNOME for $ARCH"
# https://stackoverflow.com/a/246128 # https://stackoverflow.com/a/246128
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
ROOT_DIR=$(realpath $SCRIPT_DIR/..) ROOT_DIR=$(realpath $SCRIPT_DIR/..)
VULKAN_DIR=$ROOT_DIR/vulkan VULKAN_DIR=$ROOT_DIR/vulkan
GNOME_DIR=$ROOT_DIR/gnome GNOME_DIR=$ROOT_DIR/gnome
GNOME_BUILD_DIR=$GNOME_DIR/build GNOME_BUILD_DIR=$GNOME_DIR/build
BUILD_FILE_NAME=breezyGNOME-$ARCH.tar.gz
rm -rf $GNOME_BUILD_DIR
mkdir -p $GNOME_BUILD_DIR mkdir -p $GNOME_BUILD_DIR
if [ -e "$GNOME_BUILD_DIR/$BUILD_FILE_NAME" ]; then
rm $GNOME_BUILD_DIR/$BUILD_FILE_NAME
fi
PACKAGE_DIR=$GNOME_BUILD_DIR/breezy_gnome PACKAGE_DIR=$GNOME_BUILD_DIR/breezy_gnome
rm -rf $PACKAGE_DIR rm -rf $PACKAGE_DIR
mkdir -p $PACKAGE_DIR mkdir -p $PACKAGE_DIR
XREAL_DRIVER_DIR=$ROOT_DIR/modules/XRLinuxDriver XR_DRIVER_DIR=$ROOT_DIR/modules/XRLinuxDriver
source $XREAL_DRIVER_DIR/bin/inject_ua source $XR_DRIVER_DIR/bin/inject_ua
# check out submodules, recursively for nested ones
git submodule update --init --recursive
# if a custom_banner image exists, copy it over the sombrero one # if a custom_banner image exists, copy it over the sombrero one
if [ -e "$VULKAN_DIR/custom_banner.png" ]; then if [ -e "$VULKAN_DIR/custom_banner.png" ]; then
@ -30,48 +33,63 @@ fi
# copy vulkan setup scripts and configs # copy vulkan setup scripts and configs
mkdir -p $PACKAGE_DIR/bin mkdir -p $PACKAGE_DIR/bin
copy_and_inject_ua "$XREAL_DRIVER_DIR/bin/ua.sh" "$PACKAGE_DIR/bin" "$GNOME_DIR/bin/setup" "$GNOME_DIR/bin/breezy_gnome_verify" "$GNOME_DIR/bin/breezy_gnome_uninstall" copy_and_inject_ua "$XR_DRIVER_DIR/bin/ua.sh" "$PACKAGE_DIR/bin" "$GNOME_DIR/bin/setup" "$GNOME_DIR/bin/breezy_gnome_verify" "$GNOME_DIR/bin/breezy_gnome_uninstall"
XREAL_BINARY=$XREAL_DRIVER_DIR/build/xrealAirLinuxDriver.tar.gz XR_DRIVER_BINARY=$XR_DRIVER_DIR/out/xrDriver-$ARCH.tar.gz
pushd $XREAL_DRIVER_DIR
if [ ! -e "$XREAL_BINARY" ] || [ "$1" != "--skip-module-builds" ]; then if [ ! -e "$XR_DRIVER_BINARY" ] || [ "$1" == "--rebuild-driver" ]; then
# if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory # if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory
if [ -e "$VULKAN_DIR/custom_banner_config.yml" ]; then if [ -e "$VULKAN_DIR/custom_banner_config.yml" ]; then
cp $VULKAN_DIR/custom_banner_config.yml $XREAL_DRIVER_DIR cp $VULKAN_DIR/custom_banner_config.yml $XR_DRIVER_DIR
fi fi
pushd $XR_DRIVER_DIR
# strange issue where the base library produces a .so file if the build is not cleaned # strange issue where the base library produces a .so file if the build is not cleaned
rm -rf build/ rm -rf build/
BREEZY_DESKTOP=1 bin/package docker-build/init.sh
BREEZY_DESKTOP=1 docker-build/run-build.sh $ARCH
popd
fi fi
XREAL_MANIFEST_LINE=$(sha256sum build/driver_air_glasses/manifest) TMP_DIR=$(mktemp -d -t breezy-gnome-XXXXXXXXXX)
popd pushd $TMP_DIR
cp $XR_DRIVER_BINARY $TMP_DIR/xrDriver.tar.gz
tar -xf $TMP_DIR/xrDriver.tar.gz
cp $XREAL_BINARY $PACKAGE_DIR XR_DRIVER_MANIFEST_LINE=$(sha256sum xr_driver/manifest)
cp $XREAL_DRIVER_DIR/bin/xreal_driver_setup $PACKAGE_DIR/bin popd
rm -rf $TMP_DIR
cp $XR_DRIVER_BINARY $PACKAGE_DIR/xrDriver.tar.gz
cp $XR_DRIVER_DIR/bin/xr_driver_setup $PACKAGE_DIR/bin
gnome/bin/package_extension gnome/bin/package_extension
cp gnome/out/* $PACKAGE_DIR cp gnome/out/breezydesktop@xronlinux.com.shell-extension.zip $PACKAGE_DIR
# create a checksum that combines the checksums of all files in the directory # create a checksum that combines the checksums of all files in the directory
pushd gnome/src pushd gnome/src
GNOME_MANIFEST_LINE=$(find -L . -type f ! -name "*.compiled" -exec sha256sum {} \; | sort | sha256sum | sed 's/ .*//') GNOME_MANIFEST_LINE=$(find -L . -type f ! -name "*.compiled" -exec sha256sum {} \; | sort | sha256sum | sed 's/ .*//')
popd popd
ui/bin/package ui/bin/package $ARCH
cp ui/out/* $PACKAGE_DIR cp ui/out/com.xronlinux.BreezyDesktop-$ARCH.flatpak $PACKAGE_DIR/com.xronlinux.BreezyDesktop.flatpak
# create manifest file for verifying installed file checksums against the originally packaged versions # 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) # include any file that doesn't get modified during setup (e.g. vkBasalt.json files)
pushd $PACKAGE_DIR pushd $PACKAGE_DIR
echo $XREAL_MANIFEST_LINE > manifest echo $XR_DRIVER_MANIFEST_LINE > manifest
echo -e "$GNOME_MANIFEST_LINE breezydesktop@xronlinux.com" >> manifest echo -e "$GNOME_MANIFEST_LINE breezydesktop@xronlinux.com" >> manifest
popd popd
# bundle everything up # bundle everything up
pushd $GNOME_BUILD_DIR pushd $GNOME_BUILD_DIR
tar -zcvf breezyGNOME.tar.gz breezy_gnome tar -zcvf $BUILD_FILE_NAME breezy_gnome
popd popd
mkdir -p out
if [ -e "out/$BUILD_FILE_NAME" ]; then
rm out/$BUILD_FILE_NAME
fi
cp $GNOME_BUILD_DIR/$BUILD_FILE_NAME out

View File

@ -3,27 +3,28 @@
# exit when any command fails # exit when any command fails
set -e set -e
XREAL_DRIVER_DIR=modules/XRLinuxDriver ARCH=${ARCH:-$(uname -m)}
source $XREAL_DRIVER_DIR/bin/inject_ua echo "Building Breezy Vulkan for $ARCH"
# check out submodules, recursively for nested ones XR_DRIVER_DIR=modules/XRLinuxDriver
git submodule update --init --recursive source $XR_DRIVER_DIR/bin/inject_ua
VULKAN_DIR=vulkan VULKAN_DIR=vulkan
VULKAN_BUILD=$VULKAN_DIR/build VULKAN_BUILD_DIR=$VULKAN_DIR/build
if [ ! -d "$VULKAN_BUILD" ]; then if [ ! -d "$VULKAN_BUILD_DIR" ]; then
mkdir -p $VULKAN_BUILD mkdir -p $VULKAN_BUILD_DIR
else else
rm -rf $VULKAN_BUILD/* rm -rf $VULKAN_BUILD_DIR/*
fi fi
VULKAN_MODULES=$VULKAN_DIR/modules VULKAN_MODULES=$VULKAN_DIR/modules
PACKAGE_DIR=$VULKAN_BUILD/breezy_vulkan PACKAGE_DIR=$VULKAN_BUILD_DIR/breezy_vulkan
if [ ! -d "$PACKAGE_DIR" ]; then if [ ! -d "$PACKAGE_DIR" ]; then
mkdir -p $PACKAGE_DIR mkdir -p $PACKAGE_DIR
else else
rm -rf $PACKAGE_DIR/* rm -rf $PACKAGE_DIR/*
fi fi
BUILD_FILE_NAME=breezyVulkan-$ARCH.tar.gz
# build vkBasalt # build vkBasalt
VKBASALT_MODULE_DIR=$VULKAN_MODULES/vkBasalt VKBASALT_MODULE_DIR=$VULKAN_MODULES/vkBasalt
@ -53,41 +54,55 @@ fi
# copy vulkan setup scripts and configs # copy vulkan setup scripts and configs
mkdir -p $PACKAGE_DIR/bin mkdir -p $PACKAGE_DIR/bin
copy_and_inject_ua "$XREAL_DRIVER_DIR/bin/ua.sh" "$PACKAGE_DIR/bin" "$VULKAN_DIR/bin/setup" "$VULKAN_DIR/bin/verify_installation" "$VULKAN_DIR/bin/breezy_vulkan_uninstall" copy_and_inject_ua "$XR_DRIVER_DIR/bin/ua.sh" "$PACKAGE_DIR/bin" "$VULKAN_DIR/bin/setup" "$VULKAN_DIR/bin/breezy_vulkan_verify" "$VULKAN_DIR/bin/breezy_vulkan_uninstall"
cp -r $VULKAN_DIR/config $PACKAGE_DIR cp -r $VULKAN_DIR/config $PACKAGE_DIR
# build xreal driver # build XR driver
XREAL_BINARY=$XREAL_DRIVER_DIR/build/xrealAirLinuxDriver.tar.gz XR_DRIVER_BINARY=$XR_DRIVER_DIR/out/xrDriver-$ARCH.tar.gz
if [ ! -e "$XREAL_BINARY" ] || [ "$1" != "--skip-module-builds" ]; then if [ ! -e "$XR_DRIVER_BINARY" ] || [ "$1" != "--skip-module-builds" ]; then
# if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory # if a file exists at custom_banner_config.yml, copy it to the xrealAirLinuxDriver directory
if [ -e "$VULKAN_DIR/custom_banner_config.yml" ]; then if [ -e "$VULKAN_DIR/custom_banner_config.yml" ]; then
cp $VULKAN_DIR/custom_banner_config.yml $XREAL_DRIVER_DIR cp $VULKAN_DIR/custom_banner_config.yml $XR_DRIVER_DIR
fi fi
pushd $XREAL_DRIVER_DIR pushd $XR_DRIVER_DIR
# strange issue where the base library produces a .so file if the build is not cleaned # strange issue where the base library produces a .so file if the build is not cleaned
rm -rf build/ rm -rf build/
bin/package docker-build/init.sh
else docker-build/run-build.sh $ARCH
pushd $XREAL_DRIVER_DIR popd
fi fi
XREAL_MANIFEST_LINE=$(sha256sum build/driver_air_glasses/manifest) TMP_DIR=$(mktemp -d -t breezy-vulkan-XXXXXXXXXX)
popd cp $XR_DRIVER_BINARY $TMP_DIR/xrDriver.tar.gz
pushd $TMP_DIR
tar -xf $TMP_DIR/xrDriver.tar.gz
# copy xreal binary and setup script XR_DRIVER_MANIFEST_LINE=$(sha256sum xr_driver/manifest)
cp $XREAL_BINARY $PACKAGE_DIR popd
cp $XREAL_DRIVER_DIR/bin/xreal_driver_setup $PACKAGE_DIR/bin rm -rf $TMP_DIR
# copy XR driver binary and setup script
cp $XR_DRIVER_BINARY $PACKAGE_DIR/xrDriver.tar.gz
cp $XR_DRIVER_DIR/bin/xr_driver_setup $PACKAGE_DIR/bin
# create manifest file for verifying installed file checksums against the originally packaged versions # 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) # include any file that doesn't get modified during setup (e.g. vkBasalt.json files)
pushd $PACKAGE_DIR pushd $PACKAGE_DIR
echo $XREAL_MANIFEST_LINE > manifest echo $XR_DRIVER_MANIFEST_LINE > manifest
sha256sum bin/breezy_vulkan_uninstall vkBasalt.64/libvkbasalt.so vkBasalt.32/libvkbasalt.so *.fx* *.png >> manifest sha256sum bin/breezy_vulkan_uninstall vkBasalt.64/libvkbasalt.so vkBasalt.32/libvkbasalt.so *.fx* *.png >> manifest
popd popd
# bundle everything up # bundle everything up
tar -zcvf $VULKAN_BUILD/breezyVulkan.tar.gz --directory $VULKAN_BUILD breezy_vulkan pushd $VULKAN_BUILD_DIR
tar -zcvf $BUILD_FILE_NAME breezy_vulkan
popd
mkdir -p out
if [ -e "out/$BUILD_FILE_NAME" ]; then
rm out/$BUILD_FILE_NAME
fi
cp $VULKAN_BUILD_DIR/$BUILD_FILE_NAME out

View File

@ -42,9 +42,9 @@ flatpak uninstall --user --noninteractive --force-remove com.xronlinux.BreezyDe
[ "$for_install" -eq 0 ] && echo "Uninstalling XRLinuxDriver" [ "$for_install" -eq 0 ] && echo "Uninstalling XRLinuxDriver"
# if for-install # if for-install
if [ "$for_install" -eq 1 ]; then if [ "$for_install" -eq 1 ]; then
sudo ~/bin/xreal_driver_uninstall --for-install sudo ~/.local/bin/xr_driver_uninstall --for-install
else else
sudo ~/bin/xreal_driver_uninstall sudo ~/.local/bin/xr_driver_uninstall
fi fi

View File

@ -2,18 +2,11 @@
set -e set -e
USER_HOME=$(realpath ~)
if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share"
fi
DATA_DIR="$XDG_DATA_HOME/breezy_gnome"
# create a string to string mapping, file name to expected file location # create a string to string mapping, file name to expected file location
declare -A file_paths declare -A file_paths
file_paths=( file_paths=(
["build/driver_air_glasses/manifest"]="$USER_HOME/.local/bin/xr_driver/manifest" ["xr_driver/manifest"]="{xr_driver_data_dir}/manifest"
["breezydesktop@xronlinux.com"]="$XDG_DATA_HOME/gnome-shell/extensions/breezydesktop@xronlinux.com" ["breezydesktop@xronlinux.com"]="{gnome_shell_data_dir}/extensions/breezydesktop@xronlinux.com"
) )
# verify the file hashes in ./manifest # verify the file hashes in ./manifest
@ -41,9 +34,9 @@ do
echo "Verification failed" >&2 echo "Verification failed" >&2
exit 1 exit 1
fi fi
done < "$XDG_DATA_HOME/breezy_gnome/manifest" done < "{data_dir}/manifest"
# if our checks succeeded, run the xr_driver verify script # if our checks succeeded, run the xr_driver verify script
$USER_HOME/.local/bin/xr_driver/verify_installation > /dev/null {bin_dir}/xr_driver_verify > /dev/null
echo "Verification succeeded" echo "Verification succeeded"

View File

@ -5,11 +5,6 @@ UUID="breezydesktop@xronlinux.com"
# fail on error # fail on error
set -e set -e
if [[ $EUID -eq 0 ]]; then
echo "This script must NOT be run as root" 1>&2
exit 1
fi
# https://stackoverflow.com/a/246128 # https://stackoverflow.com/a/246128
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)

View File

@ -17,9 +17,17 @@ check_command "gnome-extensions"
USER_HOME=$(realpath ~) USER_HOME=$(realpath ~)
if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share"
fi
XR_DRIVER_DATA_DIR="$XDG_DATA_HOME/xr_driver"
GNOME_SHELL_DATA_DIR="$XDG_DATA_HOME/gnome-shell"
DATA_DIR="$XDG_DATA_HOME/breezy_gnome"
if [ -z "$XDG_BIN_HOME" ]; then if [ -z "$XDG_BIN_HOME" ]; then
XDG_BIN_HOME="$USER_HOME/.local/bin" XDG_BIN_HOME="$USER_HOME/.local/bin"
fi fi
BIN_DIR="$XDG_BIN_HOME"
if [ -d "$XDG_BIN_HOME" ]; then if [ -d "$XDG_BIN_HOME" ]; then
# check ownership and permissions before doing chown and chmod # check ownership and permissions before doing chown and chmod
@ -36,11 +44,6 @@ if [ -d "$XDG_BIN_HOME" ]; then
fi fi
fi fi
if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share"
fi
DATA_DIR="$XDG_DATA_HOME/breezy_gnome"
UA_EVENT_NAME="breezy_gnome_install" UA_EVENT_NAME="breezy_gnome_install"
if [ -e "$XDG_BIN_HOME/breezy_gnome_uninstall" ]; then if [ -e "$XDG_BIN_HOME/breezy_gnome_uninstall" ]; then
echo "Cleaning up the previous installation" echo "Cleaning up the previous installation"
@ -55,9 +58,20 @@ UA_CLIENT_ID="BreezyGNOME"
UA_EVENT_VERSION="$1" UA_EVENT_VERSION="$1"
#INJECT_UA_CALL #INJECT_UA_CALL
# escaping sed replace: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
ESCAPED_BIN_DIR=$(printf '%s\n' "$BIN_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_DATA_DIR=$(printf '%s\n' "$DATA_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_XR_DRIVER_DATA_DIR=$(printf '%s\n' "$XR_DRIVER_DATA_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_GNOME_SHELL_DATA_DIR=$(printf '%s\n' "$GNOME_SHELL_DATA_DIR" | sed -e 's/[\/&]/\\&/g')
echo "Copying the breezy_gnome scripts to ${XDG_BIN_HOME}" echo "Copying the breezy_gnome scripts to ${XDG_BIN_HOME}"
mkdir -p $XDG_BIN_HOME mkdir -p $XDG_BIN_HOME
cp bin/breezy_gnome_uninstall $XDG_BIN_HOME cp bin/breezy_gnome_uninstall $XDG_BIN_HOME
sed -i -e "s/{bin_dir}/$ESCAPED_BIN_DIR/g" \
-e "s/{data_dir}/$ESCAPED_DATA_DIR/g" \
-e "s/{xr_driver_data_dir}/$ESCAPED_XR_DRIVER_DATA_DIR/g" \
-e "s/{gnome_shell_data_dir}/$ESCAPED_GNOME_SHELL_DATA_DIR/g" \
bin/breezy_gnome_verify
cp bin/breezy_gnome_verify $XDG_BIN_HOME cp bin/breezy_gnome_verify $XDG_BIN_HOME
echo "Copying the manifest file to ${DATA_DIR}" echo "Copying the manifest file to ${DATA_DIR}"
@ -70,20 +84,14 @@ gnome-extensions install --force breezydesktop@xronlinux.com.shell-extension.zip
echo "Installing the Breezy Desktop UI Flatpak (this may take a couple minutes the first time)" echo "Installing the Breezy Desktop UI Flatpak (this may take a couple minutes the first time)"
flatpak install --user --noninteractive --reinstall com.xronlinux.BreezyDesktop.flatpak flatpak install --user --noninteractive --reinstall com.xronlinux.BreezyDesktop.flatpak
# set up the XREAL driver using the local binary # set up the XR driver using the local binary
echo "Installing xrealAirLinuxDriver" echo "Installing xrDriver"
echo "BEGIN - xreal_driver_setup" echo "BEGIN - xr_driver_setup"
if [ -z "$1" ] if [ -z "$1" ]
then then
sudo bin/xreal_driver_setup $(pwd)/xrealAirLinuxDriver.tar.gz sudo bin/xr_driver_setup $(pwd)/xrDriver.tar.gz
else else
sudo bin/xreal_driver_setup -v $1 $(pwd)/xrealAirLinuxDriver.tar.gz sudo bin/xr_driver_setup -v $1 $(pwd)/xrDriver.tar.gz
fi fi
echo "END - xreal_driver_setup" echo "END - xr_driver_setup"
echo "Enabling the driver and setting it to Breezy Desktop mode"
$USER_HOME/bin/xreal_driver_config -e
$USER_HOME/bin/xreal_driver_config -vd
sed -i 's/virtual_display/breezy_desktop/g' $USER_HOME/.xreal_driver_config

View File

@ -17,13 +17,15 @@ const { MonitorManager } = Me.imports.monitormanager;
const { isValidKeepAlive } = Me.imports.time; const { isValidKeepAlive } = Me.imports.time;
const { IPC_FILE_PATH, XREffect } = Me.imports.xrEffect; const { IPC_FILE_PATH, XREffect } = Me.imports.xrEffect;
const NESTED_MONITOR_PRODUCT = 'MetaMonitor';
const SUPPORTED_MONITOR_PRODUCTS = [ const SUPPORTED_MONITOR_PRODUCTS = [
'VITURE', 'VITURE',
'nreal air', 'nreal air',
'Air', 'Air',
'Air 2', // guessing this one 'Air 2', // guessing this one
'Air 2 Pro', 'Air 2 Pro',
'SmartGlasses' // TCL/RayNeo 'SmartGlasses', // TCL/RayNeo
NESTED_MONITOR_PRODUCT
]; ];
class BreezyDesktopExtension { class BreezyDesktopExtension {
@ -72,6 +74,7 @@ class BreezyDesktopExtension {
this._monitor_manager = new MonitorManager({ this._monitor_manager = new MonitorManager({
use_optimal_monitor_config: this.settings.get_boolean('use-optimal-monitor-config'), use_optimal_monitor_config: this.settings.get_boolean('use-optimal-monitor-config'),
headset_as_primary: this.settings.get_boolean('headset-as-primary'), headset_as_primary: this.settings.get_boolean('headset-as-primary'),
use_highest_refresh_rate: this.settings.get_boolean('use-highest-refresh-rate'),
extension_path: this.path extension_path: this.path
}); });
this._monitor_manager.setChangeHook(this._handle_monitor_change.bind(this)); this._monitor_manager.setChangeHook(this._handle_monitor_change.bind(this));
@ -121,11 +124,12 @@ class BreezyDesktopExtension {
const target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find( const target_monitor = this._monitor_manager.getMonitorPropertiesList()?.find(
monitor => SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product)); monitor => SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product));
if (target_monitor !== undefined) { if (target_monitor !== undefined) {
Globals.logger.log_debug(`BreezyDesktopExtension _find_supported_monitor - Identified supported monitor: ${target_monitor.connector}`); Globals.logger.log(`Identified supported monitor: ${target_monitor.product} on ${target_monitor.connector}`);
return { return {
monitor: this._monitor_manager.getMonitors()[target_monitor.index], monitor: this._monitor_manager.getMonitors()[target_monitor.index],
connector: target_monitor.connector, connector: target_monitor.connector,
refreshRate: target_monitor.refreshRate refreshRate: target_monitor.refreshRate,
is_dummy: target_monitor.product === NESTED_MONITOR_PRODUCT
}; };
} }
@ -152,8 +156,11 @@ class BreezyDesktopExtension {
// A false result means we'll expect _handle_monitor_change to be triggered, so active polling // A false result means we'll expect _handle_monitor_change to be triggered, so active polling
// can be disabled. // can be disabled.
_target_monitor_ready(target_monitor) { _target_monitor_ready(target_monitor) {
return target_monitor.is_dummy || if (target_monitor.is_dummy) return true;
!this._monitor_manager.needsOptimalModeCheck(target_monitor.connector);
const needs_sbs_mode_switch = this.settings.get_boolean('fast-sbs-mode-switching') &&
this._needs_widescreen_monitor_update();
return !needs_sbs_mode_switch && !this._monitor_manager.needsOptimalModeCheck(target_monitor.connector);
} }
_setup() { _setup() {
@ -214,7 +221,7 @@ class BreezyDesktopExtension {
const widescreen_setting_enabled = this.settings.get_boolean('widescreen-mode'); const widescreen_setting_enabled = this.settings.get_boolean('widescreen-mode');
if (widescreen_setting_enabled !== sbs_enabled) { if (widescreen_setting_enabled !== sbs_enabled) {
Globals.logger.log_debug('BreezyDesktopExtension _needs_widescreen_monitor_update - true'); Globals.logger.log_debug('BreezyDesktopExtension _needs_widescreen_monitor_update - true');
this._write_control('sbs_mode', widescreen_setting_enabled ? 'enable' : 'disable'); this._request_sbs_mode_change(widescreen_setting_enabled);
return true; return true;
} }
@ -260,7 +267,10 @@ class BreezyDesktopExtension {
}); });
this._update_follow_threshold(this.settings); this._update_follow_threshold(this.settings);
this._update_widescreen_mode_from_settings(this.settings);
// this gets triggered before _effect_enable if in fast-sbs-mode-switching mode
if (!this.settings.get_boolean('fast-sbs-mode-switching'))
this._update_widescreen_mode_from_settings(this.settings);
this._widescreen_mode_effect_state_connection = this._xr_effect.connect('notify::widescreen-mode-state', this._update_widescreen_mode_from_state.bind(this)); this._widescreen_mode_effect_state_connection = this._xr_effect.connect('notify::widescreen-mode-state', this._update_widescreen_mode_from_state.bind(this));
this._supported_device_detected_connected = this._xr_effect.connect('notify::supported-device-detected', this._handle_supported_device_change.bind(this)); this._supported_device_detected_connected = this._xr_effect.connect('notify::supported-device-detected', this._handle_supported_device_change.bind(this));
@ -363,16 +373,41 @@ class BreezyDesktopExtension {
if (value !== undefined) this._write_control('breezy_desktop_follow_threshold', value); if (value !== undefined) this._write_control('breezy_desktop_follow_threshold', value);
} }
// requests sbs_mode change and monitors to ensure the state reflects the setting
_request_sbs_mode_change(value) {
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, (() => {
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
this.settings.set_boolean('widescreen-mode', !value);
this._sbs_mode_update_timeout = undefined;
return GLib.SOURCE_REMOVE;
}).bind(this));
}
}
_update_widescreen_mode_from_settings(settings, event) { _update_widescreen_mode_from_settings(settings, event) {
const value = settings.get_boolean('widescreen-mode'); const value = settings.get_boolean('widescreen-mode');
Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_settings ${value}`); Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_settings ${value}`);
if (value !== undefined && value !== this._xr_effect.widescreen_mode_state) if (value !== undefined && value !== this._xr_effect.widescreen_mode_state) {
this._write_control('sbs_mode', value ? 'enable' : 'disable'); this._request_sbs_mode_change(value);
else } else
Globals.logger.log_debug('effect.widescreen_mode_state already matched setting'); Globals.logger.log_debug('effect.widescreen_mode_state already matched setting');
} }
_update_widescreen_mode_from_state(effect, _pspec) { _update_widescreen_mode_from_state(effect, _pspec) {
// kill our state checker if it's running
if (this._sbs_mode_update_timeout) {
GLib.source_remove(this._sbs_mode_update_timeout);
this._sbs_mode_update_timeout = undefined;
}
const value = effect.widescreen_mode_state; const value = effect.widescreen_mode_state;
Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_state ${value}`); Globals.logger.log_debug(`BreezyDesktopExtension _update_widescreen_mode_from_state ${value}`);
if (value !== this.settings.get_boolean('widescreen-mode')) if (value !== this.settings.get_boolean('widescreen-mode'))

View File

@ -89,9 +89,18 @@ function getMonitorConfig(displayConfigProxy, callback) {
} }
// triggers callback with true result if an an async monitor config change was triggered, false if no config change needed // triggers callback with true result if an an async monitor config change was triggered, false if no config change needed
function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPrimary, callback) { function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPrimary, useHighestRefreshRate,
callback, allowConfigUpdateFn) {
Globals.logger.log_debug(`monitormanager.js performOptimalModeCheck for ${connectorName}`); Globals.logger.log_debug(`monitormanager.js performOptimalModeCheck for ${connectorName}`);
displayConfigProxy.GetCurrentStateRemote((result, error) => { displayConfigProxy.GetCurrentStateRemote((result, error) => {
if (!allowConfigUpdateFn()) {
// other requests are in progress, this monitor state may be stale, do nothing
Globals.logger.log_debug('MonitorManager performOptimalModeCheck: allowConfigUpdate is false');
callback(false, null);
return;
}
if (error) { if (error) {
callback(null, `GetCurrentState failed: ${error}`); callback(null, `GetCurrentState failed: ${error}`);
} else { } else {
@ -104,10 +113,19 @@ function performOptimalModeCheck(displayConfigProxy, connectorName, headsetAsPri
let monitorToModeIdMap = {}; let monitorToModeIdMap = {};
let bestFitMode = undefined; let bestFitMode = undefined;
for (let monitor of monitors) { for (let monitor of monitors) {
const [details, modes, monProperties] = monitor; const [details, availableModes, monProperties] = monitor;
const [connector, vendor, product, monitorSerial] = details; const [connector, vendor, product, monitorSerial] = details;
const isOurMonitor = connector == connectorName; const isOurMonitor = connector == connectorName;
if (isOurMonitor) ourMonitor = monitor; let modes = availableModes;
if (isOurMonitor) {
ourMonitor = monitor;
if (!useHighestRefreshRate) {
const currentMode = modes.find((mode) => !!mode[6]['is-current']);
// filter modes to only include the current refresh rate
modes = availableModes.filter((mode) => mode[3] === currentMode[3]);
}
}
for (let mode of modes) { for (let mode of modes) {
const [modeId, width, height, refreshRate, preferredScale, supportedScales, modeProperites] = mode; const [modeId, width, height, refreshRate, preferredScale, supportedScales, modeProperites] = mode;
@ -202,6 +220,13 @@ var MonitorManager = GObject.registerClass({
GObject.ParamFlags.READWRITE, GObject.ParamFlags.READWRITE,
true true
), ),
'use-highest-refresh-rate': GObject.ParamSpec.boolean(
'use-highest-refresh-rate',
'Use highest refresh rate',
'Set the highest refresh rate which choosing optimal configs',
GObject.ParamFlags.READWRITE,
true
),
'headset-as-primary': GObject.ParamSpec.boolean( 'headset-as-primary': GObject.ParamSpec.boolean(
'headset-as-primary', 'headset-as-primary',
'Use headset as primary monitor', 'Use headset as primary monitor',
@ -227,6 +252,10 @@ var MonitorManager = GObject.registerClass({
this._monitorProperties = null; this._monitorProperties = null;
this._changeHookFn = null; this._changeHookFn = null;
this._needsConfigCheck = this.use_optimal_monitor_config; this._needsConfigCheck = this.use_optimal_monitor_config;
// help prevent certain actions from taking place multiple times in the event of rapid monitor updates
this._asyncRequestsInFlight = 0;
this._configCheckRequestsCount = 0;
} }
enable() { enable() {
@ -266,36 +295,59 @@ var MonitorManager = GObject.registerClass({
return this._monitorProperties; return this._monitorProperties;
} }
// returns true if a check is needed, caller should wait for the next change hook call // returns true if an async check is needed, caller should wait for the next change hook call
needsOptimalModeCheck(monitorConnector) { needsOptimalModeCheck(monitorConnector) {
Globals.logger.log_debug(`MonitorManager checkOptimalMode: ${monitorConnector}`); Globals.logger.log_debug(`MonitorManager needsOptimalModeCheck: ${monitorConnector}`);
if (this._displayConfigProxy == null) { if (this._displayConfigProxy == null) {
Globals.logger.log('MonitorManager checkOptimalMode: _displayConfigProxy not set!'); Globals.logger.log('MonitorManager needsOptimalModeCheck: _displayConfigProxy not set!');
return false; return false;
} }
if (this._needsConfigCheck) { const isCheckingConfig = this._needsConfigCheck;
performOptimalModeCheck(this._displayConfigProxy, monitorConnector, this.headset_as_primary, ((configChanged, error) => { if (this._needsConfigCheck && this._asyncRequestsInFlight === 0) {
this._needsConfigCheck = false; this._asyncRequestsInFlight++;
const configCheckCountSnapshot = this._configCheckRequestsCount;
const allowConfigUpdateFn = (() => {
// allow updates to the config if this is the only in-flight request and no more requests
// were made while we were waiting for the previous request to complete
return this._asyncRequestsInFlight === 1 && this._configCheckRequestsCount === configCheckCountSnapshot;
}).bind(this);
performOptimalModeCheck(this._displayConfigProxy, monitorConnector, this.headset_as_primary, this.use_highest_refresh_rate, ((configChanged, error) => {
if (--this._asyncRequestsInFlight > 0) {
Globals.logger.log_debug(`MonitorManager needsOptimalModeCheck: ${this._asyncRequestsInFlight} async requests still pending, skipping change hook`);
return;
} else if (this._configCheckRequestsCount !== configCheckCountSnapshot) {
Globals.logger.log_debug('MonitorManager needsOptimalModeCheck: config checks requested while in-flight, skipping change hook');
return;
}
if (error) { if (error) {
Globals.logger.log(`Failed to switch to optimal mode for monitor ${monitorConnector}: ${error}`); Globals.logger.log(`Failed to switch to optimal mode for monitor ${monitorConnector}: ${error}`);
// tell the extension to proceed, this should result in another config check
this._changeHookFn();
} else { } else {
this._needsConfigCheck = false;
if (configChanged) { if (configChanged) {
Globals.logger.log(`Switched to optimal mode for monitor ${monitorConnector}`); Globals.logger.log(`Switched to optimal mode for monitor ${monitorConnector}`);
} else if (!!this._changeHookFn) { } else if (!!this._changeHookFn) {
Globals.logger.log_debug('MonitorManager checkOptimalMode: no config change'); Globals.logger.log_debug('MonitorManager needsOptimalModeCheck: no config change');
// no config change means this won't be triggered automatically, so trigger it manually // no config change means this won't be triggered automatically, so trigger it manually
this._changeHookFn(); this._changeHookFn();
} else { } else {
Globals.logger.log('MonitorManager checkOptimalMode: can\'t trigger change hook, no hook set!'); Globals.logger.log('MonitorManager needsOptimalModeCheck: can\'t trigger change hook, no hook set!');
} }
} }
}).bind(this)); }).bind(this), allowConfigUpdateFn);
} else if (!this._needsConfigCheck) {
Globals.logger.log_debug('MonitorManager needsOptimalModeCheck: skipping config check');
} else { } else {
Globals.logger.log_debug('MonitorManager checkOptimalMode: skipping config check'); Globals.logger.log_debug(`MonitorManager needsOptimalModeCheck: skipping due to async requests ${this._asyncRequestsInFlight}`);
} }
return this._needsConfigCheck; return isCheckingConfig;
} }
_on_monitors_change() { _on_monitors_change() {
@ -303,16 +355,22 @@ var MonitorManager = GObject.registerClass({
if (this._displayConfigProxy == null) { if (this._displayConfigProxy == null) {
return; return;
} }
this._needsConfigCheck = this.use_optimal_monitor_config; if (this.use_optimal_monitor_config) {
this._needsConfigCheck = true;
this._configCheckRequestsCount++;
}
this._asyncRequestsInFlight++;
getMonitorConfig(this._displayConfigProxy, ((result, error) => { getMonitorConfig(this._displayConfigProxy, ((result, error) => {
if (error) { if (error) {
Globals.logger.log(error);
return; return;
} }
const monitorProperties = []; const monitorProperties = [];
for (let i = 0; i < result.length; i++) { for (let i = 0; i < result.length; i++) {
const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i]; const [monitorName, connectorName, vendor, product, serial, refreshRate] = result[i];
const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName); const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName);
Globals.logger.log(`Found monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}`); Globals.logger.log_debug(`Found monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}`);
if (monitorIndex >= 0) { if (monitorIndex >= 0) {
monitorProperties[monitorIndex] = { monitorProperties[monitorIndex] = {
index: monitorIndex, index: monitorIndex,
@ -327,7 +385,11 @@ var MonitorManager = GObject.registerClass({
} }
this._monitorProperties = monitorProperties; this._monitorProperties = monitorProperties;
if (!!this._changeHookFn) { if (!!this._changeHookFn) {
this._changeHookFn(); if (--this._asyncRequestsInFlight === 0) {
this._changeHookFn();
} else {
Globals.logger.log_debug(`MonitorManager _on_monitors_change: ${this._asyncRequestsInFlight} requests still pending, skipping change hook`);
}
} else { } else {
Globals.logger.log('MonitorManager _on_monitors_change: can\'t trigger change hook, no hook set!'); Globals.logger.log('MonitorManager _on_monitors_change: can\'t trigger change hook, no hook set!');
} }

@ -1 +1 @@
Subproject commit b97faa82a7767e4270e46886d68dd1c70b71abca Subproject commit b0080ca844e057d31aae0e70aa6d026059ea304f

View File

@ -3,6 +3,9 @@
# exit when any command fails # exit when any command fails
set -e set -e
ARCH=${ARCH:-$(uname -m)}
echo "Building Breezy UI for $ARCH"
check_command() { check_command() {
if ! command -v "$1" &>/dev/null; then if ! command -v "$1" &>/dev/null; then
echo "Please install \"$1\" and make sure it's available in your \$PATH" echo "Please install \"$1\" and make sure it's available in your \$PATH"
@ -16,13 +19,13 @@ check_command "flatpak-builder"
# https://stackoverflow.com/a/246128 # https://stackoverflow.com/a/246128
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
TMP_DIR=$(mktemp -d -t breezy-ui-flatpak-XXXXXXXXXX) TMP_DIR=$(mktemp -d --tmpdir=$SCRIPT_DIR/.. -t .breezy-ui-flatpak-XXXXXXXXXX)
OUT_DIR=$SCRIPT_DIR/../out OUT_DIR=$SCRIPT_DIR/../out
rm -rf $OUT_DIR rm -rf $OUT_DIR
mkdir -p $OUT_DIR mkdir -p $OUT_DIR
flatpak-builder --force-clean $TMP_DIR/build $SCRIPT_DIR/../com.xronlinux.BreezyDesktop.json 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 $TMP_DIR/export $TMP_DIR/build flatpak build-export --arch $ARCH $TMP_DIR/export $TMP_DIR/build
flatpak build-bundle $TMP_DIR/export $OUT_DIR/com.xronlinux.BreezyDesktop.flatpak com.xronlinux.BreezyDesktop --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo 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
rm -rf "$TMP_DIR" rm -rf "$TMP_DIR"

View File

@ -5,6 +5,7 @@
IFS=: read -ra host_data_dirs < <(flatpak-spawn --host sh -c 'echo "$XDG_DATA_DIRS"') IFS=: read -ra host_data_dirs < <(flatpak-spawn --host sh -c 'echo "$XDG_DATA_DIRS"')
IFS=: read -ra HOST_XDG_STATE_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_STATE_HOME"') IFS=: read -ra HOST_XDG_STATE_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_STATE_HOME"')
IFS=: read -ra HOST_XDG_CONFIG_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_CONFIG_HOME"')
IFS=: read -ra HOST_XDG_BIN_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_BIN_HOME"') IFS=: read -ra HOST_XDG_BIN_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_BIN_HOME"')
IFS=: read -ra HOST_XDG_DATA_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_DATA_HOME"') IFS=: read -ra HOST_XDG_DATA_HOME < <(flatpak-spawn --host sh -c 'echo "$XDG_DATA_HOME"')
@ -48,6 +49,12 @@ else
XDG_STATE_HOME="$(realpath ~)/.local/state" XDG_STATE_HOME="$(realpath ~)/.local/state"
fi fi
if [[ ! -z "${HOST_XDG_CONFIG_HOME}" ]]; then
XDG_CONFIG_HOME="${HOST_XDG_CONFIG_HOME}"
else
XDG_CONFIG_HOME="$(realpath ~)/.config"
fi
if [[ ! -z "${HOST_XDG_DATA_HOME}" ]]; then if [[ ! -z "${HOST_XDG_DATA_HOME}" ]]; then
XDG_DATA_HOME="${HOST_XDG_DATA_HOME}" XDG_DATA_HOME="${HOST_XDG_DATA_HOME}"
else else
@ -57,5 +64,6 @@ fi
export XDG_DATA_DIRS export XDG_DATA_DIRS
export XDG_BIN_HOME export XDG_BIN_HOME
export XDG_STATE_HOME export XDG_STATE_HOME
export XDG_CONFIG_HOME
export XDG_DATA_HOME export XDG_DATA_HOME
exec breezydesktop "$@" exec breezydesktop "$@"

View File

@ -118,6 +118,24 @@
Automatically set the headset as the primary display upon connection Automatically set the headset as the primary display upon connection
</description> </description>
</key> </key>
<key name="use-highest-refresh-rate" type="b">
<default>
true
</default>
<summary>Use highest refresh rate</summary>
<description>
Automatically set the highest refresh rate upon connection
</description>
</key>
<key name="fast-sbs-mode-switching" type="b">
<default>
true
</default>
<summary>Fast SBS mode switching</summary>
<description>
Enable fast SBS mode switching
</description>
</key>
<key name="disable-anti-aliasing" type="b"> <key name="disable-anti-aliasing" type="b">
<default> <default>
false false

View File

@ -3,7 +3,13 @@
<id>com.xronlinux.BreezyDesktop.desktop</id> <id>com.xronlinux.BreezyDesktop.desktop</id>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-or-later</project_license> <project_license>GPL-3.0-or-later</project_license>
<name>Breezy Desktop</name>
<summary>XR Desktop Control Panel</summary>
<description> <description>
<p>No description</p> <p>XR Desktop Control Panel</p>
</description> </description>
<categories>
<category>Office</category>
<category>Development</category>
</categories>
</component> </component>

@ -1 +1 @@
Subproject commit 34a349d39efc02f4b65550debd193d29d95a84f9 Subproject commit a96fdeb1557d8cd24e73cb8e9e2559adfa46e3aa

View File

@ -31,6 +31,8 @@ class ConnectedDevice(Gtk.Box):
toggle_follow_shortcut_label = Gtk.Template.Child() toggle_follow_shortcut_label = Gtk.Template.Child()
headset_as_primary_switch = Gtk.Template.Child() headset_as_primary_switch = Gtk.Template.Child()
use_optimal_monitor_config_switch = Gtk.Template.Child() use_optimal_monitor_config_switch = Gtk.Template.Child()
use_highest_refresh_rate_switch = Gtk.Template.Child()
fast_sbs_mode_switch = Gtk.Template.Child()
movement_look_ahead_scale = Gtk.Template.Child() movement_look_ahead_scale = Gtk.Template.Child()
movement_look_ahead_adjustment = Gtk.Template.Child() movement_look_ahead_adjustment = Gtk.Template.Child()
@ -52,6 +54,8 @@ class ConnectedDevice(Gtk.Box):
self.reassign_toggle_follow_shortcut_button, self.reassign_toggle_follow_shortcut_button,
self.headset_as_primary_switch, self.headset_as_primary_switch,
self.use_optimal_monitor_config_switch, self.use_optimal_monitor_config_switch,
self.use_highest_refresh_rate_switch,
self.fast_sbs_mode_switch,
self.movement_look_ahead_scale self.movement_look_ahead_scale
] ]
@ -66,6 +70,8 @@ class ConnectedDevice(Gtk.Box):
self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('curved-display', self.curved_display_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('headset-as-primary', self.headset_as_primary_switch, 'active', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('headset-as-primary', self.headset_as_primary_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('use-optimal-monitor-config', self.use_optimal_monitor_config_switch, 'active', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('use-optimal-monitor-config', self.use_optimal_monitor_config_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('use-highest-refresh-rate', self.use_highest_refresh_rate_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('fast-sbs-mode-switching', self.fast_sbs_mode_switch, 'active', Gio.SettingsBindFlags.DEFAULT)
self.settings.bind('look-ahead-override', self.movement_look_ahead_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('look-ahead-override', self.movement_look_ahead_adjustment, 'value', Gio.SettingsBindFlags.DEFAULT)
bind_shortcut_settings(self.get_parent(), [ bind_shortcut_settings(self.get_parent(), [
@ -136,8 +142,10 @@ class ConnectedDevice(Gtk.Box):
def _refresh_use_optimal_monitor_config(self, switch, param): def _refresh_use_optimal_monitor_config(self, switch, param):
self.headset_as_primary_switch.set_sensitive(switch.get_active()) self.headset_as_primary_switch.set_sensitive(switch.get_active())
self.use_highest_refresh_rate_switch.set_sensitive(switch.get_active())
if not switch.get_active(): if not switch.get_active():
self.headset_as_primary_switch.set_active(False) self.headset_as_primary_switch.set_active(False)
self.use_highest_refresh_rate_switch.set_active(False)
def set_device_name(self, name): def set_device_name(self, name):
self.device_label.set_markup(f"<b>{name}</b>") self.device_label.set_markup(f"<b>{name}</b>")

View File

@ -328,6 +328,17 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Use highest refresh rate</property>
<property name="subtitle" translatable="true">Refresh rate may affect performance, disable this to set it manually.</property>
<child>
<object class="GtkSwitch" id="use_highest_refresh_rate_switch">
<property name="valign">3</property>
</object>
</child>
</object>
</child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow">
<property name="title" translatable="true">Always primary display</property> <property name="title" translatable="true">Always primary display</property>
@ -339,6 +350,17 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="true">Fast SBS mode switching</property>
<property name="subtitle" translatable="true">Switches glasses to SBS mode immediately when plugged in, if widescreen mode is on. May cause instability.</property>
<child>
<object class="GtkSwitch" id="fast_sbs_mode_switch">
<property name="valign">3</property>
</object>
</child>
</object>
</child>
<child> <child>
<object class="AdwActionRow"> <object class="AdwActionRow">
<property name="title" translatable="true">Movement look-ahead</property> <property name="title" translatable="true">Movement look-ahead</property>

View File

@ -21,6 +21,7 @@ import gi
import logging import logging
import os import os
import sys import sys
import argparse
from logging.handlers import TimedRotatingFileHandler from logging.handlers import TimedRotatingFileHandler
@ -35,8 +36,12 @@ from .statemanager import StateManager
from .window import BreezydesktopWindow from .window import BreezydesktopWindow
from .xrdriveripc import XRDriverIPC from .xrdriveripc import XRDriverIPC
state_dir = os.path.expanduser("~/.local/state") config_home = os.environ.get('XDG_CONFIG_HOME', '~/.config')
log_dir = os.path.join(state_dir, 'breezy_gnome/logs/ui') config_dir = os.path.expanduser(config_home)
state_home = os.environ.get('XDG_STATE_HOME', '~/.local/state')
state_dir = os.path.expanduser(state_home)
breezy_state_dir = os.path.join(state_dir, 'breezy_gnome')
log_dir = os.path.join(breezy_state_dir, 'logs/ui')
os.makedirs(log_dir, exist_ok=True) os.makedirs(log_dir, exist_ok=True)
logger = logging.getLogger('breezy_ui') logger = logging.getLogger('breezy_ui')
@ -53,18 +58,19 @@ def excepthook(exc_type, exc_value, exc_traceback):
sys.excepthook = excepthook sys.excepthook = excepthook
XRDriverIPC.set_instance(XRDriverIPC(logger)) XRDriverIPC.set_instance(XRDriverIPC(logger, config_dir))
class BreezydesktopApplication(Adw.Application): class BreezydesktopApplication(Adw.Application):
"""The main application singleton class.""" """The main application singleton class."""
def __init__(self): def __init__(self, skip_verification):
super().__init__(application_id='com.xronlinux.BreezyDesktop', super().__init__(application_id='com.xronlinux.BreezyDesktop',
flags=Gio.ApplicationFlags.DEFAULT_FLAGS) flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
self.create_action('quit', self.on_quit_action, ['<primary>q']) self.create_action('quit', self.on_quit_action, ['<primary>q'])
self.create_action('about', self.on_about_action) self.create_action('about', self.on_about_action)
self.create_action('license', self.on_license_action) self.create_action('license', self.on_license_action)
self.create_action('reset_driver', self.on_reset_driver_action) self.create_action('reset_driver', self.on_reset_driver_action)
self._skip_verification = skip_verification
def do_activate(self): def do_activate(self):
"""Called when the application is activated. """Called when the application is activated.
@ -74,7 +80,7 @@ class BreezydesktopApplication(Adw.Application):
""" """
win = self.props.active_window win = self.props.active_window
if not win: if not win:
win = BreezydesktopWindow(application=self) win = BreezydesktopWindow(self._skip_verification, application=self)
win.connect('close-request', lambda *_: self.on_quit_action()) win.connect('close-request', lambda *_: self.on_quit_action())
win.connect('destroy', lambda *_: self.on_quit_action()) win.connect('destroy', lambda *_: self.on_quit_action())
win.present() win.present()
@ -125,5 +131,9 @@ class BreezydesktopApplication(Adw.Application):
def main(version): def main(version):
app = BreezydesktopApplication() parser = argparse.ArgumentParser()
return app.run(sys.argv) parser.add_argument("-sv", "--skip-verification", action="store_true")
args = parser.parse_args()
app = BreezydesktopApplication(args.skip_verification)
return app.run(None)

View File

@ -37,7 +37,7 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
license_action_needed_banner = Gtk.Template.Child() license_action_needed_banner = Gtk.Template.Child()
missing_breezy_features_banner = Gtk.Template.Child() missing_breezy_features_banner = Gtk.Template.Child()
def __init__(self, **kwargs): def __init__(self, skip_verification, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.state_manager = StateManager.get_instance() self.state_manager = StateManager.get_instance()
@ -57,6 +57,8 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
self._handle_state_update(self.state_manager, None) self._handle_state_update(self.state_manager, None)
self._skip_verification = skip_verification
self.connect("destroy", self._on_window_destroy) self.connect("destroy", self._on_window_destroy)
def _handle_state_update(self, state_manager, val): def _handle_state_update(self, state_manager, val):
@ -71,9 +73,11 @@ class BreezydesktopWindow(Gtk.ApplicationWindow):
for child in self.main_content: for child in self.main_content:
self.main_content.remove(child) self.main_content.remove(child)
if not verify_installation(): if not self._skip_verification:
self.main_content.append(self.failed_verification) if not verify_installation():
elif not self.state_manager.get_property('license-present'): self.main_content.append(self.failed_verification)
if not self.state_manager.get_property('license-present'):
self.main_content.append(self.no_license) self.main_content.append(self.no_license)
elif not ExtensionsManager.get_instance().is_installed(): elif not ExtensionsManager.get_instance().is_installed():
self.main_content.append(self.no_extension) self.main_content.append(self.no_extension)

View File

@ -14,31 +14,88 @@ if [ "$(id -u)" != "0" ]; then
exit 1 exit 1
fi fi
# Get the directory of the current script
script_dir=$(dirname "$0")
USER=${SUDO_USER:-$USER} USER=${SUDO_USER:-$USER}
GROUP=$(id -gn $USER)
USER_HOME=$(getent passwd $USER | cut -d: -f6) USER_HOME=$(getent passwd $USER | cut -d: -f6)
if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share"
fi
DATA_DIR="$XDG_DATA_HOME/breezy_vulkan"
if [ ! -d "$DATA_DIR" ]; then
mkdir -p $DATA_DIR
fi
VULKAN_DATA_DIR="$XDG_DATA_HOME/vulkan"
XR_DRIVER_DATA_DIR="$XDG_DATA_HOME/breezy_vulkan"
if [ -z "$XDG_CONFIG_HOME" ]; then
XDG_CONFIG_HOME="$USER_HOME/.config"
fi
CONFIG_DIR="$XDG_CONFIG_HOME/breezy_vulkan"
if [ ! -d "$CONFIG_DIR" ]; then
mkdir -p $CONFIG_DIR
fi
if [ -z "$XDG_BIN_HOME" ]; then
XDG_BIN_HOME="$USER_HOME/.local/bin"
fi
OLD_BIN_DIR="$USER_HOME/bin"
BIN_DIR="$XDG_BIN_HOME"
if [ ! -d "$BIN_DIR" ]; then
mkdir -p $BIN_DIR
fi
if [ -z "$XDG_STATE_HOME" ]; then
XDG_STATE_HOME="$USER_HOME/.local/state"
fi
STATE_DIR="$XDG_STATE_HOME/breezy_vulkan"
if [ ! -d "$STATE_DIR" ]; then
mkdir -p $STATE_DIR
fi
LIB_DIR="$USER_HOME/.local/lib"
if [ ! -d "$LIB_DIR" ]; then
mkdir -p "$LIB_DIR"
fi
LIB32_DIR="$USER_HOME/.local/lib32"
if [ ! -d "$LIB32_DIR" ]; then
mkdir -p "$LIB32_DIR"
fi
if [ "$for_install" -eq 0 ]; then if [ "$for_install" -eq 0 ]; then
UA_EVENT_NAME="breezy_uninstall" UA_EVENT_NAME="breezy_uninstall"
UA_CLIENT_ID="BreezyVulkan" UA_CLIENT_ID="BreezyVulkan"
#INJECT_UA_CALL #INJECT_UA_CALL
fi fi
[ "$for_install" -eq 0 ] && echo "Removing vkBasalt binaries from $USER_HOME/.local/lib[32]/" [ "$for_install" -eq 0 ] && echo "Removing vkBasalt binaries from $LIB_DIR[32]/"
rm $USER_HOME/.local/lib/libvkbasalt.so rm $LIB_DIR/libvkbasalt.so
rm $USER_HOME/.local/lib32/libvkbasalt.so rm $LIB32_DIR/libvkbasalt.so
[ "$for_install" -eq 0 ] && echo "Removing vkBasalt vulkan layer configs from $USER_HOME/.local/share/vulkan/implicit_layer.d/" [ "$for_install" -eq 0 ] && echo "Removing vkBasalt vulkan layer configs from $VULKAN_DATA_DIR/implicit_layer.d/"
rm $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.json rm $VULKAN_DATA_DIR/implicit_layer.d/vkBasalt.json
rm $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.x86.json rm $VULKAN_DATA_DIR/implicit_layer.d/vkBasalt.x86.json
[ "$for_install" -eq 0 ] && echo "Removing vkBasalt and reshade directories at $USER_HOME/.config/" [ "$for_install" -eq 0 ] && echo "Removing vkBasalt and reshade directories at $CONFIG_DIR/"
rm -rf $USER_HOME/.config/vkBasalt rm -rf $CONFIG_DIR/vkBasalt
rm -rf $USER_HOME/.config/reshade rm -rf $CONFIG_DIR/reshade
[ "$for_install" -eq 0 ] && echo "Removing scripts at $USER_HOME/.local/bin/breezy_vulkan" [ "$for_install" -eq 0 ] && echo "Removing scripts at $BIN_DIR"
rm -rf $USER_HOME/.local/bin/breezy_vulkan rm -f $BIN_DIR/breezy_vulkan_verify
[ "$for_install" -eq 0 ] && echo "SKIPPING xrealAirLinuxDriver uninstall to keep mouse/joystick driver functionality." [ "$for_install" -eq 0 ] && echo "SKIPPING xrDriver uninstall to keep mouse/joystick driver functionality."
[ "$for_install" -eq 0 ] && echo "To manually uninstall xrealAirLinuxDriver, do: \"sudo ~/bin/xreal_driver_uninstall\"" [ "$for_install" -eq 0 ] && echo "To manually uninstall xrDriver, do: \"sudo xr_driver_uninstall\""
# this script is self-deleting, leave this as the last command # this script is self-deleting, leave this as the last command
rm -f $USER_HOME/bin/breezy_vulkan_uninstall # remove the one we're not using first
if [ "$script_dir" = "$OLD_BIN_DIR" ]; then
rm -f "$BIN_DIR/breezy_vulkan_uninstall"
rm -f "$OLD_BIN_DIR/breezy_vulkan_uninstall"
else
rm -f "$OLD_BIN_DIR/breezy_vulkan_uninstall"
rm -f "$BIN_DIR/breezy_vulkan_uninstall"
fi

42
vulkan/bin/breezy_vulkan_verify Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -e
# create a string to string mapping, file name to expected file location
declare -A file_paths
file_paths=(
["bin/breezy_vulkan_uninstall"]="{bin_dir}/breezy_vulkan_uninstall"
["vkBasalt.64/libvkbasalt.so"]="{lib_dir}/libvkbasalt.so"
["vkBasalt.32/libvkbasalt.so"]="{lib32_dir}/libvkbasalt.so"
["IMUAdjust.fx"]="{reshade_config_dir}/Shaders/IMUAdjust.fx"
["ReShade.fxh"]="{reshade_config_dir}/Shaders/ReShade.fxh"
["ReShadeUI.fxh"]="{reshade_config_dir}/Shaders/ReShadeUI.fxh"
["Sideview.fx"]="{reshade_config_dir}/Shaders/Sideview.fx"
["calibrating.png"]="{reshade_config_dir}/Textures/calibrating.png"
["custom_banner.png"]="{reshade_config_dir}/Textures/custom_banner.png"
["xr_driver/manifest"]="{xr_driver_data_dir}/manifest"
)
# verify the file hashes in ./manifest
while IFS= read -r line
do
# split the line into hash and filename
manifest_hash=$(echo $line | awk '{print $1}')
file=$(echo $line | awk '{print $2}')
actual_file_path=${file_paths[$file]}
# compute the SHA256 hash of the actual file
actual_hash=$(sha256sum $actual_file_path | awk '{print $1}')
# compare the hashes
if ! [ "$manifest_hash" = "$actual_hash" ]; then
echo "Verification failed" >&2
exit 1
fi
done < "{data_dir}/manifest"
# if our checks succeeded, run the xr_driver verify script
{bin_dir}/xr_driver_verify > /dev/null
echo "Verification succeeded"

View File

@ -6,8 +6,53 @@ set -e
# to a specific release of the code, and guarantees it will never run along-side newer or older binaries. # to a specific release of the code, and guarantees it will never run along-side newer or older binaries.
USER=${SUDO_USER:-$USER} USER=${SUDO_USER:-$USER}
GROUP=$(id -gn $USER)
USER_HOME=$(getent passwd $USER | cut -d: -f6) USER_HOME=$(getent passwd $USER | cut -d: -f6)
UA_EVENT_NAME="breezy_install" UA_EVENT_NAME="breezy_install"
if [ -z "$XDG_DATA_HOME" ]; then
XDG_DATA_HOME="$USER_HOME/.local/share"
fi
DATA_DIR="$XDG_DATA_HOME/breezy_vulkan"
if [ ! -d "$DATA_DIR" ]; then
mkdir -p $DATA_DIR
fi
VULKAN_DATA_DIR="$XDG_DATA_HOME/vulkan"
XR_DRIVER_DATA_DIR="$XDG_DATA_HOME/xr_driver"
if [ -z "$XDG_CONFIG_HOME" ]; then
XDG_CONFIG_HOME="$USER_HOME/.config"
fi
RESHADE_CONFIG_DIR="$XDG_CONFIG_HOME/reshade"
VKBASALT_CONFIG_DIR="$XDG_CONFIG_HOME/vkBasalt"
if [ -z "$XDG_BIN_HOME" ]; then
XDG_BIN_HOME="$USER_HOME/.local/bin"
fi
OLD_BIN_DIR="$USER_HOME/bin"
BIN_DIR="$XDG_BIN_HOME"
if [ ! -d "$BIN_DIR" ]; then
mkdir -p $BIN_DIR
fi
if [ -z "$XDG_STATE_HOME" ]; then
XDG_STATE_HOME="$USER_HOME/.local/state"
fi
STATE_DIR="$XDG_STATE_HOME/breezy_vulkan"
if [ ! -d "$STATE_DIR" ]; then
mkdir -p $STATE_DIR
fi
LIB_DIR="$USER_HOME/.local/lib"
if [ ! -d "$LIB_DIR" ]; then
mkdir -p "$LIB_DIR"
fi
LIB32_DIR="$USER_HOME/.local/lib32"
if [ ! -d "$LIB32_DIR" ]; then
mkdir -p "$LIB32_DIR"
fi
if [ -e "$USER_HOME/bin/breezy_vulkan_uninstall" ]; then if [ -e "$USER_HOME/bin/breezy_vulkan_uninstall" ]; then
echo "Cleaning up the previous installation" echo "Cleaning up the previous installation"
@ -17,68 +62,93 @@ if [ -e "$USER_HOME/bin/breezy_vulkan_uninstall" ]; then
UA_EVENT_NAME="breezy_update" UA_EVENT_NAME="breezy_update"
fi fi
if [ -e "$BIN_DIR/breezy_vulkan_uninstall" ]; then
echo "Cleaning up the previous installation"
# ` || true` will ensure that this can't cause a failure, even with `set -e`
$BIN_DIR/breezy_vulkan_uninstall --for-install || true
UA_EVENT_NAME="breezy_update"
fi
UA_CLIENT_ID="BreezyVulkan" UA_CLIENT_ID="BreezyVulkan"
UA_EVENT_VERSION="$1" UA_EVENT_VERSION="$1"
#INJECT_UA_CALL #INJECT_UA_CALL
echo "Copying the breezy_vulkan scripts to ${USER_HOME}/bin"
if [ ! -d "$USER_HOME/bin" ]; then
su -c 'mkdir -p '$USER_HOME'/bin' $USER
fi
cp bin/breezy_vulkan_uninstall $USER_HOME/bin
echo "Installing vkBasalt; copying binaries, configs, and shader files to ${USER_HOME}/.local and ${USER_HOME}/.config" # escaping sed replace: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
ESCAPED_BIN_DIR=$(printf '%s\n' "$BIN_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_LIB_DIR=$(printf '%s\n' "$LIB_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_LIB32_DIR=$(printf '%s\n' "$LIB32_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_RESHADE_CONFIG_DIR=$(printf '%s\n' "$RESHADE_CONFIG_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_STATE_DIR=$(printf '%s\n' "$STATE_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_DATA_DIR=$(printf '%s\n' "$DATA_DIR" | sed -e 's/[\/&]/\\&/g')
ESCAPED_XR_DRIVER_DATA_DIR=$(printf '%s\n' "$XR_DRIVER_DATA_DIR" | sed -e 's/[\/&]/\\&/g')
echo "Copying the breezy_vulkan scripts to ${BIN_DIR} and related files to ${DATA_DIR}"
cp bin/breezy_vulkan_uninstall $BIN_DIR
sed -i -e "s/{bin_dir}/$ESCAPED_BIN_DIR/g" \
-e "s/{lib_dir}/$ESCAPED_LIB_DIR/g" \
-e "s/{lib32_dir}/$ESCAPED_LIB32_DIR/g" \
-e "s/{reshade_config_dir}/$ESCAPED_RESHADE_CONFIG_DIR/g" \
-e "s/{state_dir}/$ESCAPED_STATE_DIR/g" \
-e "s/{data_dir}/$ESCAPED_DATA_DIR/g" \
-e "s/{xr_driver_data_dir}/$ESCAPED_XR_DRIVER_DATA_DIR/g" \
bin/breezy_vulkan_verify
cp bin/breezy_vulkan_verify $BIN_DIR
cp manifest $DATA_DIR
# keep putting this in the old location in case an older version of the script tries to find it
cp bin/breezy_vulkan_uninstall $OLD_BIN_DIR
echo "Installing vkBasalt; copying binaries, configs, and shader files"
# much of the setup below was informed by https://github.com/simons-public/steam-deck-vkbasalt-install # much of the setup below was informed by https://github.com/simons-public/steam-deck-vkbasalt-install
# copy the vkBasalt binaries and configs # copy the vkBasalt binaries and configs
su -c 'mkdir -p '$USER_HOME'/.local/{lib,lib32,share/vulkan/implicit_layer.d}' $USER mkdir -p "$VULKAN_DATA_DIR"/implicit_layer.d
su -c 'mkdir -p '$USER_HOME'/.config/{vkBasalt,reshade/Shaders,reshade/Textures}' $USER mkdir -p "$XDG_CONFIG_HOME"/{vkBasalt,reshade/Shaders,reshade/Textures}
cp vkBasalt.64/libvkbasalt.so $USER_HOME/.local/lib/ cp vkBasalt.64/libvkbasalt.so $LIB_DIR/
cp vkBasalt.32/libvkbasalt.so $USER_HOME/.local/lib32/ cp vkBasalt.32/libvkbasalt.so $LIB32_DIR/
chown $USER:$USER $USER_HOME/.local/lib/libvkbasalt.so
chown $USER:$USER $USER_HOME/.local/lib32/libvkbasalt.so
# there is only one vkBasalt.json file, use the 64-bit directory for both, copy and make replacements # there is only one vkBasalt.json file, use the 64-bit directory for both, copy and make replacements
if grep -q SteamOS /etc/os-release ; then if grep -q SteamOS /etc/os-release ; then
sed -e "s|libvkbasalt.so|${USER_HOME}/.local/lib/libvkbasalt.so|" -e "s/ENABLE_VKBASALT/SteamDeck/" vkBasalt.64/vkBasalt.json > $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.json sed -e "s|libvkbasalt.so|${LIB_DIR}/libvkbasalt.so|" -e "s/ENABLE_VKBASALT/SteamDeck/" vkBasalt.64/vkBasalt.json > $VULKAN_DATA_DIR/implicit_layer.d/vkBasalt.json
sed -e "s|libvkbasalt.so|${USER_HOME}/.local/lib32/libvkbasalt.so|" -e "s/ENABLE_VKBASALT/SteamDeck/" vkBasalt.64/vkBasalt.json > $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.x86.json sed -e "s|libvkbasalt.so|${LIB32_DIR}/libvkbasalt.so|" -e "s/ENABLE_VKBASALT/SteamDeck/" vkBasalt.64/vkBasalt.json > $VULKAN_DATA_DIR/implicit_layer.d/vkBasalt.x86.json
else else
sed -e "s|libvkbasalt.so|${USER_HOME}/.local/lib/libvkbasalt.so|" vkBasalt.64/vkBasalt.json > $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.json sed -e "s|libvkbasalt.so|${LIB_DIR}/libvkbasalt.so|" vkBasalt.64/vkBasalt.json > $VULKAN_DATA_DIR/implicit_layer.d/vkBasalt.json
sed -e "s|libvkbasalt.so|${USER_HOME}/.local/lib32/libvkbasalt.so|" vkBasalt.64/vkBasalt.json > $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.x86.json sed -e "s|libvkbasalt.so|${LIB32_DIR}/libvkbasalt.so|" vkBasalt.64/vkBasalt.json > $VULKAN_DATA_DIR/implicit_layer.d/vkBasalt.x86.json
fi fi
chown $USER:$USER $USER_HOME/.local/share/vulkan/implicit_layer.d/vkBasalt.*
# copy the vkBasalt.conf file and make replacements # copy the vkBasalt.conf file and make replacements
sed -e "s|/path/to/reshade-shaders|${USER_HOME}/.config/reshade|" \ sed -e "s|/path/to/reshade-shaders|${RESHADE_CONFIG_DIR}|" \
-e "s|/path/to/virtual_display|${USER_HOME}/.config/reshade/Shaders/IMUAdjust.fx|" \ -e "s|/path/to/virtual_display|${RESHADE_CONFIG_DIR}/Shaders/IMUAdjust.fx|" \
-e "s|/path/to/sideview|${USER_HOME}/.config/reshade/Shaders/Sideview.fx|" \ -e "s|/path/to/sideview|${RESHADE_CONFIG_DIR}/Shaders/Sideview.fx|" \
config/vkBasalt.conf > $USER_HOME/.config/vkBasalt/vkBasalt.conf config/vkBasalt.conf > $VKBASALT_CONFIG_DIR/vkBasalt.conf
chown -R $USER:$USER $USER_HOME/.config/vkBasalt
echo "Installing the Sombrero shaders and texture files to ${USER_HOME}/.config/reshade/{Shaders,Textures}" echo "Installing the Sombrero shaders and texture files to ${RESHADE_CONFIG_DIR}/{Shaders,Textures}"
cp *.fx* $USER_HOME/.config/reshade/Shaders cp *.fx* $RESHADE_CONFIG_DIR/Shaders
cp *.png $USER_HOME/.config/reshade/Textures cp *.png $RESHADE_CONFIG_DIR/Textures
chown -R $USER:$USER $USER_HOME/.config/reshade
# escaping sed replace: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern chown -R $USER:$GROUP $LIB_DIR
ESCAPED_USER_HOME=$(printf '%s\n' "$USER_HOME" | sed -e 's/[\/&]/\\&/g') chown -R $USER:$GROUP $LIB32_DIR
chown -R $USER:$GROUP $DATA_DIR
chown -R $USER:$GROUP $VULKAN_DATA_DIR
chown -R $USER:$GROUP $RESHADE_CONFIG_DIR
chown -R $USER:$GROUP $VKBASALT_CONFIG_DIR
chown -R $USER:$GROUP $STATE_DIR
chown -R $USER:$GROUP $BIN_DIR/breezy_vulkan_*
echo "Copying the verification script and manifest to ${USER_HOME}/.local/bin/breezy_vulkan" # clear bash's cache of executable locations, so it can find the newly installed scripts
sed -i -e "s/{user_home}/$ESCAPED_USER_HOME/g" bin/verify_installation hash -r
if [ ! -d "$USER_HOME/.local/bin/breezy_vulkan" ]; then
mkdir -p $USER_HOME/.local/bin/breezy_vulkan
fi
cp -p bin/verify_installation $USER_HOME/.local/bin/breezy_vulkan
cp manifest $USER_HOME/.local/bin/breezy_vulkan
# set up the XREAL driver using the local binary # set up the XR driver using the local binary
echo "Installing xrealAirLinuxDriver" echo "Installing xrDriver"
echo "BEGIN - xreal_driver_setup" echo "BEGIN - xr_driver_setup"
if [ -z "$1" ] if [ -z "$1" ]
then then
bin/xreal_driver_setup $(pwd)/xrealAirLinuxDriver.tar.gz bin/xr_driver_setup $(pwd)/xrDriver.tar.gz
else else
bin/xreal_driver_setup -v $1 $(pwd)/xrealAirLinuxDriver.tar.gz bin/xr_driver_setup -v $1 $(pwd)/xrDriver.tar.gz
fi fi
echo "END - xreal_driver_setup" echo "END - xr_driver_setup"

View File

@ -1,42 +0,0 @@
#!/usr/bin/env bash
set -e
# create a string to string mapping, file name to expected file location
declare -A file_paths
file_paths=(
["bin/breezy_vulkan_uninstall"]="{user_home}/bin/breezy_vulkan_uninstall"
["vkBasalt.64/libvkbasalt.so"]="{user_home}/.local/lib/libvkbasalt.so"
["vkBasalt.32/libvkbasalt.so"]="{user_home}/.local/lib32/libvkbasalt.so"
["IMUAdjust.fx"]="{user_home}/.config/reshade/Shaders/IMUAdjust.fx"
["ReShade.fxh"]="{user_home}/.config/reshade/Shaders/ReShade.fxh"
["ReShadeUI.fxh"]="{user_home}/.config/reshade/Shaders/ReShadeUI.fxh"
["Sideview.fx"]="{user_home}/.config/reshade/Shaders/Sideview.fx"
["calibrating.png"]="{user_home}/.config/reshade/Textures/calibrating.png"
["custom_banner.png"]="{user_home}/.config/reshade/Textures/custom_banner.png"
["build/driver_air_glasses/manifest"]="{user_home}/.local/bin/xr_driver/manifest"
)
# verify the file hashes in ./manifest
while IFS= read -r line
do
# split the line into hash and filename
manifest_hash=$(echo $line | awk '{print $1}')
file=$(echo $line | awk '{print $2}')
actual_file_path=${file_paths[$file]}
# compute the SHA256 hash of the actual file
actual_hash=$(sha256sum $actual_file_path | awk '{print $1}')
# compare the hashes
if ! [ "$manifest_hash" = "$actual_hash" ]; then
echo "Verification failed" >&2
exit 1
fi
done < "{user_home}/.local/bin/breezy_vulkan/manifest"
# if our checks succeeded, run the xr_driver verify script
{user_home}/.local/bin/xr_driver/verify_installation > /dev/null
echo "Verification succeeded"