mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-04-20 11:35:48 +00:00
feat: box to start wivrn when headset is wired (#117)
This commit is contained in:
parent
3a024cb9ab
commit
41e9af1676
4 changed files with 201 additions and 4 deletions
|
@ -1,4 +1,4 @@
|
|||
use super::alert::alert;
|
||||
use super::{alert::alert, ADB_ERR_NO_DEV};
|
||||
use crate::{
|
||||
async_process::async_process,
|
||||
depcheck::common::dep_adb,
|
||||
|
@ -9,8 +9,6 @@ use gtk::prelude::*;
|
|||
use relm4::{new_action_group, new_stateless_action, prelude::*};
|
||||
use std::fs::remove_file;
|
||||
|
||||
const ADB_ERR_NO_DEV: &str = "no devices/emulators found";
|
||||
|
||||
const WIVRN_LATEST_RELEASE_APK_URL: &str =
|
||||
"https://github.com/WiVRn/WiVRn/releases/latest/download/WiVRn-standard-release.apk";
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use super::{
|
|||
steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg},
|
||||
steamvr_calibration_box::{SteamVrCalibrationBox, SteamVrCalibrationBoxMsg},
|
||||
util::{limit_dropdown_width, warning_heading},
|
||||
wivrn_wired_start_box::{WivrnWiredStartBox, WivrnWiredStartBoxInit, WivrnWiredStartBoxMsg},
|
||||
};
|
||||
use crate::{
|
||||
config::Config,
|
||||
|
@ -44,6 +45,8 @@ pub struct MainView {
|
|||
#[tracker::do_not_track]
|
||||
install_wivrn_box: AsyncController<InstallWivrnBox>,
|
||||
#[tracker::do_not_track]
|
||||
wivrn_wired_start_box: AsyncController<WivrnWiredStartBox>,
|
||||
#[tracker::do_not_track]
|
||||
steam_launch_options_box: Controller<SteamLaunchOptionsBox>,
|
||||
#[tracker::do_not_track]
|
||||
devices_box: Controller<DevicesBox>,
|
||||
|
@ -364,6 +367,7 @@ impl SimpleComponent for MainView {
|
|||
},
|
||||
|
||||
model.steam_launch_options_box.widget(),
|
||||
model.wivrn_wired_start_box.widget(),
|
||||
model.install_wivrn_box.widget(),
|
||||
model.steamvr_calibration_box.widget(),
|
||||
|
||||
|
@ -494,6 +498,9 @@ impl SimpleComponent for MainView {
|
|||
self.install_wivrn_box
|
||||
.sender()
|
||||
.emit(InstallWivrnBoxMsg::UpdateSelectedProfile(prof.clone()));
|
||||
self.wivrn_wired_start_box
|
||||
.sender()
|
||||
.emit(WivrnWiredStartBoxMsg::UpdateSelectedProfile(prof.clone()));
|
||||
}
|
||||
Self::Input::UpdateProfiles(profiles, config) => {
|
||||
self.set_profiles(profiles);
|
||||
|
@ -789,6 +796,12 @@ impl SimpleComponent for MainView {
|
|||
root_win: init.root_win.clone(),
|
||||
})
|
||||
.detach(),
|
||||
wivrn_wired_start_box: WivrnWiredStartBox::builder()
|
||||
.launch(WivrnWiredStartBoxInit {
|
||||
selected_profile: init.selected_profile.clone(),
|
||||
root_win: init.root_win.clone(),
|
||||
})
|
||||
.detach(),
|
||||
devices_box: DevicesBox::builder().launch(()).detach(),
|
||||
selected_profile: init.selected_profile.clone(),
|
||||
profile_not_editable_dialog,
|
||||
|
|
|
@ -19,7 +19,9 @@ mod steamvr_calibration_box;
|
|||
mod term_widget;
|
||||
mod util;
|
||||
mod wivrn_conf_editor;
|
||||
pub mod wivrn_encoder_presets_win;
|
||||
mod wivrn_encoder_presets_win;
|
||||
mod wivrn_wired_start_box;
|
||||
|
||||
pub const SENDER_IO_ERR_MSG: &str = "relm4 sender i/o failed";
|
||||
pub const ADW_DIALOG_WIDTH: i32 = 600;
|
||||
pub const ADB_ERR_NO_DEV: &str = "no devices/emulators found";
|
||||
|
|
184
src/ui/wivrn_wired_start_box.rs
Normal file
184
src/ui/wivrn_wired_start_box.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
use super::{alert::alert, ADB_ERR_NO_DEV};
|
||||
use crate::{
|
||||
async_process::async_process,
|
||||
constants::APP_NAME,
|
||||
depcheck::common::dep_adb,
|
||||
profile::{Profile, XRServiceType},
|
||||
};
|
||||
use gtk::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum StartClientStatus {
|
||||
Success,
|
||||
Done(Option<String>),
|
||||
InProgress,
|
||||
}
|
||||
|
||||
#[tracker::track]
|
||||
pub struct WivrnWiredStartBox {
|
||||
selected_profile: Profile,
|
||||
start_client_status: StartClientStatus,
|
||||
#[tracker::do_not_track]
|
||||
root_win: gtk::Window,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
pub enum WivrnWiredStartBoxMsg {
|
||||
UpdateSelectedProfile(Profile),
|
||||
/// prepares state for async action, calls DoStartClient
|
||||
StartWivrnClient,
|
||||
/// actually sends the adb commands to start the client
|
||||
DoStartClient,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WivrnWiredStartBoxInit {
|
||||
pub selected_profile: Profile,
|
||||
pub root_win: gtk::Window,
|
||||
}
|
||||
|
||||
#[relm4::component(pub async)]
|
||||
impl AsyncComponent for WivrnWiredStartBox {
|
||||
type Init = WivrnWiredStartBoxInit;
|
||||
type Input = WivrnWiredStartBoxMsg;
|
||||
type Output = ();
|
||||
type CommandOutput = ();
|
||||
|
||||
view! {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 12,
|
||||
add_css_class: "card",
|
||||
add_css_class: "padded",
|
||||
#[track = "model.changed(Self::selected_profile())"]
|
||||
set_visible: model.selected_profile.xrservice_type == XRServiceType::Wivrn,
|
||||
gtk::Label {
|
||||
add_css_class: "heading",
|
||||
set_hexpand: true,
|
||||
set_xalign: 0.0,
|
||||
set_label: "Start WiVRn Client (Wired)",
|
||||
set_wrap: true,
|
||||
set_wrap_mode: gtk::pango::WrapMode::Word,
|
||||
},
|
||||
gtk::Label {
|
||||
add_css_class: "dim-label",
|
||||
set_hexpand: true,
|
||||
set_label: concat!(
|
||||
"Start the WiVRn client on your Android headset. This only ",
|
||||
"works in wired connection mode."
|
||||
),
|
||||
set_xalign: 0.0,
|
||||
set_wrap: true,
|
||||
set_wrap_mode: gtk::pango::WrapMode::Word,
|
||||
},
|
||||
gtk::Button {
|
||||
add_css_class: "suggested-action",
|
||||
set_label: "Start WiVRn Client",
|
||||
set_halign: gtk::Align::Start,
|
||||
#[track = "model.changed(Self::start_client_status())"]
|
||||
set_sensitive: model.start_client_status != StartClientStatus::InProgress,
|
||||
connect_clicked[sender] => move |_| {
|
||||
sender.input(Self::Input::StartWivrnClient)
|
||||
},
|
||||
},
|
||||
gtk::Label {
|
||||
add_css_class: "error",
|
||||
set_xalign: 0.0,
|
||||
set_wrap: true,
|
||||
set_wrap_mode: gtk::pango::WrapMode::Word,
|
||||
#[track = "model.changed(Self::start_client_status())"]
|
||||
set_visible: matches!(&model.start_client_status, StartClientStatus::Done(Some(_))),
|
||||
#[track = "model.changed(Self::start_client_status())"]
|
||||
set_label: match &model.start_client_status {
|
||||
StartClientStatus::Done(Some(err)) => err.as_str(),
|
||||
_ => "",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn update(
|
||||
&mut self,
|
||||
message: Self::Input,
|
||||
sender: AsyncComponentSender<Self>,
|
||||
_root: &Self::Root,
|
||||
) {
|
||||
self.reset();
|
||||
|
||||
match message {
|
||||
Self::Input::UpdateSelectedProfile(p) => self.set_selected_profile(p),
|
||||
Self::Input::StartWivrnClient => {
|
||||
if !dep_adb().check() {
|
||||
alert("ADB is not installed", Some(&format!("Please install ADB on your computer to start the WiVRn client from {}.", APP_NAME)), Some(&self.root_win));
|
||||
return;
|
||||
}
|
||||
self.set_start_client_status(StartClientStatus::InProgress);
|
||||
sender.input(Self::Input::DoStartClient);
|
||||
}
|
||||
Self::Input::DoStartClient => {
|
||||
let n_status = match async_process(
|
||||
"sh",
|
||||
Some(&[
|
||||
"-c",
|
||||
concat!(
|
||||
"adb reverse tcp:9757 tcp:9757 && ",
|
||||
"adb shell am force-stop org.meumeu.wivrn && ",
|
||||
// wait for force-stop
|
||||
"sleep 1 && ",
|
||||
"adb shell am start -a android.intent.action.VIEW -d \"wivrn+tcp://127.0.0.1\" org.meumeu.wivrn"
|
||||
)
|
||||
]),
|
||||
None
|
||||
).await {
|
||||
Ok(out) if out.exit_code == 0 =>
|
||||
StartClientStatus::Success
|
||||
,
|
||||
Ok(out) => {
|
||||
if out.stdout.contains(ADB_ERR_NO_DEV)
|
||||
|| out.stderr.contains(ADB_ERR_NO_DEV)
|
||||
{
|
||||
StartClientStatus::Done(Some(
|
||||
concat!(
|
||||
"No devices connected. Make sure your headset is connected ",
|
||||
"to this computer and USB debugging is turned on."
|
||||
)
|
||||
.into(),
|
||||
))
|
||||
} else {
|
||||
eprintln!("Error: ADB failed with code {}.\nstdout:\n{}\n======\nstderr:\n{}", out.exit_code, out.stdout, out.stderr);
|
||||
StartClientStatus::Done(Some(
|
||||
format!("ADB exited with code \"{}\"", out.exit_code)
|
||||
))
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error: failed to run ADB: {e}");
|
||||
StartClientStatus::Done(Some(
|
||||
"Failed to run ADB".into()
|
||||
))
|
||||
},
|
||||
};
|
||||
self.set_start_client_status(n_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn init(
|
||||
init: Self::Init,
|
||||
root: Self::Root,
|
||||
sender: AsyncComponentSender<Self>,
|
||||
) -> AsyncComponentParts<Self> {
|
||||
let model = Self {
|
||||
selected_profile: init.selected_profile,
|
||||
start_client_status: StartClientStatus::Done(None),
|
||||
root_win: init.root_win,
|
||||
tracker: 0,
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
AsyncComponentParts { model, widgets }
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue