app autostart
This commit is contained in:
@@ -18,17 +18,16 @@
|
|||||||
<rectangle macro="group_box" id="icon_parent" padding="16" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center">
|
<rectangle macro="group_box" id="icon_parent" padding="16" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center">
|
||||||
|
|
||||||
</rectangle>
|
</rectangle>
|
||||||
<div flex_direction="column" gap="8">
|
<div flex_direction="column" gap="8" min_width="720" max_width="720">
|
||||||
<label id="label_title" weight="bold" size="32" />
|
<label id="label_title" weight="bold" size="32" overflow="hidden" />
|
||||||
<Subtext title="Exec:" label_id="label_exec" />
|
<Subtext label_id="label_exec" overflow="hidden" />
|
||||||
<Subtext title="Args:" label_id="label_args" />
|
|
||||||
<Separator />
|
<Separator />
|
||||||
<RadioGroup id="radio_compositor" flex_direction="row" gap="16">
|
<RadioGroup id="radio_compositor" flex_direction="row" gap="16">
|
||||||
<RadioBox text="Native mode" value="Native" checked="1" />
|
<RadioBox translation="APP_LAUNCHER.MODE.NATIVE" value="Native" checked="1" />
|
||||||
<RadioBox text="Compatibility mode" value="Cage" /> <!-- TODO: tooltips -->
|
<RadioBox translation="APP_LAUNCHER.MODE.CAGE" value="Cage" /> <!-- TODO: tooltips -->
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<Separator />
|
<Separator />
|
||||||
<label text="Resolution" />
|
<label translation="APP_LAUNCHER.RES_TITLE" />
|
||||||
<RadioGroup id="radio_res" flex_direction="row" gap="16">
|
<RadioGroup id="radio_res" flex_direction="row" gap="16">
|
||||||
<RadioBox text="1440p" value="Res1440" />
|
<RadioBox text="1440p" value="Res1440" />
|
||||||
<RadioBox text="1080p" value="Res1080" checked="1" />
|
<RadioBox text="1080p" value="Res1080" checked="1" />
|
||||||
@@ -36,19 +35,30 @@
|
|||||||
<RadioBox text="480p" value="Res480" />
|
<RadioBox text="480p" value="Res480" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<Separator />
|
<Separator />
|
||||||
<label text="Orientation" />
|
<label translation="APP_LAUNCHER.ASPECT_TITLE" />
|
||||||
<RadioGroup id="radio_orientation" flex_direction="row" gap="16">
|
<RadioGroup id="radio_orientation" flex_direction="row" gap="16">
|
||||||
<RadioBox text="Wide" value="Wide" tooltip="16:9" checked="1" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.WIDE" value="Wide" tooltip="16:9" checked="1" />
|
||||||
<RadioBox text="Semi-Wide" value="SemiWide" tooltip="3:2" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.SEMI_WIDE" value="SemiWide" tooltip="3:2" />
|
||||||
<RadioBox text="Square" value="Square" tooltip="1:1" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.SQUARE" value="Square" tooltip="1:1" />
|
||||||
<RadioBox text="Semi-Tall" value="SemiTall" tooltip="2:3" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.SEMI_TALL" value="SemiTall" tooltip="2:3" />
|
||||||
<RadioBox text="Tall" value="Tall" tooltip="9:16" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.TALL" value="Tall" tooltip="9:16" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<Button id="btn_launch" align_self="baseline" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12" min_height="40">
|
<Separator />
|
||||||
<sprite src_builtin="dashboard/play.svg" width="32" height="32" />
|
<label translation="APP_LAUNCHER.POS_TITLE" />
|
||||||
<label text="Launch" weight="bold" size="17" shadow="#00000099" />
|
<RadioGroup id="radio_pos" flex_direction="row" gap="16">
|
||||||
</Button>
|
<RadioBox translation="APP_LAUNCHER.POS.FLOATING" value="Floating" tooltip="APP_LAUNCHER.POS.FLOATING_HELP" />
|
||||||
|
<RadioBox translation="APP_LAUNCHER.POS.ANCHORED" value="Anchored" tooltip="APP_LAUNCHER.POS.ANCHORED_HELP" checked="1" />
|
||||||
|
<RadioBox translation="APP_LAUNCHER.POS.STATIC" value="Static" tooltip="APP_LAUNCHER.POS.STATIC_HELP" />
|
||||||
|
</RadioGroup>
|
||||||
|
<Separator />
|
||||||
|
<div flex_direction="row" justify_content="space_between" gap="16">
|
||||||
|
<CheckBox id="cb_autostart" translation="APP_LAUNCHER.AUTOSTART" />
|
||||||
|
<Button id="btn_launch" align_self="baseline" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12" min_height="40">
|
||||||
|
<sprite src_builtin="dashboard/play.svg" width="32" height="32" />
|
||||||
|
<label translation="APP_LAUNCHER.LAUNCH" weight="bold" size="17" shadow="#00000099" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@@ -83,6 +83,32 @@
|
|||||||
"SCREENCOPY_HELP": "Slow. Works on: Hyprland, Niri, River, Sway"
|
"SCREENCOPY_HELP": "Slow. Works on: Hyprland, Niri, River, Sway"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"APP_LAUNCHER": {
|
||||||
|
"MODE": {
|
||||||
|
"NATIVE": "Native mode",
|
||||||
|
"CAGE": "Compatibility mode (Cage)"
|
||||||
|
},
|
||||||
|
"RES_TITLE": "Resolution",
|
||||||
|
"ASPECT_TITLE": "Aspect",
|
||||||
|
"ASPECT": {
|
||||||
|
"WIDE": "Wide",
|
||||||
|
"SEMI_WIDE": "Semi-wide",
|
||||||
|
"SQUARE": "Square",
|
||||||
|
"SEMI_TALL": "Semi-tall",
|
||||||
|
"TALL": "Tall"
|
||||||
|
},
|
||||||
|
"POS_TITLE": "Positioning",
|
||||||
|
"POS": {
|
||||||
|
"FLOATING": "Floating",
|
||||||
|
"ANCHORED": "Anchored",
|
||||||
|
"STATIC": "Static",
|
||||||
|
"FLOATING_HELP": "Moves independently, recenters on show.",
|
||||||
|
"ANCHORED_HELP": "Stays in place relative to center marker.",
|
||||||
|
"STATIC_HELP": "Not part of any set. Does not recenter."
|
||||||
|
},
|
||||||
|
"AUTOSTART": "Run automatically on startup",
|
||||||
|
"LAUNCH": "Launch"
|
||||||
|
},
|
||||||
"APPLICATION_LAUNCHER": "Application launcher",
|
"APPLICATION_LAUNCHER": "Application launcher",
|
||||||
"APPLICATION_STARTED": "Application started",
|
"APPLICATION_STARTED": "Application started",
|
||||||
"APPLICATIONS": "Applications",
|
"APPLICATIONS": "Applications",
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::{collections::HashMap, rc::Rc, str::FromStr};
|
use std::{collections::HashMap, rc::Rc, str::FromStr};
|
||||||
|
|
||||||
use strum::{AsRefStr, EnumString, VariantNames};
|
use strum::{AsRefStr, EnumString, VariantNames};
|
||||||
use wayvr_ipc::packet_client::WvrProcessLaunchParams;
|
use wayvr_ipc::packet_client::{PositionMode, WvrProcessLaunchParams};
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::{button::ComponentButton, radio_group::ComponentRadioGroup},
|
components::{button::ComponentButton, checkbox::ComponentCheckbox, radio_group::ComponentRadioGroup},
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, WidgetID},
|
layout::{Layout, WidgetID},
|
||||||
@@ -16,6 +16,13 @@ use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, deskto
|
|||||||
|
|
||||||
use crate::frontend::{FrontendTask, FrontendTasks, SoundType};
|
use crate::frontend::{FrontendTask, FrontendTasks, SoundType};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
|
enum PosMode {
|
||||||
|
Floating,
|
||||||
|
Anchored,
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
enum ResMode {
|
enum ResMode {
|
||||||
Res1440,
|
Res1440,
|
||||||
@@ -43,18 +50,22 @@ enum CompositorMode {
|
|||||||
enum Task {
|
enum Task {
|
||||||
SetCompositor(CompositorMode),
|
SetCompositor(CompositorMode),
|
||||||
SetRes(ResMode),
|
SetRes(ResMode),
|
||||||
|
SetPos(PosMode),
|
||||||
SetOrientation(OrientationMode),
|
SetOrientation(OrientationMode),
|
||||||
|
SetAutoStart(bool),
|
||||||
Launch,
|
Launch,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LaunchParams<'a, T> {
|
struct LaunchParams<'a, T> {
|
||||||
application: &'a DesktopEntry,
|
application: &'a DesktopEntry,
|
||||||
compositor_mode: CompositorMode,
|
compositor_mode: CompositorMode,
|
||||||
|
pos_mode: PosMode,
|
||||||
res_mode: ResMode,
|
res_mode: ResMode,
|
||||||
orientation_mode: OrientationMode,
|
orientation_mode: OrientationMode,
|
||||||
globals: &'a WguiGlobals,
|
globals: &'a WguiGlobals,
|
||||||
frontend_tasks: &'a FrontendTasks,
|
frontend_tasks: &'a FrontendTasks,
|
||||||
interface: &'a mut BoxDashInterface<T>,
|
interface: &'a mut BoxDashInterface<T>,
|
||||||
|
auto_start: bool,
|
||||||
data: &'a mut T,
|
data: &'a mut T,
|
||||||
on_launched: &'a dyn Fn(),
|
on_launched: &'a dyn Fn(),
|
||||||
}
|
}
|
||||||
@@ -75,9 +86,12 @@ pub struct View {
|
|||||||
radio_orientation: Rc<ComponentRadioGroup>,
|
radio_orientation: Rc<ComponentRadioGroup>,
|
||||||
|
|
||||||
compositor_mode: CompositorMode,
|
compositor_mode: CompositorMode,
|
||||||
|
pos_mode: PosMode,
|
||||||
res_mode: ResMode,
|
res_mode: ResMode,
|
||||||
orientation_mode: OrientationMode,
|
orientation_mode: OrientationMode,
|
||||||
|
|
||||||
|
auto_start: bool,
|
||||||
|
|
||||||
on_launched: Box<dyn Fn()>,
|
on_launched: Box<dyn Fn()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,22 +117,18 @@ impl View {
|
|||||||
|
|
||||||
let radio_compositor = state.fetch_component_as::<ComponentRadioGroup>("radio_compositor")?;
|
let radio_compositor = state.fetch_component_as::<ComponentRadioGroup>("radio_compositor")?;
|
||||||
let radio_res = state.fetch_component_as::<ComponentRadioGroup>("radio_res")?;
|
let radio_res = state.fetch_component_as::<ComponentRadioGroup>("radio_res")?;
|
||||||
|
let radio_pos = state.fetch_component_as::<ComponentRadioGroup>("radio_pos")?;
|
||||||
let radio_orientation = state.fetch_component_as::<ComponentRadioGroup>("radio_orientation")?;
|
let radio_orientation = state.fetch_component_as::<ComponentRadioGroup>("radio_orientation")?;
|
||||||
|
let cb_autostart = state.fetch_component_as::<ComponentCheckbox>("cb_autostart")?;
|
||||||
|
|
||||||
let btn_launch = state.fetch_component_as::<ComponentButton>("btn_launch")?;
|
let btn_launch = state.fetch_component_as::<ComponentButton>("btn_launch")?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut label_exec = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_exec")?;
|
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(
|
label_exec.set_text_simple(
|
||||||
&mut params.globals.get(),
|
&mut params.globals.get(),
|
||||||
Translation::from_raw_text_rc(params.entry.exec_path.clone()),
|
Translation::from_raw_text_string(format!("{} {}", params.entry.exec_path, params.entry.exec_args)),
|
||||||
);
|
|
||||||
|
|
||||||
label_args.set_text_simple(
|
|
||||||
&mut params.globals.get(),
|
|
||||||
Translation::from_raw_text_rc(params.entry.exec_args.clone()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +169,13 @@ impl View {
|
|||||||
//radio_orientation.set_value(orientation_mode.as_ref())?;
|
//radio_orientation.set_value(orientation_mode.as_ref())?;
|
||||||
//tasks.push(Task::SetOrientation(orientation_mode));
|
//tasks.push(Task::SetOrientation(orientation_mode));
|
||||||
|
|
||||||
|
let pos_mode = PosMode::Floating;
|
||||||
|
// TODO: configurable defaults ?
|
||||||
|
//radio_pos.set_value(pos_mode.as_ref())?;
|
||||||
|
//tasks.push(Task::SetPos(pos_mode));
|
||||||
|
|
||||||
|
let auto_start = false;
|
||||||
|
|
||||||
radio_compositor.on_value_changed({
|
radio_compositor.on_value_changed({
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
Box::new(move |_, ev| {
|
Box::new(move |_, ev| {
|
||||||
@@ -197,6 +214,25 @@ impl View {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
radio_pos.on_value_changed({
|
||||||
|
let tasks = tasks.clone();
|
||||||
|
Box::new(move |_, ev| {
|
||||||
|
if let Some(mode) = ev.value.and_then(|v| {
|
||||||
|
PosMode::from_str(&*v)
|
||||||
|
.inspect_err(|_| {
|
||||||
|
log::error!(
|
||||||
|
"Invalid value for position: '{v}'. Valid values are: {:?}",
|
||||||
|
PosMode::VARIANTS
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
}) {
|
||||||
|
tasks.push(Task::SetPos(mode));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
radio_orientation.on_value_changed({
|
radio_orientation.on_value_changed({
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
Box::new(move |_, ev| {
|
Box::new(move |_, ev| {
|
||||||
@@ -216,6 +252,14 @@ impl View {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cb_autostart.on_toggle({
|
||||||
|
let tasks = tasks.clone();
|
||||||
|
Box::new(move |_, ev| {
|
||||||
|
tasks.push(Task::SetAutoStart(ev.checked));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let mut label_title = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_title")?;
|
let mut label_title = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_title")?;
|
||||||
|
|
||||||
label_title.set_text_simple(
|
label_title.set_text_simple(
|
||||||
@@ -230,8 +274,10 @@ impl View {
|
|||||||
radio_res,
|
radio_res,
|
||||||
radio_orientation,
|
radio_orientation,
|
||||||
compositor_mode,
|
compositor_mode,
|
||||||
|
pos_mode,
|
||||||
res_mode,
|
res_mode,
|
||||||
orientation_mode,
|
orientation_mode,
|
||||||
|
auto_start,
|
||||||
entry: params.entry,
|
entry: params.entry,
|
||||||
frontend_tasks: params.frontend_tasks.clone(),
|
frontend_tasks: params.frontend_tasks.clone(),
|
||||||
globals: params.globals.clone(),
|
globals: params.globals.clone(),
|
||||||
@@ -249,7 +295,9 @@ impl View {
|
|||||||
match task {
|
match task {
|
||||||
Task::SetCompositor(mode) => self.compositor_mode = mode,
|
Task::SetCompositor(mode) => self.compositor_mode = mode,
|
||||||
Task::SetRes(mode) => self.res_mode = mode,
|
Task::SetRes(mode) => self.res_mode = mode,
|
||||||
|
Task::SetPos(mode) => self.pos_mode = mode,
|
||||||
Task::SetOrientation(mode) => self.orientation_mode = mode,
|
Task::SetOrientation(mode) => self.orientation_mode = mode,
|
||||||
|
Task::SetAutoStart(auto_start) => self.auto_start = auto_start,
|
||||||
Task::Launch => self.action_launch(interface, data),
|
Task::Launch => self.action_launch(interface, data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,7 +313,9 @@ impl View {
|
|||||||
globals: &self.globals,
|
globals: &self.globals,
|
||||||
compositor_mode: self.compositor_mode,
|
compositor_mode: self.compositor_mode,
|
||||||
res_mode: self.res_mode,
|
res_mode: self.res_mode,
|
||||||
|
pos_mode: self.pos_mode,
|
||||||
orientation_mode: self.orientation_mode,
|
orientation_mode: self.orientation_mode,
|
||||||
|
auto_start: self.auto_start,
|
||||||
interface,
|
interface,
|
||||||
data,
|
data,
|
||||||
on_launched: &self.on_launched,
|
on_launched: &self.on_launched,
|
||||||
@@ -308,6 +358,12 @@ impl View {
|
|||||||
CompositorMode::Native => params.application.exec_path.to_string(),
|
CompositorMode::Native => params.application.exec_path.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pos_mode = match params.pos_mode {
|
||||||
|
PosMode::Floating => PositionMode::Anchor,
|
||||||
|
PosMode::Anchored => PositionMode::Anchor,
|
||||||
|
PosMode::Static => PositionMode::Static,
|
||||||
|
};
|
||||||
|
|
||||||
let mut userdata = HashMap::new();
|
let mut userdata = HashMap::new();
|
||||||
userdata.insert("desktop-entry".to_string(), serde_json::to_string(params.application)?);
|
userdata.insert("desktop-entry".to_string(), serde_json::to_string(params.application)?);
|
||||||
|
|
||||||
@@ -315,12 +371,14 @@ impl View {
|
|||||||
|
|
||||||
params.interface.process_launch(
|
params.interface.process_launch(
|
||||||
params.data,
|
params.data,
|
||||||
|
params.auto_start,
|
||||||
WvrProcessLaunchParams {
|
WvrProcessLaunchParams {
|
||||||
env,
|
env,
|
||||||
exec,
|
exec,
|
||||||
name: params.application.app_name.to_string(),
|
name: params.application.app_name.to_string(),
|
||||||
args,
|
args,
|
||||||
resolution,
|
resolution,
|
||||||
|
pos_mode,
|
||||||
icon: params.application.icon_path.as_ref().map(|x| x.as_ref().to_string()),
|
icon: params.application.icon_path.as_ref().map(|x| x.as_ref().to_string()),
|
||||||
userdata,
|
userdata,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,15 +14,13 @@ pub struct Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub enum AttachTo {
|
pub enum PositionMode {
|
||||||
None,
|
Float,
|
||||||
HandLeft,
|
Anchor,
|
||||||
HandRight,
|
Static,
|
||||||
Head,
|
|
||||||
Stage,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct WvrProcessLaunchParams {
|
pub struct WvrProcessLaunchParams {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub exec: String,
|
pub exec: String,
|
||||||
@@ -30,6 +28,7 @@ pub struct WvrProcessLaunchParams {
|
|||||||
pub args: String,
|
pub args: String,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub resolution: [u32; 2],
|
pub resolution: [u32; 2],
|
||||||
|
pub pos_mode: PositionMode,
|
||||||
pub userdata: HashMap<String, String>,
|
pub userdata: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ use anyhow::Context;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use wayvr_ipc::{
|
use wayvr_ipc::{
|
||||||
client::{WayVRClient, WayVRClientMutex},
|
client::{WayVRClient, WayVRClientMutex},
|
||||||
ipc, packet_client, packet_server,
|
ipc,
|
||||||
|
packet_client::{self, PositionMode},
|
||||||
|
packet_server,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct WayVRClientState {
|
pub struct WayVRClientState {
|
||||||
@@ -106,6 +108,7 @@ pub async fn wvr_process_launch(
|
|||||||
name: String,
|
name: String,
|
||||||
env: Vec<String>,
|
env: Vec<String>,
|
||||||
resolution: [u32; 2],
|
resolution: [u32; 2],
|
||||||
|
pos_mode: PositionMode,
|
||||||
icon: Option<String>,
|
icon: Option<String>,
|
||||||
args: String,
|
args: String,
|
||||||
userdata: HashMap<String, String>,
|
userdata: HashMap<String, String>,
|
||||||
@@ -121,6 +124,7 @@ pub async fn wvr_process_launch(
|
|||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
resolution,
|
resolution,
|
||||||
|
pos_mode,
|
||||||
icon,
|
icon,
|
||||||
userdata,
|
userdata,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ use std::{
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use wayvr_ipc::{client::WayVRClient, ipc, packet_client};
|
use wayvr_ipc::{
|
||||||
|
client::WayVRClient,
|
||||||
|
ipc,
|
||||||
|
packet_client::{self, PositionMode},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::helper::{
|
use crate::helper::{
|
||||||
WayVRClientState, wlx_device_haptics, wlx_input_state, wlx_panel_modify, wvr_process_get,
|
WayVRClientState, wlx_device_haptics, wlx_input_state, wlx_panel_modify, wvr_process_get,
|
||||||
@@ -120,6 +124,7 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
|
|||||||
name,
|
name,
|
||||||
env,
|
env,
|
||||||
resolution,
|
resolution,
|
||||||
|
pos,
|
||||||
icon,
|
icon,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
@@ -131,12 +136,19 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
|
|||||||
"Invalid resolution format. Expecting <width>x<height>, for example: 1920x1080, 1280x720",
|
"Invalid resolution format. Expecting <width>x<height>, for example: 1920x1080, 1280x720",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let pos_mode = match pos {
|
||||||
|
PosModeEnum::Floating => PositionMode::Float,
|
||||||
|
PosModeEnum::Static => PositionMode::Static,
|
||||||
|
PosModeEnum::Anchored => PositionMode::Anchor,
|
||||||
|
};
|
||||||
|
|
||||||
wvr_process_launch(
|
wvr_process_launch(
|
||||||
state,
|
state,
|
||||||
exec,
|
exec,
|
||||||
name,
|
name,
|
||||||
env,
|
env,
|
||||||
resolution,
|
resolution,
|
||||||
|
pos_mode,
|
||||||
icon,
|
icon,
|
||||||
args,
|
args,
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
@@ -237,6 +249,9 @@ enum Subcommands {
|
|||||||
exec: String,
|
exec: String,
|
||||||
#[arg(default_value = "1920x1080")]
|
#[arg(default_value = "1920x1080")]
|
||||||
resolution: String,
|
resolution: String,
|
||||||
|
/// Default positioning
|
||||||
|
pos: PosModeEnum,
|
||||||
|
/// Absolute path to the app icon
|
||||||
icon: Option<String>,
|
icon: Option<String>,
|
||||||
/// Arguments to pass to executable
|
/// Arguments to pass to executable
|
||||||
#[arg(default_value = "")]
|
#[arg(default_value = "")]
|
||||||
@@ -265,6 +280,13 @@ enum Subcommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||||
|
enum PosModeEnum {
|
||||||
|
Floating,
|
||||||
|
Anchored,
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(clap::Parser, Debug)]
|
#[derive(clap::Parser, Debug)]
|
||||||
enum SubcommandPanelModify {
|
enum SubcommandPanelModify {
|
||||||
/// Set the text of a <label> or <Button>
|
/// Set the text of a <label> or <Button>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use chrono::Offset;
|
|||||||
use idmap::IdMap;
|
use idmap::IdMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
|
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
|
||||||
|
use wayvr_ipc::packet_client::WvrProcessLaunchParams;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
astr_containers::{AStrMap, AStrSet},
|
astr_containers::{AStrMap, AStrSet},
|
||||||
@@ -267,6 +268,9 @@ pub struct GeneralConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub global_set: SerializedWindowStates,
|
pub global_set: SerializedWindowStates,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub autostart_apps: Vec<WvrProcessLaunchParams>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub last_set: u32,
|
pub last_set: u32,
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ pub trait DashInterface<T> {
|
|||||||
fn window_set_visible(&mut self, data: &mut T, handle: WvrWindowHandle, visible: bool) -> anyhow::Result<()>;
|
fn window_set_visible(&mut self, data: &mut T, handle: WvrWindowHandle, visible: bool) -> anyhow::Result<()>;
|
||||||
fn window_request_close(&mut self, data: &mut T, handle: WvrWindowHandle) -> anyhow::Result<()>;
|
fn window_request_close(&mut self, data: &mut T, handle: WvrWindowHandle) -> anyhow::Result<()>;
|
||||||
fn process_get(&mut self, data: &mut T, handle: WvrProcessHandle) -> Option<WvrProcess>;
|
fn process_get(&mut self, data: &mut T, handle: WvrProcessHandle) -> Option<WvrProcess>;
|
||||||
fn process_launch(&mut self, data: &mut T, params: WvrProcessLaunchParams) -> anyhow::Result<WvrProcessHandle>;
|
fn process_launch(
|
||||||
|
&mut self,
|
||||||
|
data: &mut T,
|
||||||
|
auto_start: bool,
|
||||||
|
params: WvrProcessLaunchParams,
|
||||||
|
) -> anyhow::Result<WvrProcessHandle>;
|
||||||
fn process_list(&mut self, data: &mut T) -> anyhow::Result<Vec<WvrProcess>>;
|
fn process_list(&mut self, data: &mut T) -> anyhow::Result<Vec<WvrProcess>>;
|
||||||
fn process_terminate(&mut self, data: &mut T, handle: WvrProcessHandle) -> anyhow::Result<()>;
|
fn process_terminate(&mut self, data: &mut T, handle: WvrProcessHandle) -> anyhow::Result<()>;
|
||||||
fn recenter_playspace(&mut self, data: &mut T) -> anyhow::Result<()>;
|
fn recenter_playspace(&mut self, data: &mut T) -> anyhow::Result<()>;
|
||||||
|
|||||||
@@ -110,7 +110,12 @@ impl DashInterface<()> for DashInterfaceEmulated {
|
|||||||
self.processes.get(&emu_handle).map(|process| process.to(emu_handle))
|
self.processes.get(&emu_handle).map(|process| process.to(emu_handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_launch(&mut self, _: &mut (), params: WvrProcessLaunchParams) -> anyhow::Result<WvrProcessHandle> {
|
fn process_launch(
|
||||||
|
&mut self,
|
||||||
|
_: &mut (),
|
||||||
|
_: bool,
|
||||||
|
params: WvrProcessLaunchParams,
|
||||||
|
) -> anyhow::Result<WvrProcessHandle> {
|
||||||
let res = self.processes.add(EmuProcess { name: params.name });
|
let res = self.processes.add(EmuProcess { name: params.name });
|
||||||
|
|
||||||
self.windows.add(EmuWindow {
|
self.windows.add(EmuWindow {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
round="8"
|
round="8"
|
||||||
align_items="center"
|
align_items="center"
|
||||||
justify_content="space_between"
|
justify_content="space_between"
|
||||||
color="~color_background"
|
color="~color_bg"
|
||||||
border_color="~color_accent"
|
border_color="~color_accent"
|
||||||
>
|
>
|
||||||
<label id="label_title" margin_left="8" color="~color_text" size="22" weight="bold" />
|
<label id="label_title" margin_left="8" color="~color_text" size="22" weight="bold" />
|
||||||
@@ -29,4 +29,4 @@
|
|||||||
</rectangle>
|
</rectangle>
|
||||||
</div>
|
</div>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
"LOCK_INTERACTION": "Lock interaction",
|
"LOCK_INTERACTION": "Lock interaction",
|
||||||
"MOVE_PRESS_AND_DRAG": "Move (press & drag)",
|
"MOVE_PRESS_AND_DRAG": "Move (press & drag)",
|
||||||
"OPACITY": "Opacity",
|
"OPACITY": "Opacity",
|
||||||
"POS_ANCHORED": "Anchored: Moves together with the rest of the set. Default.",
|
"POS_ANCHORED": "Anchored: Moves with center marker. Default.",
|
||||||
"POS_FLOATING": "Floating: Stay in place, recenter when shown.",
|
"POS_FLOATING": "Floating: Moves independently, recenters when shown.",
|
||||||
"POS_HAND_L": "Follow the left hand.",
|
"POS_HAND_L": "Follow the left hand.",
|
||||||
"POS_HAND_R": "Follow the right hand.",
|
"POS_HAND_R": "Follow the right hand.",
|
||||||
"POS_HMD": "Follow the HMD.",
|
"POS_HMD": "Follow the HMD.",
|
||||||
"POS_STATIC": "Static: Stay in place and never recenter.",
|
"POS_STATIC": "Static: Not part of any set, no recenter.",
|
||||||
"POSITIONING": "Positioning",
|
"POSITIONING": "Positioning",
|
||||||
"RESIZE_PRESS_AND_DRAG": "Resize (press & drag)",
|
"RESIZE_PRESS_AND_DRAG": "Resize (press & drag)",
|
||||||
"STEREO_3D_MODE": {
|
"STEREO_3D_MODE": {
|
||||||
|
|||||||
@@ -106,20 +106,11 @@ impl BlitMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
pub click_freeze_time_ms: i32,
|
|
||||||
pub keyboard_repeat_delay_ms: u32,
|
|
||||||
pub keyboard_repeat_rate: u32,
|
|
||||||
pub auto_hide_delay: Option<u32>, // if None, auto-hide is disabled
|
|
||||||
pub blit_method: BlitMethod,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WvrServerState {
|
pub struct WvrServerState {
|
||||||
time_start: u64,
|
time_start: u64,
|
||||||
pub manager: client::WayVRCompositor,
|
pub manager: client::WayVRCompositor,
|
||||||
pub wm: window::WindowManager,
|
pub wm: window::WindowManager,
|
||||||
pub processes: process::ProcessVec,
|
pub processes: process::ProcessVec,
|
||||||
pub config: Config,
|
|
||||||
pub tasks: SyncEventQueue<WayVRTask>,
|
pub tasks: SyncEventQueue<WayVRTask>,
|
||||||
ticks: u64,
|
ticks: u64,
|
||||||
cur_modifiers: u8,
|
cur_modifiers: u8,
|
||||||
@@ -139,11 +130,13 @@ pub enum TickTask {
|
|||||||
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
|
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KEY_REPEAT_DELAY: i32 = 200;
|
||||||
|
const KEY_REPEAT_RATE: i32 = 50;
|
||||||
|
|
||||||
impl WvrServerState {
|
impl WvrServerState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
gfx: Arc<WGfx>,
|
gfx: Arc<WGfx>,
|
||||||
gfx_extras: &WGfxExtras,
|
gfx_extras: &WGfxExtras,
|
||||||
config: Config,
|
|
||||||
signals: SyncEventQueue<WayVRSignal>,
|
signals: SyncEventQueue<WayVRSignal>,
|
||||||
) -> anyhow::Result<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
log::info!("Initializing WayVR server");
|
log::info!("Initializing WayVR server");
|
||||||
@@ -210,11 +203,8 @@ impl WvrServerState {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let seat_keyboard = seat.add_keyboard(
|
let seat_keyboard =
|
||||||
XkbConfig::default(),
|
seat.add_keyboard(XkbConfig::default(), KEY_REPEAT_DELAY, KEY_REPEAT_RATE)?;
|
||||||
config.keyboard_repeat_delay_ms as i32,
|
|
||||||
config.keyboard_repeat_rate as i32,
|
|
||||||
)?;
|
|
||||||
let seat_pointer = seat.add_pointer();
|
let seat_pointer = seat.add_pointer();
|
||||||
|
|
||||||
let tasks = SyncEventQueue::new();
|
let tasks = SyncEventQueue::new();
|
||||||
@@ -241,7 +231,6 @@ impl WvrServerState {
|
|||||||
manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?,
|
manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?,
|
||||||
processes: ProcessVec::new(),
|
processes: ProcessVec::new(),
|
||||||
wm: window::WindowManager::new(),
|
wm: window::WindowManager::new(),
|
||||||
config,
|
|
||||||
ticks: 0,
|
ticks: 0,
|
||||||
tasks,
|
tasks,
|
||||||
cur_modifiers: 0,
|
cur_modifiers: 0,
|
||||||
@@ -507,9 +496,13 @@ impl WvrServerState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_mouse_down(&mut self, handle: window::WindowHandle, index: MouseIndex) {
|
pub fn send_mouse_down(
|
||||||
self.mouse_freeze =
|
&mut self,
|
||||||
Instant::now() + Duration::from_millis(self.config.click_freeze_time_ms as _);
|
click_freeze: i32,
|
||||||
|
handle: window::WindowHandle,
|
||||||
|
index: MouseIndex,
|
||||||
|
) {
|
||||||
|
self.mouse_freeze = Instant::now() + Duration::from_millis(click_freeze as _);
|
||||||
|
|
||||||
if let Some(window) = self.wm.windows.get_mut(&handle) {
|
if let Some(window) = self.wm.windows.get_mut(&handle) {
|
||||||
window.send_mouse_down(&mut self.manager, index);
|
window.send_mouse_down(&mut self.manager, index);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use config::{Config, File};
|
|||||||
use log::error;
|
use log::error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use wayvr_ipc::packet_client::WvrProcessLaunchParams;
|
||||||
use wlx_common::{
|
use wlx_common::{
|
||||||
astr_containers::AStrMap,
|
astr_containers::AStrMap,
|
||||||
config::{
|
config::{
|
||||||
@@ -142,6 +143,7 @@ pub struct AutoSettings {
|
|||||||
pub context_menu_hold_and_release: bool,
|
pub context_menu_hold_and_release: bool,
|
||||||
pub capture_method: CaptureMethod,
|
pub capture_method: CaptureMethod,
|
||||||
pub keyboard_middle_click_mode: AltModifier,
|
pub keyboard_middle_click_mode: AltModifier,
|
||||||
|
pub autostart_apps: Vec<WvrProcessLaunchParams>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_settings_path() -> PathBuf {
|
fn get_settings_path() -> PathBuf {
|
||||||
@@ -187,6 +189,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
|
|||||||
context_menu_hold_and_release: config.context_menu_hold_and_release,
|
context_menu_hold_and_release: config.context_menu_hold_and_release,
|
||||||
capture_method: config.capture_method,
|
capture_method: config.capture_method,
|
||||||
keyboard_middle_click_mode: config.keyboard_middle_click_mode,
|
keyboard_middle_click_mode: config.keyboard_middle_click_mode,
|
||||||
|
autostart_apps: config.autostart_apps.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic
|
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic
|
||||||
|
|||||||
@@ -1,286 +0,0 @@
|
|||||||
#[cfg(not(feature = "wayvr"))]
|
|
||||||
compile_error!("WayVR feature is not enabled");
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use wgui::gfx::WGfx;
|
|
||||||
use wlx_common::{common::LeftRight, config::GeneralConfig, config_io, windowing::Positioning};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
backend::{
|
|
||||||
task::TaskContainer,
|
|
||||||
wayvr::{self, WvrServerState},
|
|
||||||
},
|
|
||||||
config::load_config_with_conf_d,
|
|
||||||
graphics::WGfxExtras,
|
|
||||||
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Flat version of RelativeTo
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub enum AttachTo {
|
|
||||||
None,
|
|
||||||
HandLeft,
|
|
||||||
HandRight,
|
|
||||||
Head,
|
|
||||||
Stage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AttachTo {
|
|
||||||
// TODO: adjustable lerp factor
|
|
||||||
pub const fn get_positioning(&self) -> Positioning {
|
|
||||||
match self {
|
|
||||||
Self::None => Positioning::Floating,
|
|
||||||
Self::HandLeft => Positioning::FollowHand {
|
|
||||||
hand: LeftRight::Left,
|
|
||||||
lerp: 1.0,
|
|
||||||
align_to_hmd: false,
|
|
||||||
},
|
|
||||||
Self::HandRight => Positioning::FollowHand {
|
|
||||||
hand: LeftRight::Right,
|
|
||||||
lerp: 1.0,
|
|
||||||
align_to_hmd: false,
|
|
||||||
},
|
|
||||||
Self::Stage => Positioning::Static,
|
|
||||||
Self::Head => Positioning::FollowHead { lerp: 1.0 },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn from_packet(input: &wayvr_ipc::packet_client::AttachTo) -> Self {
|
|
||||||
match input {
|
|
||||||
wayvr_ipc::packet_client::AttachTo::None => Self::None,
|
|
||||||
wayvr_ipc::packet_client::AttachTo::HandLeft => Self::HandLeft,
|
|
||||||
wayvr_ipc::packet_client::AttachTo::HandRight => Self::HandRight,
|
|
||||||
wayvr_ipc::packet_client::AttachTo::Head => Self::Head,
|
|
||||||
wayvr_ipc::packet_client::AttachTo::Stage => Self::Stage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Rotation {
|
|
||||||
pub axis: [f32; 3],
|
|
||||||
pub angle: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct WayVRAppEntry {
|
|
||||||
pub name: String,
|
|
||||||
pub target_display: String,
|
|
||||||
pub exec: String,
|
|
||||||
pub args: Option<String>,
|
|
||||||
pub env: Option<Vec<String>>,
|
|
||||||
pub shown_at_start: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct WayVRDisplay {
|
|
||||||
pub width: u16,
|
|
||||||
pub height: u16,
|
|
||||||
pub scale: Option<f32>,
|
|
||||||
pub rotation: Option<Rotation>,
|
|
||||||
pub pos: Option<[f32; 3]>,
|
|
||||||
pub attach_to: Option<AttachTo>,
|
|
||||||
pub primary: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct WayVRCatalog {
|
|
||||||
pub apps: Vec<WayVRAppEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WayVRCatalog {
|
|
||||||
pub fn get_app(&self, name: &str) -> Option<&WayVRAppEntry> {
|
|
||||||
self.apps.iter().find(|&app| app.name.as_str() == name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn def_false() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn def_true() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn def_autohide_delay() -> u32 {
|
|
||||||
750
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn def_keyboard_repeat_delay() -> u32 {
|
|
||||||
200
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn def_keyboard_repeat_rate() -> u32 {
|
|
||||||
50
|
|
||||||
}
|
|
||||||
|
|
||||||
fn def_blit_method() -> String {
|
|
||||||
String::from("dmabuf")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct WayVRDashboard {
|
|
||||||
pub exec: String,
|
|
||||||
pub working_dir: Option<String>,
|
|
||||||
pub args: Option<String>,
|
|
||||||
pub env: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct WayVRConfig {
|
|
||||||
#[serde(default = "Default::default")]
|
|
||||||
pub catalogs: HashMap<String, WayVRCatalog>,
|
|
||||||
|
|
||||||
#[serde(default = "Default::default")]
|
|
||||||
pub displays: BTreeMap<String, WayVRDisplay>, // sorted alphabetically
|
|
||||||
|
|
||||||
#[serde(default = "Default::default")]
|
|
||||||
pub dashboard: Option<WayVRDashboard>,
|
|
||||||
|
|
||||||
#[serde(default = "def_true")]
|
|
||||||
pub auto_hide: bool,
|
|
||||||
|
|
||||||
#[serde(default = "def_autohide_delay")]
|
|
||||||
pub auto_hide_delay: u32,
|
|
||||||
|
|
||||||
#[serde(default = "def_keyboard_repeat_delay")]
|
|
||||||
pub keyboard_repeat_delay: u32,
|
|
||||||
|
|
||||||
#[serde(default = "def_keyboard_repeat_rate")]
|
|
||||||
pub keyboard_repeat_rate: u32,
|
|
||||||
|
|
||||||
#[serde(default = "def_blit_method")]
|
|
||||||
pub blit_method: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WayVRConfig {
|
|
||||||
pub fn get_catalog(&self, name: &str) -> Option<&WayVRCatalog> {
|
|
||||||
self.catalogs.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_display(&self, name: &str) -> Option<&WayVRDisplay> {
|
|
||||||
self.displays.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_default_display(&self) -> Option<(String, &WayVRDisplay)> {
|
|
||||||
for (disp_name, disp) in &self.displays {
|
|
||||||
if disp.primary.unwrap_or(false) {
|
|
||||||
return Some((disp_name.clone(), disp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_wayvr_config(
|
|
||||||
config_general: &GeneralConfig,
|
|
||||||
config_wayvr: &Self,
|
|
||||||
) -> anyhow::Result<wayvr::Config> {
|
|
||||||
Ok(wayvr::Config {
|
|
||||||
click_freeze_time_ms: config_general.click_freeze_time_ms,
|
|
||||||
keyboard_repeat_delay_ms: config_wayvr.keyboard_repeat_delay,
|
|
||||||
keyboard_repeat_rate: config_wayvr.keyboard_repeat_rate,
|
|
||||||
blit_method: wayvr::BlitMethod::from_string(&config_wayvr.blit_method)
|
|
||||||
.context("unknown blit method")?,
|
|
||||||
auto_hide_delay: if config_wayvr.auto_hide {
|
|
||||||
Some(config_wayvr.auto_hide_delay)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn post_load(
|
|
||||||
&self,
|
|
||||||
gfx: Arc<WGfx>,
|
|
||||||
gfx_extras: &WGfxExtras,
|
|
||||||
config: &GeneralConfig,
|
|
||||||
_tasks: &mut TaskContainer,
|
|
||||||
signals: SyncEventQueue<WayVRSignal>,
|
|
||||||
) -> anyhow::Result<WvrServerState> {
|
|
||||||
let primary_count = self
|
|
||||||
.displays
|
|
||||||
.iter()
|
|
||||||
.filter(|d| d.1.primary.unwrap_or(false))
|
|
||||||
.count();
|
|
||||||
|
|
||||||
if primary_count > 1 {
|
|
||||||
anyhow::bail!("Number of primary displays is more than 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (_catalog_name, catalog) in &self.catalogs {
|
|
||||||
for app in &catalog.apps {
|
|
||||||
if let Some(b) = app.shown_at_start
|
|
||||||
&& b
|
|
||||||
{
|
|
||||||
//CLEANUP: is this needed?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WvrServerState::new(
|
|
||||||
gfx,
|
|
||||||
gfx_extras,
|
|
||||||
Self::get_wayvr_config(config, self)?,
|
|
||||||
signals,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_default_dashboard_exec() -> (
|
|
||||||
String, /* exec path */
|
|
||||||
Option<String>, /* working directory */
|
|
||||||
) {
|
|
||||||
if let Ok(appdir) = std::env::var("APPDIR") {
|
|
||||||
// Running in AppImage
|
|
||||||
let embedded_path = format!("{appdir}/usr/bin/wayvr-dashboard");
|
|
||||||
if executable_exists_in_path(&embedded_path) {
|
|
||||||
log::info!("Using WayVR Dashboard from AppDir: {embedded_path}");
|
|
||||||
return (embedded_path, Some(format!("{appdir}/usr")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(String::from("wayvr-dashboard"), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn executable_exists_in_path(command: &str) -> bool {
|
|
||||||
let Ok(path) = std::env::var("PATH") else {
|
|
||||||
return false; // very unlikely to happen
|
|
||||||
};
|
|
||||||
for dir in path.split(':') {
|
|
||||||
let exec_path = std::path::PathBuf::from(dir).join(command);
|
|
||||||
if exec_path.exists() && exec_path.is_file() {
|
|
||||||
return true; // executable found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_wayvr() -> WayVRConfig {
|
|
||||||
let config_root_path = config_io::ConfigRoot::WayVR.ensure_dir();
|
|
||||||
log::info!("WayVR Config root path: {}", config_root_path.display());
|
|
||||||
log::info!(
|
|
||||||
"WayVR conf.d path: {}",
|
|
||||||
config_io::ConfigRoot::WayVR.get_conf_d_path().display()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut conf =
|
|
||||||
load_config_with_conf_d::<WayVRConfig>("wayvr.yaml", config_io::ConfigRoot::WayVR);
|
|
||||||
|
|
||||||
if conf.dashboard.is_none() {
|
|
||||||
let (exec, working_dir) = get_default_dashboard_exec();
|
|
||||||
|
|
||||||
conf.dashboard = Some(WayVRDashboard {
|
|
||||||
args: None,
|
|
||||||
env: None,
|
|
||||||
exec,
|
|
||||||
working_dir,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
conf
|
|
||||||
}
|
|
||||||
@@ -29,9 +29,6 @@ mod state;
|
|||||||
mod subsystem;
|
mod subsystem;
|
||||||
mod windowing;
|
mod windowing;
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
|
||||||
mod config_wayvr;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Command,
|
process::Command,
|
||||||
|
|||||||
@@ -66,7 +66,11 @@ const GUI_SCALE: f32 = 2.0;
|
|||||||
|
|
||||||
impl DashFrontend {
|
impl DashFrontend {
|
||||||
fn new(app: &mut AppState) -> anyhow::Result<Self> {
|
fn new(app: &mut AppState) -> anyhow::Result<Self> {
|
||||||
let interface = DashInterfaceLive::new();
|
let mut interface = DashInterfaceLive::new();
|
||||||
|
|
||||||
|
for p in app.session.config.autostart_apps.clone() {
|
||||||
|
let _ = interface.process_launch(app, false, p)?;
|
||||||
|
}
|
||||||
|
|
||||||
let frontend = frontend::Frontend::new(
|
let frontend = frontend::Frontend::new(
|
||||||
frontend::InitParams {
|
frontend::InitParams {
|
||||||
@@ -341,6 +345,7 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||||||
fn process_launch(
|
fn process_launch(
|
||||||
&mut self,
|
&mut self,
|
||||||
app: &mut AppState,
|
app: &mut AppState,
|
||||||
|
auto_start: bool,
|
||||||
params: WvrProcessLaunchParams,
|
params: WvrProcessLaunchParams,
|
||||||
) -> anyhow::Result<WvrProcessHandle> {
|
) -> anyhow::Result<WvrProcessHandle> {
|
||||||
let wvr_server = app.wvr_server.as_mut().unwrap();
|
let wvr_server = app.wvr_server.as_mut().unwrap();
|
||||||
@@ -348,6 +353,11 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||||||
let args_vec = gen_args_vec(¶ms.args);
|
let args_vec = gen_args_vec(¶ms.args);
|
||||||
let env_vec = gen_env_vec(¶ms.env);
|
let env_vec = gen_env_vec(¶ms.env);
|
||||||
|
|
||||||
|
if auto_start {
|
||||||
|
app.session.config.autostart_apps.push(params.clone());
|
||||||
|
save_settings(&app.session.config)?;
|
||||||
|
}
|
||||||
|
|
||||||
wvr_server
|
wvr_server
|
||||||
.spawn_process(
|
.spawn_process(
|
||||||
¶ms.name,
|
¶ms.name,
|
||||||
|
|||||||
@@ -508,9 +508,10 @@ impl OverlayBackend for WvrWindowBackend {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
|
let click_freeze = app.session.config.click_freeze_time_ms;
|
||||||
let wvr_server = app.wvr_server.as_mut().unwrap(); //never None
|
let wvr_server = app.wvr_server.as_mut().unwrap(); //never None
|
||||||
if pressed {
|
if pressed {
|
||||||
wvr_server.send_mouse_down(self.window, index);
|
wvr_server.send_mouse_down(click_freeze, self.window, index);
|
||||||
} else {
|
} else {
|
||||||
wvr_server.send_mouse_up(index);
|
wvr_server.send_mouse_up(index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ use glam::Affine3A;
|
|||||||
use idmap::IdMap;
|
use idmap::IdMap;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
#[cfg(feature = "wayvr")]
|
||||||
|
use wgui::log::LogErr;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
drawing, font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
drawing, font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
||||||
renderer_vk::context::SharedContext as WSharedContext,
|
renderer_vk::context::SharedContext as WSharedContext,
|
||||||
@@ -14,9 +16,6 @@ use wlx_common::{
|
|||||||
overlays::{ToastDisplayMethod, ToastTopic},
|
overlays::{ToastDisplayMethod, ToastTopic},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
|
||||||
use crate::config_wayvr::{self, WayVRConfig};
|
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
#[cfg(feature = "wayvr")]
|
||||||
use crate::backend::wayvr::WvrServerState;
|
use crate::backend::wayvr::WvrServerState;
|
||||||
#[cfg(feature = "osc")]
|
#[cfg(feature = "osc")]
|
||||||
@@ -78,19 +77,11 @@ impl AppState {
|
|||||||
let mut tasks = TaskContainer::new();
|
let mut tasks = TaskContainer::new();
|
||||||
|
|
||||||
let session = AppSession::load();
|
let session = AppSession::load();
|
||||||
let wayvr_signals = SyncEventQueue::new();
|
let wvr_signals = SyncEventQueue::new();
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
#[cfg(feature = "wayvr")]
|
||||||
let wayvr_server = session
|
let wvr_server = WvrServerState::new(gfx.clone(), &gfx_extras, wvr_signals.clone())
|
||||||
.wayvr_config
|
.log_err("Could not initialize WayVR Server")
|
||||||
.post_load(
|
|
||||||
gfx.clone(),
|
|
||||||
&gfx_extras,
|
|
||||||
&session.config,
|
|
||||||
&mut tasks,
|
|
||||||
wayvr_signals.clone(),
|
|
||||||
)
|
|
||||||
.inspect_err(|e| log::error!("Could not initialize wayland server: {e:?}"))
|
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let mut hid_provider = HidWrapper::new();
|
let mut hid_provider = HidWrapper::new();
|
||||||
@@ -164,14 +155,14 @@ impl AppState {
|
|||||||
dbus,
|
dbus,
|
||||||
xr_backend,
|
xr_backend,
|
||||||
ipc_server,
|
ipc_server,
|
||||||
wayvr_signals,
|
wayvr_signals: wvr_signals,
|
||||||
desktop_finder,
|
desktop_finder,
|
||||||
|
|
||||||
#[cfg(feature = "osc")]
|
#[cfg(feature = "osc")]
|
||||||
osc_sender,
|
osc_sender,
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
#[cfg(feature = "wayvr")]
|
||||||
wvr_server: wayvr_server,
|
wvr_server,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,9 +171,6 @@ pub struct AppSession {
|
|||||||
pub config: GeneralConfig,
|
pub config: GeneralConfig,
|
||||||
pub config_dirty: bool,
|
pub config_dirty: bool,
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
|
||||||
pub wayvr_config: WayVRConfig, // TODO: rename to "wayland_server_config"
|
|
||||||
|
|
||||||
pub toast_topics: IdMap<ToastTopic, ToastDisplayMethod>,
|
pub toast_topics: IdMap<ToastTopic, ToastDisplayMethod>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,13 +190,8 @@ impl AppSession {
|
|||||||
toast_topics.insert(*k, *v);
|
toast_topics.insert(*k, *v);
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
|
||||||
let wayvr_config = config_wayvr::load_wayvr();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
#[cfg(feature = "wayvr")]
|
|
||||||
wayvr_config,
|
|
||||||
toast_topics,
|
toast_topics,
|
||||||
config_dirty: false,
|
config_dirty: false,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user