feat: split install wivrn box into its own component

This commit is contained in:
Gabriele Musco 2023-06-20 19:27:58 +02:00
parent d9c8e6e728
commit 65039c36dd
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
5 changed files with 270 additions and 201 deletions

View file

@ -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>) -> 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",

View file

@ -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<RunnerPipeline>,
#[tracker::do_not_track]
profiles: Vec<Profile>,
#[tracker::do_not_track]
download_wivrn_thread: Cell<Option<JoinHandle<Result<(), reqwest::Error>>>>,
#[tracker::do_not_track]
install_wivrn_runner: Option<Runner>,
}
#[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!();

209
src/ui/install_wivrn_box.rs Normal file
View file

@ -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<String>),
InProgress,
}
#[tracker::track]
pub struct InstallWivrnBox {
selected_profile: Profile,
install_wivrn_status: InstallWivrnStatus,
#[tracker::do_not_track]
download_thread: Cell<Option<JoinHandle<Result<(), reqwest::Error>>>>,
#[tracker::do_not_track]
install_runner: Option<Runner>,
}
#[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>) {
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<Self>,
) -> ComponentParts<Self> {
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 }
}
}

View file

@ -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<String>),
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<gtk::DropDown>,
install_wivrn_status: InstallWivrnStatus,
#[tracker::do_not_track]
install_wivrn_box: Controller<InstallWivrnBox>,
}
#[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 = &gtk::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 = &gtk::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<Self>,
) -> ComponentParts<Self> {
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!();

View file

@ -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;