From 65039c36dd85f0f1408fbabcc03de368cc8d5a07 Mon Sep 17 00:00:00 2001 From: Gabriele Musco Date: Tue, 20 Jun 2023 19:27:58 +0200 Subject: [PATCH] feat: split install wivrn box into its own component --- src/config.rs | 11 +- src/ui/app.rs | 90 ++-------------- src/ui/install_wivrn_box.rs | 209 ++++++++++++++++++++++++++++++++++++ src/ui/main_view.rs | 160 ++++++++------------------- src/ui/mod.rs | 1 + 5 files changed, 270 insertions(+), 201 deletions(-) create mode 100644 src/ui/install_wivrn_box.rs diff --git a/src/config.rs b/src/config.rs index 1a022d0..349c452 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use crate::{ constants::CMD_NAME, - file_utils::{get_config_dir, get_writer}, + file_utils::{get_config_dir, get_writer}, profile::Profile, }; use expect_dialog::ExpectDialog; use serde::{Deserialize, Serialize}; @@ -12,6 +12,15 @@ pub struct Config { pub debug_view_enabled: bool, } +impl Config { + pub fn get_selected_profile(&self, profiles: &Vec) -> Profile { + match profiles.iter().find(|p| {p.name == self.selected_profile_name}) { + Some(p) => p.clone(), + None => profiles.get(0).expect_dialog("No profiles found").clone(), + } + } +} + fn config_file_path() -> String { format!( "{config}/{name}.json", diff --git a/src/ui/app.rs b/src/ui/app.rs index 1ee33ae..e0b85c6 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -2,8 +2,7 @@ use super::about_dialog::AboutDialog; use super::build_window::{BuildStatus, BuildWindow}; use super::debug_view::{DebugView, DebugViewMsg}; use super::libsurvive_setup_window::LibsurviveSetupWindow; -use super::main_view::{InstallWivrnStatus, MainViewMsg}; -use crate::adb::get_adb_install_runner; +use super::main_view::MainViewMsg; use crate::builders::build_basalt::get_build_basalt_runner; use crate::builders::build_libsurvive::get_build_libsurvive_runner; use crate::builders::build_monado::get_build_monado_runner; @@ -15,7 +14,6 @@ use crate::dependencies::basalt_deps::get_missing_basalt_deps; use crate::dependencies::libsurvive_deps::get_missing_libsurvive_deps; use crate::dependencies::monado_deps::get_missing_monado_deps; use crate::dependencies::wivrn_deps::get_missing_wivrn_deps; -use crate::downloader::download_file; use crate::file_builders::active_runtime_json::{ set_current_active_runtime_to_profile, set_current_active_runtime_to_steam, }; @@ -23,7 +21,6 @@ use crate::file_builders::openvrpaths_vrpath::{ set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam, }; use crate::file_utils::setcap_cap_sys_nice_eip; -use crate::paths::wivrn_apk_download_path; use crate::profile::{Profile, XRServiceType}; use crate::profiles::system_valve_index::system_valve_index_profile; use crate::profiles::valve_index::valve_index_profile; @@ -42,8 +39,6 @@ use relm4::adw::ResponseAppearance; use relm4::gtk::glib; use relm4::{new_action_group, new_stateful_action, new_stateless_action, prelude::*}; use relm4::{ComponentParts, ComponentSender, SimpleComponent}; -use std::cell::Cell; -use std::thread::JoinHandle; use std::time::Duration; #[tracker::track] @@ -75,11 +70,6 @@ pub struct App { build_pipeline: Option, #[tracker::do_not_track] profiles: Vec, - - #[tracker::do_not_track] - download_wivrn_thread: Cell>>>, - #[tracker::do_not_track] - install_wivrn_runner: Option, } #[derive(Debug)] @@ -92,8 +82,6 @@ pub enum Msg { SetXRServiceRuntime(bool), RunSetCap, OpenLibsurviveSetup, - DownloadWivrnApk, - InstallWivrnApk, } impl App { @@ -106,11 +94,8 @@ impl App { return None; } - pub fn get_selected_profile(&self) -> &Profile { - match self.get_profile_by_name(&self.config.selected_profile_name) { - Some(profile) => profile, - None => self.profiles.get(0).expect_dialog("No profiles found"), - } + pub fn get_selected_profile(&self) -> Profile { + self.config.get_selected_profile(&self.profiles) } pub fn start_xrservice(&mut self) { @@ -221,49 +206,7 @@ impl SimpleComponent for App { } } }; - if self.download_wivrn_thread.get_mut().is_some() { - let finished = self - .download_wivrn_thread - .get_mut() - .as_ref() - .unwrap() - .is_finished(); - if finished { - let joinh = self.download_wivrn_thread.take().unwrap(); - match joinh.join().unwrap() { - Ok(_) => { - sender.input(Msg::InstallWivrnApk); - } - Err(_) => { - self.main_view.sender().emit( - MainViewMsg::UpdateInstallWivrnStatus( - InstallWivrnStatus::Done(Some( - "Error downloading WiVRn APK".into(), - )), - ), - ); - } - } - } - } - match &mut self.install_wivrn_runner { - None => {} - Some(runner) => match runner.status() { - RunnerStatus::Running => {} - RunnerStatus::Stopped(status) => { - self.install_wivrn_runner.take(); - self.main_view - .sender() - .emit(MainViewMsg::UpdateInstallWivrnStatus(match status { - None | Some(0) => InstallWivrnStatus::Success, - Some(err_code) => InstallWivrnStatus::Done(Some(format!( - "ADB exited with code \"{c}\"", - c = err_code - ))), - })); - } - }, - } + self.main_view.sender().emit(MainViewMsg::ClockTicking); } Msg::EnableDebugViewChanged(val) => { self.set_enable_debug_view(val); @@ -389,8 +332,8 @@ impl SimpleComponent for App { let profile = self.get_selected_profile(); self.main_view .sender() - .emit(MainViewMsg::UpdateXRServiceType( - profile.clone().xrservice_type, + .emit(MainViewMsg::UpdateSelectedProfile( + profile.clone() )); } Msg::SetXRServiceRuntime(is_rex) => { @@ -411,23 +354,6 @@ impl SimpleComponent for App { )) .expect_dialog("Failed to present Libsurvive Setup Window"); } - Msg::DownloadWivrnApk => { - self.main_view - .sender() - .emit(MainViewMsg::UpdateInstallWivrnStatus( - InstallWivrnStatus::InProgress, - )); - self.download_wivrn_thread.set(Some(download_file( - // TODO: always download latest - "https://github.com/Meumeu/WiVRn/releases/latest/download/WiVRn-oculus-release.apk".into(), - wivrn_apk_download_path() - ))); - } - Msg::InstallWivrnApk => { - let mut runner = get_adb_install_runner(&wivrn_apk_download_path()); - runner.start(); - self.install_wivrn_runner = Some(runner); - } } } @@ -478,6 +404,7 @@ impl SimpleComponent for App { main_view: MainView::builder() .launch(MainViewInit { config: config.clone(), + selected_profile: config.get_selected_profile(&profiles), }) .forward(sender.input_sender(), |message| match message { MainViewOutMsg::UpdateView => Msg::UpdateView, @@ -485,7 +412,6 @@ impl SimpleComponent for App { MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService, MainViewOutMsg::ProfileSelected(name) => Msg::ProfileSelected(name), MainViewOutMsg::SetXRServiceRuntime(is_rex) => Msg::SetXRServiceRuntime(is_rex), - MainViewOutMsg::InstallWivrnApk => Msg::DownloadWivrnApk, }), debug_view: DebugView::builder() .launch(DebugViewInit { @@ -515,8 +441,6 @@ impl SimpleComponent for App { xrservice_runner: None, xrservice_log: vec![], build_pipeline: None, - download_wivrn_thread: Cell::new(None), - install_wivrn_runner: None, }; let widgets = view_output!(); diff --git a/src/ui/install_wivrn_box.rs b/src/ui/install_wivrn_box.rs new file mode 100644 index 0000000..660e4b9 --- /dev/null +++ b/src/ui/install_wivrn_box.rs @@ -0,0 +1,209 @@ +use crate::{ + adb::get_adb_install_runner, + downloader::download_file, + paths::wivrn_apk_download_path, + profile::{Profile, XRServiceType}, + runner::{Runner, RunnerStatus}, +}; +use gtk::prelude::*; +use relm4::prelude::*; +use std::{cell::Cell, thread::JoinHandle}; + +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum InstallWivrnStatus { + Success, + Done(Option), + InProgress, +} + +#[tracker::track] +pub struct InstallWivrnBox { + selected_profile: Profile, + install_wivrn_status: InstallWivrnStatus, + #[tracker::do_not_track] + download_thread: Cell>>>, + #[tracker::do_not_track] + install_runner: Option, +} + +#[derive(Debug)] +pub enum InstallWivrnBoxMsg { + ClockTicking, + UpdateSelectedProfile(Profile), + DownloadWivrn, + InstallWivrnApk, +} + +#[derive(Debug)] +pub struct InstallWivrnBoxInit { + pub selected_profile: Profile, +} + +#[relm4::component(pub)] +impl SimpleComponent for InstallWivrnBox { + type Init = InstallWivrnBoxInit; + type Input = InstallWivrnBoxMsg; + type Output = (); + + view! { + gtk::Box { + set_orientation: gtk::Orientation::Vertical, + set_spacing: 12, + set_margin_top: 12, + set_margin_bottom: 12, + #[track = "model.changed(Self::selected_profile())"] + set_visible: match model.selected_profile.xrservice_type { + XRServiceType::Wivrn => true, + _ => false, + }, + gtk::Separator { + set_orientation: gtk::Orientation::Horizontal, + set_hexpand: true, + }, + gtk::Label { + add_css_class: "heading", + set_hexpand: true, + set_xalign: 0.0, + set_margin_start: 12, + set_margin_end: 12, + set_label: "Install WiVRn APK", + set_wrap: true, + set_wrap_mode: gtk::pango::WrapMode::Word, + }, + gtk::Label { + add_css_class: "dim-label", + set_hexpand: true, + set_xalign: 0.0, + set_margin_start: 12, + set_margin_end: 12, + set_label: concat!( + "Install the WiVRn APK on your standalong Android headset. ", + "You will need to enable Developer Mode on your headset, ", + "then press the \"Install WiVRn\" button." + ), + set_wrap: true, + set_wrap_mode: gtk::pango::WrapMode::Word, + }, + gtk::Button { + add_css_class: "suggested-action", + set_label: "Install WiVRn", + set_margin_start: 12, + set_margin_end: 12, + set_halign: gtk::Align::Start, + #[track = "model.changed(Self::install_wivrn_status())"] + set_sensitive: match model.install_wivrn_status { + InstallWivrnStatus::InProgress => false, + _ => true, + }, + connect_clicked[sender] => move |_| { + sender.input(Self::Input::DownloadWivrn); + }, + }, + gtk::Label { + add_css_class: "error", + set_margin_start: 12, + set_margin_end: 12, + set_xalign: 0.0, + #[track = "model.changed(Self::install_wivrn_status())"] + set_visible: match &model.install_wivrn_status { + InstallWivrnStatus::Done(Some(_)) => true, + _ => false, + }, + #[track = "model.changed(Self::install_wivrn_status())"] + set_label: match &model.install_wivrn_status { + InstallWivrnStatus::Done(Some(err)) => err.as_str(), + _ => "", + }, + }, + gtk::Label { + add_css_class: "success", + set_margin_start: 12, + set_margin_end: 12, + set_xalign: 0.0, + #[track = "model.changed(Self::install_wivrn_status())"] + set_visible: match &model.install_wivrn_status { + InstallWivrnStatus::Success => true, + _ => false, + }, + set_label: "WiVRn Installed Successfully", + }, + } + } + + fn update(&mut self, message: Self::Input, sender: ComponentSender) { + self.reset(); + + match message { + Self::Input::ClockTicking => { + if self.download_thread.get_mut().is_some() { + let finished = self + .download_thread + .get_mut() + .as_ref() + .unwrap() + .is_finished(); + if finished { + let joinh = self.download_thread.take().unwrap(); + match joinh.join().unwrap() { + Ok(_) => { + sender.input(Self::Input::InstallWivrnApk); + } + Err(_) => self.set_install_wivrn_status(InstallWivrnStatus::Done( + Some("Error downloading WiVRn APK".into()), + )), + } + } + } + if self.install_runner.is_some() { + let runner = self.install_runner.as_mut().unwrap(); + match runner.status() { + RunnerStatus::Running => {} + RunnerStatus::Stopped(status) => { + self.install_runner.take(); + self.set_install_wivrn_status(match status { + None | Some(0) => InstallWivrnStatus::Success, + Some(err_code) => InstallWivrnStatus::Done(Some(format!( + "ADB exited with code \"{c}\"", + c = err_code + ))), + }); + } + }; + } + } + Self::Input::DownloadWivrn => { + self.set_install_wivrn_status(InstallWivrnStatus::InProgress); + self.download_thread.set(Some(download_file( + "https://github.com/Meumeu/WiVRn/releases/latest/download/WiVRn-oculus-release.apk".into(), + wivrn_apk_download_path() + ))); + } + Self::Input::InstallWivrnApk => { + let mut runner = get_adb_install_runner(&wivrn_apk_download_path()); + runner.start(); + self.install_runner = Some(runner); + } + Self::Input::UpdateSelectedProfile(p) => { + self.set_selected_profile(p); + } + } + } + + fn init( + init: Self::Init, + root: &Self::Root, + sender: ComponentSender, + ) -> ComponentParts { + let model = Self { + selected_profile: init.selected_profile, + install_wivrn_status: InstallWivrnStatus::Done(None), + download_thread: Cell::new(None), + install_runner: None, + tracker: 0, + }; + + let widgets = view_output!(); + + ComponentParts { model, widgets } + } +} diff --git a/src/ui/main_view.rs b/src/ui/main_view.rs index e08647c..76b713f 100644 --- a/src/ui/main_view.rs +++ b/src/ui/main_view.rs @@ -1,9 +1,7 @@ -use std::rc::Rc; - use crate::config::Config; use crate::constants::APP_NAME; use crate::file_builders::active_runtime_json::{self, get_current_active_runtime}; -use crate::profile::XRServiceType; +use crate::profile::{Profile, XRServiceType}; use crate::ui::app::{ AboutAction, BuildProfileAction, DebugViewToggleAction, LibsurviveSetupAction, }; @@ -13,12 +11,9 @@ use relm4::prelude::*; use relm4::{ComponentParts, ComponentSender, SimpleComponent}; use relm4_icons::icon_name; -#[derive(PartialEq, Eq, Debug, Clone)] -pub enum InstallWivrnStatus { - Success, - Done(Option), - InProgress, -} +use super::install_wivrn_box::{ + InstallWivrnBox, InstallWivrnBoxInit, InstallWivrnBoxMsg +}; #[tracker::track] pub struct MainView { @@ -29,11 +24,13 @@ pub struct MainView { steam_launch_options: String, #[tracker::do_not_track] profiles_dropdown: Option, - install_wivrn_status: InstallWivrnStatus, + #[tracker::do_not_track] + install_wivrn_box: Controller, } #[derive(Debug)] pub enum MainViewMsg { + ClockTicking, StartStopClicked, XRServiceActiveChanged(bool), EnableDebugViewChanged(bool), @@ -42,8 +39,7 @@ pub enum MainViewMsg { ProfileSelected(u32), SteamLaunchOptionsChanged(String), CopySteamLaunchOptions, - UpdateXRServiceType(XRServiceType), - UpdateInstallWivrnStatus(InstallWivrnStatus), + UpdateSelectedProfile(Profile), } #[derive(Debug)] @@ -53,11 +49,11 @@ pub enum MainViewOutMsg { DoStartStopXRService, ProfileSelected(String), SetXRServiceRuntime(bool), - InstallWivrnApk, } pub struct MainViewInit { pub config: Config, + pub selected_profile: Profile, } #[relm4::component(pub)] @@ -83,7 +79,7 @@ impl SimpleComponent for MainView { view! { gtk::Box { set_orientation: gtk::Orientation::Vertical, - #[track = "model.changed(MainView::enable_debug_view())"] + #[track = "model.changed(Self::enable_debug_view())"] set_hexpand: !model.enable_debug_view, set_vexpand: true, set_size_request: (270, 350), @@ -102,7 +98,7 @@ impl SimpleComponent for MainView { }, pack_start: profiles_dropdown = >k::DropDown { set_tooltip_text: Some("Profiles"), - #[track = "model.changed(MainView::profile_names())"] + #[track = "model.changed(Self::profile_names())"] set_model: Some(&{ let names: Vec<_> = model.profile_names.iter().map(String::as_str).collect(); gtk::StringList::new(&names) @@ -111,7 +107,7 @@ impl SimpleComponent for MainView { sender.input(MainViewMsg::ProfileSelected(this.selected())); }, }, - #[track = "model.changed(MainView::enable_debug_view())"] + #[track = "model.changed(Self::enable_debug_view())"] set_show_end_title_buttons: !model.enable_debug_view, }, }, @@ -130,9 +126,9 @@ impl SimpleComponent for MainView { add_css_class: "destructive-action", set_hexpand: true, set_halign: gtk::Align::Center, - #[track = "model.changed(MainView::xrservice_active())"] + #[track = "model.changed(Self::xrservice_active())"] set_class_active: ("suggested-action", !model.xrservice_active), - #[track = "model.changed(MainView::xrservice_active())"] + #[track = "model.changed(Self::xrservice_active())"] set_label: match model.xrservice_active { true => "Stop", false => "Start", @@ -179,7 +175,7 @@ impl SimpleComponent for MainView { set_spacing: 12, set_margin_top: 12, set_margin_bottom: 12, - #[track = "model.changed(MainView::xrservice_active())"] + #[track = "model.changed(Self::xrservice_active())"] set_visible: model.xrservice_active, gtk::Separator { set_orientation: gtk::Orientation::Horizontal, @@ -227,7 +223,7 @@ impl SimpleComponent for MainView { set_size_request: (-1, 150), #[wrap(Some)] set_buffer: cmdbuf = >k::TextBuffer { - #[track = "model.changed(MainView::steam_launch_options())"] + #[track = "model.changed(Self::steam_launch_options())"] set_text: model.steam_launch_options.as_str(), } }, @@ -244,88 +240,7 @@ impl SimpleComponent for MainView { }, }, }, - gtk::Box { - set_orientation: gtk::Orientation::Vertical, - set_spacing: 12, - set_margin_top: 12, - set_margin_bottom: 12, - #[track = "model.changed(MainView::xrservice_type())"] - set_visible: match model.xrservice_type { - XRServiceType::Wivrn => true, - _ => false, - }, - gtk::Separator { - set_orientation: gtk::Orientation::Horizontal, - set_hexpand: true, - }, - gtk::Label { - add_css_class: "heading", - set_hexpand: true, - set_xalign: 0.0, - set_margin_start: 12, - set_margin_end: 12, - set_label: "Install WiVRn APK", - set_wrap: true, - set_wrap_mode: gtk::pango::WrapMode::Word, - }, - gtk::Label { - add_css_class: "dim-label", - set_hexpand: true, - set_xalign: 0.0, - set_margin_start: 12, - set_margin_end: 12, - set_label: concat!( - "Install the WiVRn APK on your standalong Android headset. ", - "You will need to enable Developer Mode on your headset, ", - "then press the \"Install WiVRn\" button." - ), - set_wrap: true, - set_wrap_mode: gtk::pango::WrapMode::Word, - }, - gtk::Button { - add_css_class: "suggested-action", - set_label: "Install WiVRn", - set_margin_start: 12, - set_margin_end: 12, - set_halign: gtk::Align::Start, - #[track = "model.changed(MainView::install_wivrn_status())"] - set_sensitive: match model.install_wivrn_status { - InstallWivrnStatus::InProgress => false, - _ => true, - }, - connect_clicked[sender] => move |_| { - sender.output(MainViewOutMsg::InstallWivrnApk); - }, - }, - gtk::Label { - add_css_class: "error", - set_margin_start: 12, - set_margin_end: 12, - set_xalign: 0.0, - #[track = "model.changed(MainView::install_wivrn_status())"] - set_visible: match &model.install_wivrn_status { - InstallWivrnStatus::Done(Some(_)) => true, - _ => false, - }, - #[track = "model.changed(MainView::install_wivrn_status())"] - set_label: match &model.install_wivrn_status { - InstallWivrnStatus::Done(Some(err)) => err.as_str(), - _ => "", - }, - }, - gtk::Label { - add_css_class: "success", - set_margin_start: 12, - set_margin_end: 12, - set_xalign: 0.0, - #[track = "model.changed(MainView::install_wivrn_status())"] - set_visible: match &model.install_wivrn_status { - InstallWivrnStatus::Success => true, - _ => false, - }, - set_label: "WiVRn Installed Successfully", - }, - }, + model.install_wivrn_box.widget(), } } } @@ -335,19 +250,27 @@ impl SimpleComponent for MainView { self.reset(); match message { - MainViewMsg::StartStopClicked => { + Self::Input::ClockTicking => { + self.install_wivrn_box + .sender() + .emit(InstallWivrnBoxMsg::ClockTicking); + } + Self::Input::StartStopClicked => { sender.output(MainViewOutMsg::DoStartStopXRService); } - MainViewMsg::XRServiceActiveChanged(active) => { + Self::Input::XRServiceActiveChanged(active) => { self.set_xrservice_active(active); } - MainViewMsg::EnableDebugViewChanged(val) => { + Self::Input::EnableDebugViewChanged(val) => { self.set_enable_debug_view(val); } - MainViewMsg::UpdateXRServiceType(typ) => { - self.set_xrservice_type(typ); + Self::Input::UpdateSelectedProfile(prof) => { + self.set_xrservice_type(prof.xrservice_type.clone()); + self.install_wivrn_box + .sender() + .emit(InstallWivrnBoxMsg::UpdateSelectedProfile(prof.clone())); } - MainViewMsg::UpdateProfileNames(names, config) => { + Self::Input::UpdateProfileNames(names, config) => { self.set_profile_names(names); // why send another message to set the dropdown selection? // set_* from tracker likely updates the view obj in the next @@ -364,26 +287,25 @@ impl SimpleComponent for MainView { } })); } - MainViewMsg::SetSelectedProfile(index) => { + Self::Input::SetSelectedProfile(index) => { self.profiles_dropdown .as_ref() .unwrap() .clone() .set_selected(index); } - MainViewMsg::SteamLaunchOptionsChanged(lo) => self.set_steam_launch_options(lo), - MainViewMsg::CopySteamLaunchOptions => gtk::gdk::Display::default() + Self::Input::SteamLaunchOptionsChanged(lo) => self.set_steam_launch_options(lo), + Self::Input::CopySteamLaunchOptions => gtk::gdk::Display::default() .expect_dialog("Could not find default display") .clipboard() .set_text(self.steam_launch_options.as_str()), - MainViewMsg::ProfileSelected(position) => { + Self::Input::ProfileSelected(position) => { + // self.install_wivrn_box.sender.emit(InstallWivrnBoxMsg::Upda); + // TODO: send profile to install_wivrn_box sender.output(MainViewOutMsg::ProfileSelected( self.profile_names.get(position as usize).unwrap().clone(), )); } - MainViewMsg::UpdateInstallWivrnStatus(status) => { - self.set_install_wivrn_status(status); - } } } @@ -392,14 +314,18 @@ impl SimpleComponent for MainView { root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { - let mut model = MainView { + let mut model = Self { xrservice_active: false, xrservice_type: XRServiceType::Monado, enable_debug_view: init.config.debug_view_enabled, profiles_dropdown: None, profile_names: vec![], steam_launch_options: "".into(), - install_wivrn_status: InstallWivrnStatus::Done(None), + install_wivrn_box: InstallWivrnBox::builder() + .launch(InstallWivrnBoxInit { + selected_profile: init.selected_profile, + }) + .detach(), tracker: 0, }; let widgets = view_output!(); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 1f5832c..bb2b763 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -4,3 +4,4 @@ pub mod about_dialog; pub mod debug_view; pub mod build_window; pub mod libsurvive_setup_window; +pub mod install_wivrn_box;