App launcher
[skip ci]
This commit is contained in:
@@ -1,27 +1,71 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use anyhow::Context;
|
||||
use wayvr_ipc::{packet_client::WvrProcessLaunchParams, packet_server::WvrDisplayHandle};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::checkbox::ComponentCheckbox,
|
||||
globals::WguiGlobals,
|
||||
i18n::Translation,
|
||||
layout::{Layout, WidgetID},
|
||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||
widget::label::WidgetLabel,
|
||||
};
|
||||
use wlx_common::dash_interface::BoxDashInterface;
|
||||
|
||||
use crate::util::desktop_finder::DesktopEntry;
|
||||
use crate::{
|
||||
frontend::{FrontendTask, FrontendTasks},
|
||||
settings::SettingsIO,
|
||||
task::Tasks,
|
||||
util::desktop_finder::DesktopEntry,
|
||||
views::display_list,
|
||||
};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
enum RunMode {
|
||||
Cage,
|
||||
Wayland,
|
||||
}
|
||||
|
||||
enum Task {
|
||||
SetRunMode(RunMode),
|
||||
DisplayClick(WvrDisplayHandle),
|
||||
}
|
||||
|
||||
struct LaunchParams<'a> {
|
||||
display_handle: WvrDisplayHandle,
|
||||
application: &'a DesktopEntry,
|
||||
run_mode: RunMode,
|
||||
globals: &'a WguiGlobals,
|
||||
frontend_tasks: &'a FrontendTasks,
|
||||
interface: &'a mut BoxDashInterface,
|
||||
on_launched: &'a dyn Fn(),
|
||||
}
|
||||
|
||||
pub struct View {
|
||||
#[allow(dead_code)]
|
||||
pub state: ParserState,
|
||||
//entry: DesktopEntry,
|
||||
state: ParserState,
|
||||
entry: DesktopEntry,
|
||||
view_display_list: display_list::View,
|
||||
tasks: Tasks<Task>,
|
||||
frontend_tasks: FrontendTasks,
|
||||
globals: WguiGlobals,
|
||||
|
||||
cb_cage_mode: Rc<ComponentCheckbox>,
|
||||
cb_wayland_mode: Rc<ComponentCheckbox>,
|
||||
run_mode: RunMode,
|
||||
|
||||
on_launched: Box<dyn Fn()>,
|
||||
}
|
||||
|
||||
pub struct Params<'a> {
|
||||
pub globals: WguiGlobals,
|
||||
pub globals: &'a WguiGlobals,
|
||||
pub entry: DesktopEntry,
|
||||
pub layout: &'a mut Layout,
|
||||
pub parent_id: WidgetID,
|
||||
pub settings: &'a dyn SettingsIO,
|
||||
pub frontend_tasks: &'a FrontendTasks,
|
||||
pub on_launched: Box<dyn Fn()>,
|
||||
}
|
||||
|
||||
impl View {
|
||||
@@ -33,6 +77,44 @@ impl View {
|
||||
};
|
||||
|
||||
let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
|
||||
|
||||
let cb_cage_mode = state.fetch_component_as::<ComponentCheckbox>("cb_cage_mode")?;
|
||||
let cb_wayland_mode = state.fetch_component_as::<ComponentCheckbox>("cb_wayland_mode")?;
|
||||
|
||||
{
|
||||
let mut label_exec = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_exec")?;
|
||||
let mut label_args = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_args")?;
|
||||
|
||||
label_exec.set_text_simple(
|
||||
&mut params.globals.get(),
|
||||
Translation::from_raw_text_string(params.entry.app_name.clone()),
|
||||
);
|
||||
|
||||
label_args.set_text_simple(
|
||||
&mut params.globals.get(),
|
||||
Translation::from_raw_text_string(params.entry.exec_args.join(" ")),
|
||||
);
|
||||
}
|
||||
|
||||
let display_list_parent = state.fetch_widget(¶ms.layout.state, "display_list_parent")?.id;
|
||||
|
||||
let tasks = Tasks::new();
|
||||
|
||||
let on_display_click = {
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |disp_handle: WvrDisplayHandle| {
|
||||
tasks.push(Task::DisplayClick(disp_handle));
|
||||
})
|
||||
};
|
||||
|
||||
let view_display_list = display_list::View::new(display_list::Params {
|
||||
frontend_tasks: params.frontend_tasks.clone(),
|
||||
globals: params.globals,
|
||||
layout: params.layout,
|
||||
parent_id: display_list_parent,
|
||||
on_click: Some(on_display_click),
|
||||
})?;
|
||||
|
||||
let id_icon_parent = state.get_widget_id("icon_parent")?;
|
||||
|
||||
// app icon
|
||||
@@ -48,6 +130,30 @@ impl View {
|
||||
)?;
|
||||
}
|
||||
|
||||
let run_mode = if params.settings.get().tweaks.xwayland_by_default {
|
||||
RunMode::Cage
|
||||
} else {
|
||||
RunMode::Wayland
|
||||
};
|
||||
|
||||
tasks.push(Task::SetRunMode(run_mode.clone()));
|
||||
|
||||
cb_cage_mode.on_toggle({
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_, _| {
|
||||
tasks.push(Task::SetRunMode(RunMode::Cage));
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
cb_wayland_mode.on_toggle({
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_, _| {
|
||||
tasks.push(Task::SetRunMode(RunMode::Wayland));
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
let mut label_title = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_title")?;
|
||||
|
||||
label_title.set_text_simple(
|
||||
@@ -56,8 +162,139 @@ impl View {
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
//entry: params.entry,
|
||||
state,
|
||||
view_display_list,
|
||||
tasks,
|
||||
cb_cage_mode,
|
||||
cb_wayland_mode,
|
||||
run_mode,
|
||||
entry: params.entry,
|
||||
frontend_tasks: params.frontend_tasks.clone(),
|
||||
globals: params.globals.clone(),
|
||||
on_launched: params.on_launched,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, layout: &mut Layout, interface: &mut BoxDashInterface) -> anyhow::Result<()> {
|
||||
loop {
|
||||
let tasks = self.tasks.drain();
|
||||
if tasks.is_empty() {
|
||||
break;
|
||||
}
|
||||
for task in tasks {
|
||||
match task {
|
||||
Task::SetRunMode(run_mode) => self.action_set_run_mode(layout, run_mode)?,
|
||||
Task::DisplayClick(disp_handle) => self.action_display_click(disp_handle, interface),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.view_display_list.update(layout, interface)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn action_set_run_mode(&mut self, layout: &mut Layout, run_mode: RunMode) -> anyhow::Result<()> {
|
||||
let (n1, n2) = match run_mode {
|
||||
RunMode::Cage => (true, false),
|
||||
RunMode::Wayland => (false, true),
|
||||
};
|
||||
|
||||
let mut c = layout.start_common();
|
||||
self.cb_cage_mode.set_checked(&mut c.common(), n1);
|
||||
self.cb_wayland_mode.set_checked(&mut c.common(), n2);
|
||||
|
||||
c.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn action_display_click(&mut self, handle: WvrDisplayHandle, interface: &mut BoxDashInterface) {
|
||||
View::try_launch(LaunchParams {
|
||||
application: &self.entry,
|
||||
display_handle: handle,
|
||||
frontend_tasks: &self.frontend_tasks,
|
||||
globals: &self.globals,
|
||||
run_mode: self.run_mode.clone(),
|
||||
interface,
|
||||
on_launched: &self.on_launched,
|
||||
});
|
||||
}
|
||||
|
||||
fn try_launch(params: LaunchParams) {
|
||||
let globals = params.globals.clone();
|
||||
let frontend_tasks = params.frontend_tasks.clone();
|
||||
|
||||
// launch app itself
|
||||
let Err(e) = View::launch(params) else { return };
|
||||
|
||||
let str_failed = globals.i18n().translate("FAILED_TO_LAUNCH_APPLICATION");
|
||||
frontend_tasks.push(FrontendTask::PushToast(Translation::from_raw_text_string(format!(
|
||||
"{} {:?}",
|
||||
str_failed, e
|
||||
))));
|
||||
}
|
||||
|
||||
fn launch(params: LaunchParams) -> anyhow::Result<()> {
|
||||
let mut env = Vec::<String>::new();
|
||||
|
||||
if params.run_mode == RunMode::Wayland {
|
||||
// This list could be larger, feel free to expand it
|
||||
env.push("QT_QPA_PLATFORM=wayland".into());
|
||||
env.push("GDK_BACKEND=wayland".into());
|
||||
env.push("SDL_VIDEODRIVER=wayland".into());
|
||||
env.push("XDG_SESSION_TYPE=wayland".into());
|
||||
env.push("ELECTRON_OZONE_PLATFORM_HINT=wayland".into());
|
||||
}
|
||||
|
||||
// TODO: refactor this after we ditch old wayvr-dashboard completely
|
||||
let desktop_file = params.application.to_desktop_file();
|
||||
let mut userdata = HashMap::<String, String>::new();
|
||||
userdata.insert("desktop_file".into(), serde_json::to_string(&desktop_file)?);
|
||||
|
||||
let exec_args_str = desktop_file.exec_args.join(" ");
|
||||
|
||||
params
|
||||
.interface
|
||||
.display_set_visible(params.display_handle.clone(), true)?;
|
||||
|
||||
let args = match params.run_mode {
|
||||
RunMode::Cage => format!("-- {} {}", desktop_file.exec_path, exec_args_str),
|
||||
RunMode::Wayland => exec_args_str,
|
||||
};
|
||||
|
||||
let exec = match params.run_mode {
|
||||
RunMode::Cage => "cage",
|
||||
RunMode::Wayland => &desktop_file.name,
|
||||
};
|
||||
|
||||
let display = params
|
||||
.interface
|
||||
.display_get(params.display_handle.clone())
|
||||
.context("Display not found")?;
|
||||
|
||||
params.interface.process_launch(WvrProcessLaunchParams {
|
||||
env,
|
||||
exec: String::from(exec),
|
||||
name: desktop_file.name,
|
||||
target_display: params.display_handle,
|
||||
args,
|
||||
userdata,
|
||||
})?;
|
||||
|
||||
let str_launched_on = params
|
||||
.globals
|
||||
.i18n()
|
||||
.translate_and_replace("APPLICATION_LAUNCHED_ON", ("{DISPLAY_NAME}", &display.name));
|
||||
|
||||
params
|
||||
.frontend_tasks
|
||||
.push(FrontendTask::PushToast(Translation::from_raw_text_string(
|
||||
str_launched_on,
|
||||
)));
|
||||
|
||||
(*params.on_launched)();
|
||||
|
||||
// we're done!
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use wayvr_ipc::{
|
||||
packet_client::{self},
|
||||
packet_server::{self},
|
||||
packet_server::{self, WvrDisplayHandle},
|
||||
};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
@@ -31,16 +31,17 @@ use crate::{
|
||||
enum Task {
|
||||
AddDisplay,
|
||||
AddDisplayFinish(add_display::Result),
|
||||
DisplayOptions(packet_server::WvrDisplay),
|
||||
DisplayClicked(packet_server::WvrDisplay),
|
||||
DisplayOptionsFinish,
|
||||
Refresh,
|
||||
}
|
||||
|
||||
pub struct Params<'a> {
|
||||
pub globals: WguiGlobals,
|
||||
pub globals: &'a WguiGlobals,
|
||||
pub frontend_tasks: FrontendTasks,
|
||||
pub layout: &'a mut Layout,
|
||||
pub parent_id: WidgetID,
|
||||
pub on_click: Option<Box<dyn Fn(WvrDisplayHandle)>>,
|
||||
}
|
||||
|
||||
struct State {
|
||||
@@ -56,6 +57,7 @@ pub struct View {
|
||||
globals: WguiGlobals,
|
||||
state: Rc<RefCell<State>>,
|
||||
id_list_parent: WidgetID,
|
||||
on_click: Option<Box<dyn Fn(WvrDisplayHandle)>>,
|
||||
}
|
||||
|
||||
impl View {
|
||||
@@ -84,9 +86,10 @@ impl View {
|
||||
parser_state,
|
||||
tasks,
|
||||
frontend_tasks: params.frontend_tasks,
|
||||
globals: params.globals,
|
||||
globals: params.globals.clone(),
|
||||
state,
|
||||
id_list_parent: list_parent.id,
|
||||
on_click: params.on_click,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,11 +101,11 @@ impl View {
|
||||
}
|
||||
for task in tasks {
|
||||
match task {
|
||||
Task::AddDisplay => self.add_display(),
|
||||
Task::AddDisplayFinish(result) => self.add_display_finish(interface, result)?,
|
||||
Task::DisplayOptionsFinish => self.display_options_finish(),
|
||||
Task::AddDisplay => self.action_add_display(),
|
||||
Task::AddDisplayFinish(result) => self.action_add_display_finish(interface, result)?,
|
||||
Task::DisplayOptionsFinish => self.action_display_options_finish(),
|
||||
Task::Refresh => self.refresh(layout, interface)?,
|
||||
Task::DisplayOptions(display) => self.display_options(display)?,
|
||||
Task::DisplayClicked(display) => self.action_display_clicked(display)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +192,7 @@ fn fill_display_list(
|
||||
button.on_click({
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_, _| {
|
||||
tasks.push(Task::DisplayOptions(entry.clone()));
|
||||
tasks.push(Task::DisplayClicked(entry.clone()));
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
@@ -199,7 +202,7 @@ fn fill_display_list(
|
||||
}
|
||||
|
||||
impl View {
|
||||
fn add_display(&mut self) {
|
||||
fn action_add_display(&mut self) {
|
||||
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
||||
title: Translation::from_translation_key("ADD_DISPLAY"),
|
||||
on_content: {
|
||||
@@ -230,7 +233,7 @@ impl View {
|
||||
}));
|
||||
}
|
||||
|
||||
fn add_display_finish(
|
||||
fn action_add_display_finish(
|
||||
&mut self,
|
||||
interface: &mut BoxDashInterface,
|
||||
result: add_display::Result,
|
||||
@@ -246,7 +249,7 @@ impl View {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_options_finish(&mut self) {
|
||||
fn action_display_options_finish(&mut self) {
|
||||
self.state.borrow_mut().view_display_options = None;
|
||||
self.tasks.push(Task::Refresh);
|
||||
}
|
||||
@@ -291,31 +294,35 @@ impl View {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_options(&mut self, display: packet_server::WvrDisplay) -> anyhow::Result<()> {
|
||||
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
||||
title: Translation::from_translation_key("DISPLAY_OPTIONS"),
|
||||
on_content: {
|
||||
let frontend_tasks = self.frontend_tasks.clone();
|
||||
let globals = self.globals.clone();
|
||||
let state = self.state.clone();
|
||||
let tasks = self.tasks.clone();
|
||||
fn action_display_clicked(&mut self, display: packet_server::WvrDisplay) -> anyhow::Result<()> {
|
||||
if let Some(on_click) = &mut self.on_click {
|
||||
(*on_click)(display.handle);
|
||||
} else {
|
||||
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
||||
title: Translation::from_translation_key("DISPLAY_OPTIONS"),
|
||||
on_content: {
|
||||
let frontend_tasks = self.frontend_tasks.clone();
|
||||
let globals = self.globals.clone();
|
||||
let state = self.state.clone();
|
||||
let tasks = self.tasks.clone();
|
||||
|
||||
Rc::new(move |data| {
|
||||
state.borrow_mut().view_display_options = Some((
|
||||
data.handle,
|
||||
display_options::View::new(display_options::Params {
|
||||
globals: globals.clone(),
|
||||
layout: data.layout,
|
||||
parent_id: data.id_content,
|
||||
on_submit: tasks.make_callback(Task::DisplayOptionsFinish),
|
||||
display: display.clone(),
|
||||
frontend_tasks: frontend_tasks.clone(),
|
||||
})?,
|
||||
));
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
}));
|
||||
Rc::new(move |data| {
|
||||
state.borrow_mut().view_display_options = Some((
|
||||
data.handle,
|
||||
display_options::View::new(display_options::Params {
|
||||
globals: globals.clone(),
|
||||
layout: data.layout,
|
||||
parent_id: data.id_content,
|
||||
on_submit: tasks.make_callback(Task::DisplayOptionsFinish),
|
||||
display: display.clone(),
|
||||
frontend_tasks: frontend_tasks.clone(),
|
||||
})?,
|
||||
));
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ impl View {
|
||||
for task in tasks {
|
||||
match task {
|
||||
Task::Refresh => self.refresh(layout, interface)?,
|
||||
Task::TerminateProcess(process) => self.terminate_process(interface, process)?,
|
||||
Task::TerminateProcess(process) => self.action_terminate_process(interface, process)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ fn get_desktop_file_from_process(
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: refactor this after we ditch wayvr-ipc completely
|
||||
// TODO: refactor this after we ditch old wayvr-dashboard completely
|
||||
let Some(dfile_str) = process.userdata.get("desktop_file") else {
|
||||
continue;
|
||||
};
|
||||
@@ -294,7 +294,7 @@ impl View {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn terminate_process(
|
||||
fn action_terminate_process(
|
||||
&mut self,
|
||||
interface: &mut BoxDashInterface,
|
||||
process: packet_server::WvrProcess,
|
||||
|
||||
Reference in New Issue
Block a user