WayVR: Initial GUI integration

The format of the wayvr.yaml configuration file is subject to change at any time.
This commit is contained in:
Aleksander
2024-10-19 20:39:54 +02:00
committed by galister
parent edfa77e07c
commit d9dddbad11
17 changed files with 453 additions and 89 deletions

View File

@@ -10,43 +10,22 @@ use crate::{
wayvr,
},
graphics::WlxGraphics,
state::{self, KeyboardFocus},
state::{self, AppState, KeyboardFocus},
};
pub struct WayVRContext {
wayvr: Rc<RefCell<wayvr::WayVR>>,
display: wayvr::display::DisplayHandle,
width: u32,
height: u32,
}
#[derive(Default)]
pub struct WayVRProcess<'a> {
pub exec_path: &'a str,
pub args: &'a [&'a str],
pub env: &'a [(&'a str, &'a str)],
}
impl WayVRContext {
pub fn new(
wvr: Rc<RefCell<wayvr::WayVR>>,
width: u32,
height: u32,
processes: &[WayVRProcess],
display: wayvr::display::DisplayHandle,
) -> anyhow::Result<Self> {
let mut wayvr = wvr.borrow_mut();
let display = wayvr.create_display(width, height)?;
for process in processes {
wayvr.spawn_process(display, process.exec_path, process.args, process.env)?;
}
Ok(Self {
wayvr: wvr.clone(),
display,
width,
height,
})
}
}
@@ -73,14 +52,15 @@ impl InteractionHandler for WayVRInteractionHandler {
) -> Option<input::Haptics> {
let ctx = self.context.borrow();
let pos = self.mouse_transform.transform_point2(hit.uv);
let x = ((pos.x * ctx.width as f32) as i32).max(0);
let y = ((pos.y * ctx.height as f32) as i32).max(0);
let mut wayvr = ctx.wayvr.borrow_mut();
if let Some(disp) = wayvr.get_display_by_handle(ctx.display) {
let pos = self.mouse_transform.transform_point2(hit.uv);
let x = ((pos.x * disp.width as f32) as i32).max(0);
let y = ((pos.y * disp.height as f32) as i32).max(0);
let ctx = self.context.borrow();
ctx.wayvr
.borrow_mut()
.send_mouse_move(ctx.display, x as u32, y as u32);
let ctx = self.context.borrow();
wayvr.send_mouse_move(ctx.display, x as u32, y as u32);
}
None
}
@@ -120,24 +100,16 @@ pub struct WayVRRenderer {
view: Option<Arc<vulkano::image::view::ImageView>>,
context: Rc<RefCell<WayVRContext>>,
graphics: Arc<WlxGraphics>,
width: u32,
height: u32,
}
impl WayVRRenderer {
pub fn new(
app: &mut state::AppState,
wvr: Rc<RefCell<wayvr::WayVR>>,
width: u32,
height: u32,
processes: &[WayVRProcess],
display: wayvr::display::DisplayHandle,
) -> anyhow::Result<Self> {
Ok(Self {
context: Rc::new(RefCell::new(WayVRContext::new(
wvr, width, height, processes,
)?)),
width,
height,
context: Rc::new(RefCell::new(WayVRContext::new(wvr, display)?)),
dmabuf_image: None,
view: None,
graphics: app.graphics.clone(),
@@ -154,35 +126,43 @@ impl WayVRRenderer {
planes[0].offset = data.offset as u32;
planes[0].stride = data.stride;
let frame = DmabufFrame {
format: FrameFormat {
width: self.width,
height: self.height,
fourcc: FourCC {
value: data.mod_info.fourcc,
let ctx = self.context.borrow_mut();
let wayvr = ctx.wayvr.borrow_mut();
if let Some(disp) = wayvr.get_display_by_handle(ctx.display) {
let frame = DmabufFrame {
format: FrameFormat {
width: disp.width,
height: disp.height,
fourcc: FourCC {
value: data.mod_info.fourcc,
},
modifier: data.mod_info.modifiers[0], /* possibly not proper? */
},
modifier: data.mod_info.modifiers[0], /* possibly not proper? */
},
num_planes: 1,
planes,
};
num_planes: 1,
planes,
};
let layouts: Vec<SubresourceLayout> = vec![SubresourceLayout {
offset: data.offset as _,
size: 0,
row_pitch: data.stride as _,
array_pitch: None,
depth_pitch: None,
}];
drop(wayvr);
let tex = self.graphics.dmabuf_texture_ex(
frame,
vulkano::image::ImageTiling::DrmFormatModifier,
layouts,
data.mod_info.modifiers,
)?;
self.dmabuf_image = Some(tex.clone());
self.view = Some(vulkano::image::view::ImageView::new_default(tex).unwrap());
let layouts: Vec<SubresourceLayout> = vec![SubresourceLayout {
offset: data.offset as _,
size: 0,
row_pitch: data.stride as _,
array_pitch: None,
depth_pitch: None,
}];
let tex = self.graphics.dmabuf_texture_ex(
frame,
vulkano::image::ImageTiling::DrmFormatModifier,
layouts,
data.mod_info.modifiers,
)?;
self.dmabuf_image = Some(tex.clone());
self.view = Some(vulkano::image::view::ImageView::new_default(tex).unwrap());
} else {
anyhow::bail!("Failed to fetch WayVR display")
}
}
Ok(())
@@ -232,31 +212,30 @@ impl OverlayRenderer for WayVRRenderer {
#[allow(dead_code)]
pub fn create_wayvr<O>(
app: &mut state::AppState,
width: u32,
height: u32,
processes: &[WayVRProcess],
display: &wayvr::display::Display,
display_handle: wayvr::display::DisplayHandle,
display_scale: f32,
) -> anyhow::Result<OverlayData<O>>
where
O: Default,
{
let transform = ui_transform(&[width, height]);
let transform = ui_transform(&[display.width, display.height]);
let state = OverlayState {
name: format!("WayVR Screen ({}x{})", width, height).into(),
name: format!("WayVR Screen ({}x{})", display.width, display.height).into(),
keyboard_focus: Some(KeyboardFocus::WayVR),
want_visible: true,
interactable: true,
recenter: true,
grabbable: true,
spawn_scale: 1.0,
spawn_point: vec3a(0.0, -0.5, 0.0),
spawn_scale: display_scale,
spawn_point: vec3a(0.0, -0.1, -1.0),
interaction_transform: transform,
..Default::default()
};
let wayvr = app.get_wayvr()?;
let renderer = WayVRRenderer::new(app, wayvr, width, height, processes)?;
let renderer = WayVRRenderer::new(app, wayvr, display_handle)?;
let context = renderer.context.clone();
let backend = Box::new(SplitOverlayBackend {
@@ -270,3 +249,100 @@ where
..Default::default()
})
}
fn action_wayvr_internal<O>(
catalog_name: &Arc<str>,
app_name: &Arc<str>,
app: &mut AppState,
) -> anyhow::Result<Option<OverlayData<O>>>
where
O: Default,
{
use crate::overlays::wayvr::create_wayvr;
let mut created_overlay: Option<OverlayData<O>> = None;
let wayvr = app.get_wayvr()?.clone();
let catalog = app
.session
.wayvr_config
.get_catalog(catalog_name)
.ok_or(anyhow::anyhow!(
"Failed to get catalog \"{}\"",
catalog_name
))?
.clone();
if let Some(app_entry) = catalog.get_app(app_name) {
let mut wayvr = wayvr.borrow_mut();
let disp_handle = if let Some(disp) = wayvr.get_display_by_name(&app_entry.target_display) {
disp
} else {
let conf_display = app
.session
.wayvr_config
.get_display(&app_entry.target_display)
.ok_or(anyhow::anyhow!(
"Cannot find display named \"{}\"",
app_entry.target_display
))?;
let display_handle = wayvr.create_display(
conf_display.width,
conf_display.height,
&app_entry.target_display,
)?;
let display = wayvr.get_display_by_handle(display_handle).unwrap(); // Never fails
created_overlay = Some(create_wayvr::<O>(
app,
display,
display_handle,
conf_display.scale,
)?);
display_handle
};
// Parse additional args
let args_vec: Vec<&str> = if let Some(args) = &app_entry.args {
args.as_str().split_whitespace().collect()
} else {
vec![]
};
// Parse additional env
let env_vec: Vec<(&str, &str)> = if let Some(env) = &app_entry.env {
// splits "FOO=BAR=TEST,123" into (&"foo", &"bar=test,123")
env.iter()
.filter_map(|e| e.as_str().split_once('='))
.collect()
} else {
vec![]
};
wayvr.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)?
}
Ok(created_overlay)
}
// Returns newly created overlay (if needed)
pub fn action_wayvr<O>(
catalog_name: &Arc<str>,
app_name: &Arc<str>,
app: &mut AppState,
) -> Option<OverlayData<O>>
where
O: Default,
{
match action_wayvr_internal(catalog_name, app_name, app) {
Ok(res) => res,
Err(e) => {
// Happens if something went wrong with initialization
// or input exec path is invalid. Do nothing, just print an error
log::error!("action_wayvr failed: {}", e);
None
}
}
}