diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b0a1f9a..1f61b63 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,7 @@ cargo:test: script: - echo 'deb http://deb.debian.org/debian experimental main' > /etc/apt/sources.list.d/experimental.list - apt-get update - - apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libgtksourceview-5-dev libssl-dev libjxl-dev libvte-2.91-gtk4-dev -y + - apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libssl-dev libjxl-dev libvte-2.91-gtk4-dev -y - apt-get install meson ninja-build git desktop-file-utils gettext file libusb-dev libusb-1.0-0-dev curl -y - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup.sh - chmod +x /tmp/rustup.sh @@ -41,7 +41,7 @@ appimage: script: - echo 'deb http://deb.debian.org/debian experimental main' > /etc/apt/sources.list.d/experimental.list - apt-get update - - apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libgtksourceview-5-dev libssl-dev libjxl-dev libvte-2.91-gtk4-dev -y + - apt-get -t experimental install libgtk-4-dev libadwaita-1-dev libssl-dev libjxl-dev libvte-2.91-gtk4-dev -y - apt-get install meson ninja-build git desktop-file-utils gettext file libusb-dev libusb-1.0-0-dev curl -y - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup.sh - chmod +x /tmp/rustup.sh diff --git a/Cargo.lock b/Cargo.lock index 22fa4e8..7427528 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,7 +301,6 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sourceview5", "tracker", "uuid", "zoha-vte4", @@ -1877,41 +1876,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "sourceview5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c5f976a113e947bc5ec67758b2960c0db4ca76f80fb410d7cd86cd456d9ee5" -dependencies = [ - "futures-channel", - "futures-core", - "gdk-pixbuf", - "gdk4", - "gio", - "glib", - "gtk4", - "libc", - "pango", - "sourceview5-sys", -] - -[[package]] -name = "sourceview5-sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29637cccd56075a37ba72c0cc8b8d599dbc1d857e30dadea97eaacbc29b7fd46" -dependencies = [ - "gdk-pixbuf-sys", - "gdk4-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk4-sys", - "libc", - "pango-sys", - "system-deps", -] - [[package]] name = "spin" version = "0.9.8" diff --git a/Cargo.toml b/Cargo.toml index 2336889..c759857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,9 +36,6 @@ serde = { version = "1.0.188", features = [ "derive" ] } serde_json = "1.0.106" -sourceview5 = { version = "0.7.1", features = [ - "v5_6" -] } tracker = "0.2.1" uuid = { version = "1.4.1", features = ["v4", "fast-rng"] } zoha-vte4 = { version = "0.0.2", features = ["v0_72"] } diff --git a/data/style.css b/data/style.css index f2afec8..b84d6de 100644 --- a/data/style.css +++ b/data/style.css @@ -1,7 +1,3 @@ .padded { padding: 18px; } - -.sourceview-transparent-bg, .sourceview-transparent-bg * { - background-color: transparent; -} diff --git a/dist/arch/PKGBUILD b/dist/arch/PKGBUILD index 12cde9a..a0bbdc3 100644 --- a/dist/arch/PKGBUILD +++ b/dist/arch/PKGBUILD @@ -9,7 +9,6 @@ url='https://gitlab.com/gabmus/envision' license=(GPL) depends=( gtk4 - gtksourceview5 libadwaita openxr libgl @@ -23,6 +22,7 @@ depends=( ninja shaderc vulkan-headers + vte4 ) makedepends=( meson diff --git a/meson.build b/meson.build index 36ec669..7da240b 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ description = 'GUI for Monado' # temporary dependency('glib-2.0', version: '>= 2.66') dependency('gio-2.0', version: '>= 2.66') dependency('gtk4', version: '>= 4.10.0') -dependency('gtksourceview-5', version: '>= 5.6.0') +dependency('vte-2.91-gtk4', version: '>= 0.72.0') glib_compile_resources = find_program('glib-compile-resources', required: true) # glib_compile_schemas = find_program('glib-compile-schemas', required: true) diff --git a/src/build_tools/git.rs b/src/build_tools/git.rs index 39b3bb9..8476938 100644 --- a/src/build_tools/git.rs +++ b/src/build_tools/git.rs @@ -6,7 +6,7 @@ use std::path::Path; pub struct Git { pub repo: String, pub dir: String, - pub default_branch: String, + pub branch: String, } impl Git { @@ -88,7 +88,7 @@ impl Git { } pub fn get_checkout_ref_job(&self) -> WorkerJob { - let gref = self.get_ref().unwrap_or(self.default_branch.clone()); + let gref = self.get_ref().unwrap_or(self.branch.clone()); self.cmd(vec!["checkout".into(), gref]) } diff --git a/src/builders/build_basalt.rs b/src/builders/build_basalt.rs index c71e814..056d107 100644 --- a/src/builders/build_basalt.rs +++ b/src/builders/build_basalt.rs @@ -18,7 +18,10 @@ pub fn get_build_basalt_jobs(profile: &Profile, clean_build: bool) -> VecDeque "https://gitlab.freedesktop.org/mateosss/basalt.git".into(), }, dir: profile.features.basalt.path.as_ref().unwrap().clone(), - default_branch: "main".into(), + branch: match profile.features.basalt.branch.as_ref() { + Some(r) => r.clone(), + None => "main".into(), + }, }; jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); diff --git a/src/builders/build_libsurvive.rs b/src/builders/build_libsurvive.rs index 61dd992..4c0e2bc 100644 --- a/src/builders/build_libsurvive.rs +++ b/src/builders/build_libsurvive.rs @@ -18,7 +18,10 @@ pub fn get_build_libsurvive_jobs(profile: &Profile, clean_build: bool) -> VecDeq None => "https://github.com/cntools/libsurvive".into(), }, dir: profile.features.libsurvive.path.as_ref().unwrap().clone(), - default_branch: "master".into(), + branch: match profile.features.libsurvive.branch.as_ref() { + Some(r) => r.clone(), + None => "master".into(), + }, }; jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); diff --git a/src/builders/build_monado.rs b/src/builders/build_monado.rs index 89cb9dd..0dcea4c 100644 --- a/src/builders/build_monado.rs +++ b/src/builders/build_monado.rs @@ -22,7 +22,10 @@ pub fn get_build_monado_jobs( None => "https://gitlab.freedesktop.org/monado/monado".into(), }, dir: profile.xrservice_path.clone(), - default_branch: "main".into(), + branch: match profile.xrservice_branch.as_ref() { + Some(r) => r.clone(), + None => "main".into(), + }, }; jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); diff --git a/src/builders/build_opencomposite.rs b/src/builders/build_opencomposite.rs index a5b61b0..72394d1 100644 --- a/src/builders/build_opencomposite.rs +++ b/src/builders/build_opencomposite.rs @@ -22,7 +22,10 @@ pub fn get_build_opencomposite_jobs( None => "https://gitlab.com/znixian/OpenOVR.git".into(), }, dir: profile.opencomposite_path.clone(), - default_branch: "openxr".into(), + branch: match profile.opencomposite_branch.as_ref() { + Some(r) => r.clone(), + None => "openxr".into(), + }, }; jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); diff --git a/src/builders/build_openhmd.rs b/src/builders/build_openhmd.rs index 10ee072..8d4a47e 100644 --- a/src/builders/build_openhmd.rs +++ b/src/builders/build_openhmd.rs @@ -18,7 +18,10 @@ pub fn get_build_openhmd_jobs(profile: &Profile, clean_build: bool) -> VecDeque< None => "https://github.com/OpenHMD/OpenHMD".into(), }, dir: profile.features.openhmd.path.as_ref().unwrap().clone(), - default_branch: "master".into(), + branch: match profile.features.openhmd.branch.as_ref() { + Some(r) => r.clone(), + None => "master".into(), + }, }; jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); diff --git a/src/builders/build_wivrn.rs b/src/builders/build_wivrn.rs index abc55b1..255c4d5 100644 --- a/src/builders/build_wivrn.rs +++ b/src/builders/build_wivrn.rs @@ -22,7 +22,10 @@ pub fn get_build_wivrn_jobs( None => "https://github.com/Meumeu/WiVRn".into(), }, dir: profile.xrservice_path.clone(), - default_branch: "master".into(), + branch: match profile.xrservice_branch.as_ref() { + Some(r) => r.clone(), + None => "master".into(), + }, }; jobs.extend(git.get_pre_build_jobs(profile.pull_on_build)); diff --git a/src/dependencies/monado_deps.rs b/src/dependencies/monado_deps.rs index df3e124..b0e0ffd 100644 --- a/src/dependencies/monado_deps.rs +++ b/src/dependencies/monado_deps.rs @@ -40,6 +40,16 @@ fn monado_deps() -> Vec { ]), }, dep_glslang_validator(), + Dependency { + name: "sdl2".into(), + dep_type: DepType::SharedObject, + filename: "libSDL2.so".into(), + packages: HashMap::from([ + (LinuxDistro::Arch, "sdl2".into()), + (LinuxDistro::Debian, "libsdl2".into()), + (LinuxDistro::Fedora, "SDL2".into()), + ]), + }, ] } diff --git a/src/env_var_descriptions.rs b/src/env_var_descriptions.rs index e5b5f21..94213ae 100644 --- a/src/env_var_descriptions.rs +++ b/src/env_var_descriptions.rs @@ -14,6 +14,7 @@ pub static ENV_VAR_DESCRIPTIONS: Map<&str, &str> = phf_map! { "XRT_DEBUG_GUI" => "Set to 1 to enable the Monado debug UI.", "XRT_CURATED_GUI" => "Set to 1 to enable the Monado preview UI. Requires XRT_DEBUG_GUI=1 to work.", "XRT_JSON_LOG" => "Set to 1 to enable JSON logging for Monado. This enables better log visualization and log level filtering.", + "QWERTY_ENABLE" => "Set to 1 to enable QWERTY Simulation driver. This enables simulated driver that allows you to use Monado without HMD and controllers. It's also possible to mix and match different profiles with this.", "LH_DRIVER" => "Lighthouse driver, this overrides the \"Lighthouse driver option in the profile\"; Valid options are: \"vive\" for the default built-in driver, \"survive\" for Libsurvive, \"steamvr\" for the SteamVR based implementation.", "LH_LOG" => "Lighthouse log level. Can be one of: \"trace\", \"debug\", \"info\", \"warn\", \"error\".", "LIGHTHOUSE_LOG" => "Lighthouse driver log level. Can be one of: \"trace\", \"debug\", \"info\", \"warn\", \"error\"." diff --git a/src/file_builders/wivrn_config.rs b/src/file_builders/wivrn_config.rs index 71ad055..1131db6 100644 --- a/src/file_builders/wivrn_config.rs +++ b/src/file_builders/wivrn_config.rs @@ -85,6 +85,10 @@ pub struct WivrnConfEncoder { #[serde(skip_serializing_if = "Option::is_none")] pub height: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub offset_x: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub offset_y: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub group: Option, } @@ -96,6 +100,8 @@ impl Default for WivrnConfEncoder { bitrate: None, width: None, height: None, + offset_x: None, + offset_y: None, group: None, } } @@ -118,6 +124,8 @@ impl Default for WivrnConfig { bitrate: Some(100000000), width: Some(1.0), height: Some(1.0), + offset_x: Some(0.0), + offset_y: Some(0.0), group: None, }], } @@ -162,5 +170,7 @@ mod tests { assert_eq!(conf.encoders.get(0).unwrap().bitrate, Some(100000000)); assert_eq!(conf.encoders.get(0).unwrap().width, Some(1.0)); assert_eq!(conf.encoders.get(0).unwrap().height, Some(1.0)); + assert_eq!(conf.encoders.get(0).unwrap().offset_x, Some(0.0)); + assert_eq!(conf.encoders.get(0).unwrap().offset_y, Some(0.0)); } } diff --git a/src/profile.rs b/src/profile.rs index 651f224..b6d08e3 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -90,6 +90,7 @@ pub struct ProfileFeature { pub enabled: bool, pub path: Option, pub repo: Option, + pub branch: Option, } impl Default for ProfileFeature { @@ -99,6 +100,7 @@ impl Default for ProfileFeature { enabled: false, path: None, repo: None, + branch: None, } } } @@ -210,10 +212,12 @@ pub struct Profile { pub xrservice_type: XRServiceType, pub xrservice_path: String, pub xrservice_repo: Option, + pub xrservice_branch: Option, #[serde(default = "HashMap::::default")] pub xrservice_cmake_flags: HashMap, pub opencomposite_path: String, pub opencomposite_repo: Option, + pub opencomposite_branch: Option, pub features: ProfileFeatures, pub environment: HashMap, /** Install prefix */ @@ -243,24 +247,28 @@ impl Default for Profile { xrservice_path: format!("{}/xrservice", profile_dir), xrservice_type: XRServiceType::Monado, xrservice_repo: None, + xrservice_branch: None, xrservice_cmake_flags: HashMap::::default(), features: ProfileFeatures { libsurvive: ProfileFeature { enabled: false, path: Some(format!("{}/libsurvive", profile_dir)), repo: None, + branch: None, feature_type: ProfileFeatureType::Libsurvive, }, basalt: ProfileFeature { enabled: false, path: Some(format!("{}/basalt", profile_dir)), repo: None, + branch: None, feature_type: ProfileFeatureType::Basalt, }, openhmd: ProfileFeature { enabled: false, path: Some(format!("{}/openhmd", profile_dir)), repo: None, + branch: None, feature_type: ProfileFeatureType::OpenHmd, }, mercury_enabled: false, @@ -269,8 +277,9 @@ impl Default for Profile { prefix: format!("{}/prefixes/{}", get_data_dir(), uuid), can_be_built: true, pull_on_build: true, - opencomposite_repo: None, opencomposite_path: format!("{}/opencomposite", profile_dir), + opencomposite_repo: None, + opencomposite_branch: None, editable: true, lighthouse_driver: LighthouseDriver::default(), xrservice_launch_options: String::default(), @@ -337,17 +346,28 @@ impl Profile { dup.name = format!("Duplicate of {}", self.name); dup.xrservice_type = self.xrservice_type.clone(); dup.xrservice_repo = self.xrservice_repo.clone(); + dup.xrservice_branch = self.xrservice_branch.clone(); dup.xrservice_cmake_flags = self.xrservice_cmake_flags.clone(); dup.features.libsurvive.enabled = self.features.libsurvive.enabled; dup.features.libsurvive.repo = self.features.libsurvive.repo.clone(); + dup.features.libsurvive.branch = self.features.libsurvive.branch.clone(); dup.features.basalt.enabled = self.features.basalt.enabled; dup.features.basalt.repo = self.features.basalt.repo.clone(); + dup.features.basalt.branch = self.features.basalt.branch.clone(); dup.features.openhmd.enabled = self.features.openhmd.enabled; dup.features.openhmd.repo = self.features.openhmd.repo.clone(); + dup.features.openhmd.branch = self.features.openhmd.branch.clone(); dup.features.mercury_enabled = self.features.mercury_enabled; dup.environment = self.environment.clone(); + if dup.environment.contains_key("LD_LIBRARY_PATH".into()) { + dup.environment.insert( + "LD_LIBRARY_PATH".into(), + prepare_ld_library_path(&dup.prefix), + ); + } dup.pull_on_build = self.pull_on_build; dup.opencomposite_repo = self.opencomposite_repo.clone(); + dup.opencomposite_branch = self.opencomposite_branch.clone(); dup.lighthouse_driver = self.lighthouse_driver; dup.xrservice_launch_options = self.xrservice_launch_options.clone(); dup @@ -467,6 +487,7 @@ mod tests { enabled: true, path: Some(String::from("/home/user/libsurvive")), repo: None, + branch: None, }, basalt: ProfileFeature::default_basalt(), openhmd: ProfileFeature::default_openhmd(), @@ -494,3 +515,7 @@ mod tests { ); } } + +pub fn prepare_ld_library_path(prefix: &str) -> String { + format!("{pfx}/lib:{pfx}/lib64", pfx = prefix) +} diff --git a/src/profiles/lighthouse.rs b/src/profiles/lighthouse.rs index f6a1c34..43a64eb 100644 --- a/src/profiles/lighthouse.rs +++ b/src/profiles/lighthouse.rs @@ -1,7 +1,7 @@ use crate::{ constants::APP_NAME, paths::{data_monado_path, data_opencomposite_path, get_data_dir}, - profile::{LighthouseDriver, Profile, ProfileFeatures, XRServiceType}, + profile::{prepare_ld_library_path, LighthouseDriver, Profile, ProfileFeatures, XRServiceType}, }; use std::collections::HashMap; @@ -15,10 +15,7 @@ pub fn lighthouse_profile() -> Profile { environment.insert("XRT_DEBUG_GUI".into(), "1".into()); environment.insert("XRT_CURATED_GUI".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); - environment.insert( - "LD_LIBRARY_PATH".into(), - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), - ); + environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); Profile { uuid: "lighthouse-default".into(), name: format!("Lighthouse Driver - {name} Default", name = APP_NAME), diff --git a/src/profiles/mod.rs b/src/profiles/mod.rs index 4266673..b942f04 100644 --- a/src/profiles/mod.rs +++ b/src/profiles/mod.rs @@ -1,5 +1,6 @@ pub mod lighthouse; pub mod openhmd; +pub mod simulated; pub mod survive; pub mod wivrn; pub mod wmr; diff --git a/src/profiles/openhmd.rs b/src/profiles/openhmd.rs index d7ac3e9..3f20432 100644 --- a/src/profiles/openhmd.rs +++ b/src/profiles/openhmd.rs @@ -2,8 +2,8 @@ use crate::{ constants::APP_NAME, paths::{data_monado_path, data_opencomposite_path, data_openhmd_path, get_data_dir}, profile::{ - LighthouseDriver, Profile, ProfileFeature, ProfileFeatureType, ProfileFeatures, - XRServiceType, + prepare_ld_library_path, LighthouseDriver, Profile, ProfileFeature, ProfileFeatureType, + ProfileFeatures, XRServiceType, }, }; use std::collections::HashMap; @@ -18,10 +18,7 @@ pub fn openhmd_profile() -> Profile { environment.insert("XRT_DEBUG_GUI".into(), "1".into()); environment.insert("XRT_CURATED_GUI".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); - environment.insert( - "LD_LIBRARY_PATH".into(), - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), - ); + environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); Profile { uuid: "openhmd-default".into(), name: format!("OpenHMD - {name} Default", name = APP_NAME), @@ -34,6 +31,7 @@ pub fn openhmd_profile() -> Profile { enabled: true, path: Some(data_openhmd_path()), repo: None, + branch: None, }, ..Default::default() }, diff --git a/src/profiles/simulated.rs b/src/profiles/simulated.rs new file mode 100644 index 0000000..d04d4c4 --- /dev/null +++ b/src/profiles/simulated.rs @@ -0,0 +1,36 @@ +use crate::{ + constants::APP_NAME, + paths::{data_monado_path, data_opencomposite_path, get_data_dir}, + profile::{Profile, ProfileFeatures, XRServiceType}, +}; +use std::collections::HashMap; + +pub fn simulated_profile() -> Profile { + let data_dir = get_data_dir(); + let prefix = format!("{data}/prefixes/simulated_default", data = data_dir); + let mut environment: HashMap = HashMap::new(); + environment.insert("QWERTY_ENABLE".into(), "1".into()); + environment.insert("XRT_JSON_LOG".into(), "1".into()); + environment.insert("XRT_COMPOSITOR_SCALE_PERCENTAGE".into(), "140".into()); + environment.insert("XRT_COMPOSITOR_COMPUTE".into(), "1".into()); + environment.insert("XRT_DEBUG_GUI".into(), "1".into()); + environment.insert("XRT_CURATED_GUI".into(), "1".into()); + environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); + environment.insert( + "LD_LIBRARY_PATH".into(), + format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), + ); + Profile { + uuid: "simulated-default".into(), + name: format!("Simulated Driver - {name} Default", name = APP_NAME), + xrservice_path: data_monado_path(), + xrservice_type: XRServiceType::Monado, + opencomposite_path: data_opencomposite_path(), + features: ProfileFeatures::default(), + environment, + prefix, + can_be_built: true, + editable: false, + ..Default::default() + } +} diff --git a/src/profiles/survive.rs b/src/profiles/survive.rs index 67fa07d..ae767db 100644 --- a/src/profiles/survive.rs +++ b/src/profiles/survive.rs @@ -2,8 +2,8 @@ use crate::{ constants::APP_NAME, paths::{data_libsurvive_path, data_monado_path, data_opencomposite_path, get_data_dir}, profile::{ - LighthouseDriver, Profile, ProfileFeature, ProfileFeatureType, ProfileFeatures, - XRServiceType, + prepare_ld_library_path, LighthouseDriver, Profile, ProfileFeature, ProfileFeatureType, + ProfileFeatures, XRServiceType, }, }; use std::collections::HashMap; @@ -20,10 +20,7 @@ pub fn survive_profile() -> Profile { environment.insert("XRT_DEBUG_GUI".into(), "1".into()); environment.insert("XRT_CURATED_GUI".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); - environment.insert( - "LD_LIBRARY_PATH".into(), - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), - ); + environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); Profile { uuid: "survive-default".into(), name: format!("Survive - {name} Default", name = APP_NAME), @@ -36,6 +33,7 @@ pub fn survive_profile() -> Profile { enabled: true, path: Some(data_libsurvive_path()), repo: None, + branch: None, }, ..Default::default() }, diff --git a/src/profiles/wivrn.rs b/src/profiles/wivrn.rs index 25e5cdd..6f4d0c7 100644 --- a/src/profiles/wivrn.rs +++ b/src/profiles/wivrn.rs @@ -1,7 +1,7 @@ use crate::{ constants::APP_NAME, paths::{data_opencomposite_path, data_wivrn_path, get_data_dir}, - profile::{Profile, ProfileFeatures, XRServiceType}, + profile::{prepare_ld_library_path, Profile, ProfileFeatures, XRServiceType}, }; use std::collections::HashMap; @@ -9,10 +9,7 @@ pub fn wivrn_profile() -> Profile { let data_dir = get_data_dir(); let prefix = format!("{data}/prefixes/wivrn_default", data = data_dir); let mut environment: HashMap = HashMap::new(); - environment.insert( - "LD_LIBRARY_PATH".into(), - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), - ); + environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); environment.insert("XRT_DEBUG_GUI".into(), "1".into()); environment.insert("XRT_CURATED_GUI".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); diff --git a/src/profiles/wmr.rs b/src/profiles/wmr.rs index bf7d07c..0b1124e 100644 --- a/src/profiles/wmr.rs +++ b/src/profiles/wmr.rs @@ -2,8 +2,8 @@ use crate::{ constants::APP_NAME, paths::{data_basalt_path, data_monado_path, data_opencomposite_path, get_data_dir}, profile::{ - LighthouseDriver, Profile, ProfileFeature, ProfileFeatureType, ProfileFeatures, - XRServiceType, + prepare_ld_library_path, LighthouseDriver, Profile, ProfileFeature, ProfileFeatureType, + ProfileFeatures, XRServiceType, }, }; use std::collections::HashMap; @@ -18,10 +18,7 @@ pub fn wmr_profile() -> Profile { environment.insert("XRT_DEBUG_GUI".into(), "1".into()); environment.insert("XRT_CURATED_GUI".into(), "1".into()); environment.insert("U_PACING_APP_USE_MIN_FRAME_PERIOD".into(), "1".into()); - environment.insert( - "LD_LIBRARY_PATH".into(), - format!("{pfx}/lib:{pfx}/lib64", pfx = prefix), - ); + environment.insert("LD_LIBRARY_PATH".into(), prepare_ld_library_path(&prefix)); Profile { uuid: "wmr-default".into(), name: format!("WMR - {name} Default", name = APP_NAME), @@ -34,6 +31,7 @@ pub fn wmr_profile() -> Profile { enabled: true, path: Some(data_basalt_path()), repo: None, + branch: None, }, mercury_enabled: true, ..Default::default() diff --git a/src/ui/app.rs b/src/ui/app.rs index 3daa827..f77967a 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -40,6 +40,7 @@ use crate::paths::{get_data_dir, get_ipc_file_path}; use crate::profile::{Profile, XRServiceType}; use crate::profiles::lighthouse::lighthouse_profile; use crate::profiles::openhmd::openhmd_profile; +use crate::profiles::simulated::simulated_profile; use crate::profiles::survive::survive_profile; use crate::profiles::wivrn::wivrn_profile; use crate::profiles::wmr::wmr_profile; @@ -256,6 +257,7 @@ impl App { wivrn_profile(), wmr_profile(), openhmd_profile(), + simulated_profile(), ]; profiles.extend(config.user_profiles.clone()); profiles.sort_unstable_by(|a, b| a.name.cmp(&b.name)); diff --git a/src/ui/build_window.rs b/src/ui/build_window.rs index 3ef337b..229719c 100644 --- a/src/ui/build_window.rs +++ b/src/ui/build_window.rs @@ -1,3 +1,4 @@ +use super::term_widget::TermWidget; use gtk::prelude::*; use relm4::prelude::*; @@ -11,20 +12,15 @@ pub enum BuildStatus { #[tracker::track] pub struct BuildWindow { title: String, - content: String, can_close: bool, build_status: BuildStatus, - #[tracker::do_not_track] - pub textbuf: gtk::TextBuffer, - #[tracker::do_not_track] - pub textview: Option, #[tracker::do_not_track] pub win: Option, #[tracker::do_not_track] build_status_label: Option, #[tracker::do_not_track] - scrolledwin: Option, + term: TermWidget, } #[derive(Debug)] @@ -108,26 +104,7 @@ impl SimpleComponent for BuildWindow { } }, }, - #[name(scrolledwin)] - gtk::ScrolledWindow { - set_hexpand: true, - set_vexpand: true, - set_margin_all: 12, - add_css_class: "card", - set_overflow: gtk::Overflow::Hidden, - #[name(textview)] - gtk::TextView { - set_hexpand: true, - set_vexpand: true, - set_editable: false, - set_monospace: true, - set_left_margin: 6, - set_right_margin: 6, - set_top_margin: 6, - set_bottom_margin: 6, - set_buffer: Some(&model.textbuf), - }, - }, + model.term.container.clone(), }, add_bottom_bar: bottom_bar = >k::Button { add_css_class: "pill", @@ -150,10 +127,10 @@ impl SimpleComponent for BuildWindow { match message { Self::Input::Present => { - self.win.as_ref().unwrap().present(); + self.term.set_color_scheme(); sender.input(BuildWindowMsg::UpdateBuildStatus(BuildStatus::Building)); - self.set_content("".into()); - self.textbuf.set_text(""); + self.term.clear(); + self.win.as_ref().unwrap().present(); } Self::Input::UpdateTitle(t) => { self.set_title(t); @@ -161,17 +138,7 @@ impl SimpleComponent for BuildWindow { Self::Input::UpdateContent(c) => { if !c.is_empty() { let n_lines = c.concat(); - let mut n_content = self.content.clone(); - n_content.push_str(n_lines.as_str()); - self.set_content(n_content); - self.textbuf - .insert(&mut self.textbuf.end_iter(), n_lines.as_str()); - let textbuf = self.textbuf.clone(); - let textview = self.textview.as_ref().unwrap().clone(); - gtk::glib::idle_add_local_once(move || { - let end_mark = textbuf.create_mark(None, &textbuf.end_iter(), false); - textview.scroll_mark_onscreen(&end_mark); - }); + self.term.feed(&n_lines); } } Self::Input::UpdateBuildStatus(status) => { @@ -202,20 +169,21 @@ impl SimpleComponent for BuildWindow { let mut model = Self { tracker: 0, title: "".into(), - content: "".into(), can_close: false, - textbuf: gtk::TextBuffer::builder().enable_undo(false).build(), - textview: None, build_status: BuildStatus::Building, win: None, - scrolledwin: None, build_status_label: None, + term: { + let t = TermWidget::new(); + t.container.add_css_class("card"); + t.container.set_margin_all(12); + t.container.set_overflow(gtk::Overflow::Hidden); + t + }, }; let widgets = view_output!(); model.win = Some(widgets.win.clone()); model.build_status_label = Some(widgets.build_status_label.clone()); - model.textview = Some(widgets.textview.clone()); - model.scrolledwin = Some(widgets.scrolledwin.clone()); ComponentParts { model, widgets } } } diff --git a/src/ui/debug_view.rs b/src/ui/debug_view.rs index f205af6..88b707c 100644 --- a/src/ui/debug_view.rs +++ b/src/ui/debug_view.rs @@ -6,7 +6,9 @@ use gtk::glib::clone; use gtk::prelude::*; use relm4::prelude::*; use relm4::{ComponentSender, SimpleComponent}; -use zoha_vte4::{Terminal, TerminalExt}; +use zoha_vte4::TerminalExt; + +use super::term_widget::TermWidget; #[derive(Debug)] pub enum SearchDirection { @@ -37,13 +39,11 @@ pub struct DebugView { #[tracker::do_not_track] log_level: LogLevel, #[tracker::do_not_track] - vte_terminal: Terminal, + term: TermWidget, } pub struct DebugViewInit {} -const MAX_SCROLLBACK: u32 = 2000; - #[relm4::component(pub)] impl SimpleComponent for DebugView { type Init = DebugViewInit; @@ -115,16 +115,11 @@ impl SimpleComponent for DebugView { }, connect_entry: &search_entry, }, - #[wrap(Some)] - set_content: sw = >k::ScrolledWindow { - set_hexpand: true, - set_vexpand: true, - model.vte_terminal.clone(), - } + set_content: Some(&model.term.container), } } - fn update(&mut self, message: Self::Input, sender: ComponentSender) { + fn update(&mut self, message: Self::Input, _sender: ComponentSender) { self.reset(); match message { @@ -142,19 +137,17 @@ impl SimpleComponent for DebugView { let search_entry = self.search_entry.as_ref().unwrap().clone(); let search_text = search_entry.text().to_string(); if searchbar.is_search_mode() && !search_text.is_empty() { - if let Ok(regex) = zoha_vte4::Regex::for_search(&search_text, 0) { - self.vte_terminal.search_set_regex(Some(®ex), 0); - } + self.term.set_search_term(Some(&search_text)); } else { - self.vte_terminal.search_set_regex(None, 0); + self.term.set_search_term(None); } } Self::Input::SearchFindMatch(direction) => match direction { SearchDirection::Forward => { - self.vte_terminal.search_find_next(); + self.term.search_next(); } SearchDirection::Backward => { - self.vte_terminal.search_find_previous(); + self.term.search_prev(); } }, Self::Input::LogUpdated(n_log) => { @@ -173,15 +166,15 @@ impl SimpleComponent for DebugView { None => Some(row), }; if let Some(t) = txt { - self.vte_terminal.feed(t.as_bytes()) + self.term.feed(&t); } } } Self::Input::ClearLog => { - self.vte_terminal.feed("\x1bc".as_bytes()); + self.term.clear(); } Self::Input::SetColorScheme => { - Self::set_color_scheme(&self.vte_terminal); + self.term.set_color_scheme(); } } } @@ -223,24 +216,13 @@ impl SimpleComponent for DebugView { search_entry: None, dropdown: None, log_level: LogLevel::Trace, - vte_terminal: { - let t = Terminal::builder() - .scroll_on_output(true) - .scrollback_lines(MAX_SCROLLBACK) - .scroll_unit_is_pixels(true) - .vexpand(true) - .hexpand(true) - .build(); - t.set_clear_background(false); - t.search_set_wrap_around(true); - Self::set_color_scheme(&t); - t - }, + term: TermWidget::new(), }; + model.term.set_color_scheme(); { let sc = gtk::ShortcutController::new(); - let term = model.vte_terminal.clone(); + let term = model.term.term.clone(); sc.add_shortcut(gtk::Shortcut::new( gtk::ShortcutTrigger::parse_string("c"), Some(gtk::CallbackAction::new(move |_, _| { @@ -250,7 +232,7 @@ impl SimpleComponent for DebugView { true })), )); - let term = model.vte_terminal.clone(); + let term = model.term.term.clone(); sc.add_shortcut(gtk::Shortcut::new( gtk::ShortcutTrigger::parse_string("a"), Some(gtk::CallbackAction::new(move |_, _| { @@ -258,7 +240,7 @@ impl SimpleComponent for DebugView { true })), )); - model.vte_terminal.add_controller(sc); + model.term.term.add_controller(sc); } let widgets = view_output!(); @@ -269,13 +251,3 @@ impl SimpleComponent for DebugView { ComponentParts { model, widgets } } } - -impl DebugView { - fn set_color_scheme(term: &Terminal) { - if adw::StyleManager::default().is_dark() { - term.set_color_foreground(>k::gdk::RGBA::new(1.0, 1.0, 1.0, 1.0)); - } else { - term.set_color_foreground(>k::gdk::RGBA::new(0.0, 0.0, 0.0, 1.0)); - } - } -} diff --git a/src/ui/factories/wivrn_encoder_group_factory.rs b/src/ui/factories/wivrn_encoder_group_factory.rs index 6fd84a2..4b59dae 100644 --- a/src/ui/factories/wivrn_encoder_group_factory.rs +++ b/src/ui/factories/wivrn_encoder_group_factory.rs @@ -2,6 +2,7 @@ use crate::{ file_builders::wivrn_config::{Codec, Encoder, WivrnConfEncoder}, ui::{ preference_rows::{combo_row, number_entry_row, spin_row}, + util::{bits_from_mbits, bits_to_mbits}, wivrn_conf_editor::WivrnConfEditorMsg, }, }; @@ -24,6 +25,8 @@ pub enum WivrnEncoderModelMsg { BitrateChanged(Option), WidthChanged(Option), HeightChanged(Option), + OffsetXChanged(Option), + OffsetYChanged(Option), GroupChanged(Option), Delete, } @@ -83,9 +86,13 @@ impl AsyncFactoryComponent for WivrnEncoderModel { } ) -> adw::ComboRow, add: bitrate_row = &number_entry_row( - "Bitrate", + "Bitrate (Mbps)", &self.encoder_conf.bitrate - .and_then(|n| Some(n.to_string())) + .and_then(|n| if let Some(mbits) = bits_to_mbits(n) { + Some(mbits.to_string()) + } else { + None + }) .unwrap_or_default(), false, { @@ -96,7 +103,10 @@ impl AsyncFactoryComponent for WivrnEncoderModel { if txt.is_empty() { None } else { - Some(txt.parse::().unwrap()) + match txt.parse::() { + Ok(bits) => bits_from_mbits(bits), + Err(e) => None, + } } )); } @@ -134,6 +144,38 @@ impl AsyncFactoryComponent for WivrnEncoderModel { } } ) -> adw::SpinRow, + add: offset_x_row = &spin_row( + "Offset X", + None, + self.encoder_conf.offset_x.unwrap_or(0.0).into(), + 0.0, + 1.0, + 0.01, + { + let sender = sender.clone(); + move |adj| { + sender.input(Self::Input::OffsetXChanged( + Some(adj.value() as f32) + )); + } + } + ) -> adw::SpinRow, + add: offset_y_row = &spin_row( + "Offset Y", + None, + self.encoder_conf.offset_y.unwrap_or(0.0).into(), + 0.0, + 1.0, + 0.01, + { + let sender = sender.clone(); + move |adj| { + sender.input(Self::Input::OffsetYChanged( + Some(adj.value() as f32) + )); + } + } + ) -> adw::SpinRow, add: group_row = &spin_row( "Group", None, @@ -170,6 +212,12 @@ impl AsyncFactoryComponent for WivrnEncoderModel { Self::Input::HeightChanged(val) => { self.encoder_conf.height = val; } + Self::Input::OffsetXChanged(val) => { + self.encoder_conf.offset_x = val; + } + Self::Input::OffsetYChanged(val) => { + self.encoder_conf.offset_y = val; + } Self::Input::GroupChanged(val) => { self.encoder_conf.group = val; } diff --git a/src/ui/job_worker/internal_worker.rs b/src/ui/job_worker/internal_worker.rs index a29b656..a04e533 100644 --- a/src/ui/job_worker/internal_worker.rs +++ b/src/ui/job_worker/internal_worker.rs @@ -174,11 +174,11 @@ impl InternalJobWorker { let mut launch_opts = prof.xrservice_launch_options.trim(); let debug_launch_opts = if debug { if launch_opts.contains(LAUNCH_OPTS_CMD_PLACEHOLDER) { - format!("{} {}", "gdb -batch -ex run -ex bt --args", launch_opts) + format!("{} {}", "gdbserver localhost:9000", launch_opts) } else { format!( "{} {} {}", - "gdb -batch -ex run -ex bt --args", LAUNCH_OPTS_CMD_PLACEHOLDER, launch_opts + "gdbserver localhost:9000", LAUNCH_OPTS_CMD_PLACEHOLDER, launch_opts ) } } else { diff --git a/src/ui/libsurvive_setup_window.rs b/src/ui/libsurvive_setup_window.rs index 1f2fb7c..c4ff1c8 100644 --- a/src/ui/libsurvive_setup_window.rs +++ b/src/ui/libsurvive_setup_window.rs @@ -1,11 +1,14 @@ -use crate::{cmd_runner::CmdRunner, profile::Profile, runner::Runner}; +use super::alert::alert; +use crate::{ + cmd_runner::CmdRunner, + profile::{prepare_ld_library_path, Profile}, + runner::Runner, +}; use adw::prelude::*; use gtk::glib; use relm4::prelude::*; use std::{cell::Cell, collections::HashMap, path::Path, rc::Rc, time::Duration}; -use super::alert::alert; - const NO_FILE_MSG: &str = "(No file selected)"; const CALIBRATION_RUN_TIME_SECONDS: f64 = 30.0; @@ -57,9 +60,10 @@ impl LibsurviveSetupWindow { fn create_calibration_runner(&mut self, survive_cli_path: String) -> CmdRunner { let lh_path = self.steam_lighthouse_path.clone(); let mut env = HashMap::new(); + let profile_prefix = &self.profile.as_ref().unwrap().prefix; env.insert( - "LD_LIBRARY_PATH".to_string(), - format!("{pfx}/lib", pfx = self.profile.as_ref().unwrap().prefix), + "LD_LIBRARY_PATH".into(), + prepare_ld_library_path(&profile_prefix), ); CmdRunner::new( Some(env), diff --git a/src/ui/main_view.rs b/src/ui/main_view.rs index a6feae2..6a83996 100644 --- a/src/ui/main_view.rs +++ b/src/ui/main_view.rs @@ -185,7 +185,7 @@ impl SimpleComponent for MainView { }, }, gtk::Button { - set_label: "Start with GDB", + set_label: "Start with gdbserver", #[track = "model.changed(Self::xrservice_active()) || model.changed(Self::enable_debug_view())"] set_visible: model.enable_debug_view && !model.xrservice_active, connect_clicked[sender] => move |_| { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 5250eae..e79e785 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -16,5 +16,6 @@ pub mod profile_editor; pub mod stardust; pub mod steam_launch_options_box; mod steamvr_calibration_box; +pub mod term_widget; pub mod util; pub mod wivrn_conf_editor; diff --git a/src/ui/profile_editor.rs b/src/ui/profile_editor.rs index b3127b3..3087a92 100644 --- a/src/ui/profile_editor.rs +++ b/src/ui/profile_editor.rs @@ -176,6 +176,14 @@ impl SimpleComponent for ProfileEditor { prof.borrow_mut().xrservice_repo = (!n_val.is_empty()).then_some(n_val); }) ), + add: &entry_row( + "XR Service Branch", + model.profile.borrow().xrservice_branch.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().xrservice_branch = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: model.xrservice_cmake_flags_rows.widget(), add: opencompgrp = &adw::PreferencesGroup { @@ -197,6 +205,14 @@ impl SimpleComponent for ProfileEditor { prof.borrow_mut().opencomposite_repo = (!n_val.is_empty()).then_some(n_val); }) ), + add: &entry_row( + "OpenComposite Branch", + model.profile.borrow().opencomposite_branch.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().opencomposite_branch = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: libsurvivegrp = &adw::PreferencesGroup { set_title: "Libsurvive", @@ -225,6 +241,14 @@ impl SimpleComponent for ProfileEditor { prof.borrow_mut().features.libsurvive.repo = (!n_val.is_empty()).then_some(n_val); }) ), + add: &entry_row( + "Libsurvive Branch", + model.profile.borrow().features.libsurvive.branch.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().features.libsurvive.branch = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: openhmdgrp = &adw::PreferencesGroup { set_title: "OpenHMD", @@ -253,6 +277,14 @@ impl SimpleComponent for ProfileEditor { prof.borrow_mut().features.openhmd.repo = (!n_val.is_empty()).then_some(n_val); }) ), + add: &entry_row( + "OpenHMD Branch", + model.profile.borrow().features.openhmd.branch.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().features.openhmd.branch = (!n_val.is_empty()).then_some(n_val); + }) + ), }, add: basaltgrp = &adw::PreferencesGroup { set_title: "Basalt", @@ -281,6 +313,14 @@ impl SimpleComponent for ProfileEditor { prof.borrow_mut().features.basalt.repo = n_val.is_empty().then_some(n_val); }) ), + add: &entry_row( + "Basalt Branch", + model.profile.borrow().features.basalt.branch.clone().unwrap_or_default().as_str(), + clone!(@strong prof => move |row| { + let n_val = row.text().to_string(); + prof.borrow_mut().features.basalt.branch = n_val.is_empty().then_some(n_val); + }) + ), }, add: mercurygrp = &adw::PreferencesGroup { set_title: "Mercury", diff --git a/src/ui/term_widget.rs b/src/ui/term_widget.rs new file mode 100644 index 0000000..adaf2a2 --- /dev/null +++ b/src/ui/term_widget.rs @@ -0,0 +1,68 @@ +use gtk4::gdk; +use relm4::adw; +use zoha_vte4::{Terminal, TerminalExt}; + +const MAX_SCROLLBACK: u32 = 2000; + +#[derive(Debug, Clone)] +pub struct TermWidget { + pub container: gtk4::ScrolledWindow, + pub term: Terminal, +} + +impl TermWidget { + pub fn new() -> Self { + let term = Terminal::builder() + .scroll_on_output(true) + .scrollback_lines(MAX_SCROLLBACK) + .scroll_unit_is_pixels(true) + .vexpand(true) + .hexpand(true) + .build(); + term.set_clear_background(false); + term.search_set_wrap_around(true); + let container = gtk4::ScrolledWindow::builder() + .hexpand(true) + .vexpand(true) + .child(&term) + .build(); + let this = Self { container, term }; + this + } + + pub fn set_color_scheme(&self) { + // TODO: use theme colors + if adw::StyleManager::default().is_dark() { + self.term + .set_color_foreground(&gdk::RGBA::new(1.0, 1.0, 1.0, 1.0)); + } else { + self.term + .set_color_foreground(&gdk::RGBA::new(0.0, 0.0, 0.0, 1.0)); + } + } + + pub fn feed(&self, txt: &str) { + self.term.feed(txt.replace('\n', "\r\n").as_bytes()) + } + + pub fn clear(&self) { + self.term.feed("\x1bc".as_bytes()); + } + + pub fn set_search_term(&self, term: Option<&str>) { + self.term.search_set_regex( + term.map(|txt| zoha_vte4::Regex::for_search(txt, 0).ok()) + .flatten() + .as_ref(), + 0, + ); + } + + pub fn search_next(&self) { + self.term.search_find_next(); + } + + pub fn search_prev(&self) { + self.term.search_find_previous(); + } +} diff --git a/src/ui/util.rs b/src/ui/util.rs index c1826f7..ff76cf2 100644 --- a/src/ui/util.rs +++ b/src/ui/util.rs @@ -61,3 +61,11 @@ pub fn copy_text(txt: &str) { } } } + +pub fn bits_to_mbits(bits: u32) -> Option { + bits.checked_div(1000000) +} + +pub fn bits_from_mbits(mbits: u32) -> Option { + mbits.checked_mul(1000000) +} diff --git a/test/files/profile.json b/test/files/profile.json index 92a9adf..cb69c6f 100644 --- a/test/files/profile.json +++ b/test/files/profile.json @@ -4,20 +4,24 @@ "xrservice_type": "Monado", "xrservice_path": "/home/user/monado", "xrservice_repo": null, + "xrservice_branch": null, "opencomposite_path": "/home/user/opencomposite", "opencomposite_repo": null, + "opencomposite_branch": null, "features": { "libsurvive": { "feature_type": "Libsurvive", "enabled": true, "path": "/home/user/libsurvive", - "repo": null + "repo": null, + "branch": null }, "basalt": { "feature_type": "Basalt", "enabled": false, "path": null, - "repo": null + "repo": null, + "branch": null }, "mercury_enabled": false }, @@ -30,4 +34,4 @@ "can_be_built": true, "editable": true, "pull_on_build": true -} +} \ No newline at end of file diff --git a/test/files/wivrn_config.json b/test/files/wivrn_config.json index 4a38e87..aafcfd0 100644 --- a/test/files/wivrn_config.json +++ b/test/files/wivrn_config.json @@ -6,7 +6,9 @@ "codec": "h264", "bitrate": 100000000, "width": 1.0, - "height": 1.0 + "height": 1.0, + "offset_x": 0.0, + "offset_y": 0.0 } ] }