use std::{ collections::{BinaryHeap, VecDeque}, sync::Arc, time::Instant, }; #[cfg(feature = "openxr")] use openxr as xr; use glam::{Affine3A, Vec2, Vec3A}; use idmap::IdMap; use thiserror::Error; use crate::{ overlays::{ keyboard::create_keyboard, toast::Toast, watch::{create_watch, WATCH_NAME, WATCH_SCALE}, }, state::AppState, }; use super::overlay::{OverlayBackend, OverlayData, OverlayState}; #[derive(Error, Debug)] pub enum BackendError { #[error("backend not supported")] NotSupported, #[cfg(feature = "openxr")] #[error("openxr error {0}")] OpenXrError(#[from] xr::sys::Result), #[error("shutdown")] Shutdown, #[error("restart")] Restart, #[error("fatal")] Fatal(#[from] anyhow::Error), } pub struct OverlayContainer where T: Default, { overlays: IdMap>, pub extent: Vec2, } impl OverlayContainer where T: Default, { pub fn new(app: &mut AppState) -> anyhow::Result { let mut overlays = IdMap::new(); let (screens, extent) = if std::env::var("WAYLAND_DISPLAY").is_ok() { crate::overlays::screen::get_screens_wayland(&app.session)? } else { crate::overlays::screen::get_screens_x11(&app.session)? }; let mut watch = create_watch::(app, &screens)?; watch.state.want_visible = true; overlays.insert(watch.state.id, watch); let mut keyboard = create_keyboard(app)?; keyboard.state.show_hide = true; keyboard.state.want_visible = false; overlays.insert(keyboard.state.id, keyboard); let mut show_screens = app.session.config.show_screens.clone(); if show_screens.is_empty() { screens.first().map(|s| { show_screens.push(s.state.name.clone()); }); } for mut screen in screens { if show_screens.contains(&screen.state.name) { screen.state.show_hide = true; screen.state.want_visible = false; } overlays.insert(screen.state.id, screen); } Ok(Self { overlays, extent }) } pub fn mut_by_selector(&mut self, selector: &OverlaySelector) -> Option<&mut OverlayData> { match selector { OverlaySelector::Id(id) => self.mut_by_id(*id), OverlaySelector::Name(name) => self.mut_by_name(name), } } pub fn drop_by_selector(&mut self, selector: &OverlaySelector) { match selector { OverlaySelector::Id(id) => { self.overlays.remove(id); } OverlaySelector::Name(name) => { let id = self .overlays .iter() .find(|(_, o)| *o.state.name == **name) .map(|(id, _)| *id); id.and_then(|id| self.overlays.remove(id)); } }; } pub fn get_by_id(&mut self, id: usize) -> Option<&OverlayData> { self.overlays.get(id) } pub fn mut_by_id(&mut self, id: usize) -> Option<&mut OverlayData> { self.overlays.get_mut(id) } pub fn get_by_name<'a>(&'a mut self, name: &str) -> Option<&'a OverlayData> { self.overlays.values().find(|o| *o.state.name == *name) } pub fn mut_by_name<'a>(&'a mut self, name: &str) -> Option<&'a mut OverlayData> { self.overlays.values_mut().find(|o| *o.state.name == *name) } pub fn iter(&self) -> impl Iterator> { self.overlays.values() } pub fn iter_mut(&mut self) -> impl Iterator> { self.overlays.values_mut() } pub fn add(&mut self, overlay: OverlayData) { self.overlays.insert(overlay.state.id, overlay); } pub fn show_hide(&mut self, app: &mut AppState) { let any_shown = self .overlays .values() .any(|o| o.state.show_hide && o.state.want_visible); self.overlays.values_mut().for_each(|o| { if o.state.show_hide { o.state.want_visible = !any_shown; if o.state.want_visible && o.state.recenter { o.state.reset(app, false); } } // toggle watch back on if it was hidden if !any_shown && *o.state.name == *WATCH_NAME { o.state.spawn_scale = WATCH_SCALE * app.session.config.watch_scale; } }) } } #[derive(Clone)] pub enum OverlaySelector { Id(usize), Name(Arc), } struct AppTask { pub not_before: Instant, pub task: TaskType, } impl PartialEq for AppTask { fn eq(&self, other: &Self) -> bool { self.not_before == other.not_before } } impl PartialOrd for AppTask { fn partial_cmp(&self, other: &Self) -> Option { Some(self.not_before.cmp(&other.not_before).reverse()) } } impl Eq for AppTask {} impl Ord for AppTask { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.not_before.cmp(&other.not_before).reverse() } } pub enum TaskType { Global(Box), Overlay( OverlaySelector, Box, ), CreateOverlay( OverlaySelector, Box Option<(OverlayState, Box)> + Send>, ), DropOverlay(OverlaySelector), Toast(Toast), } pub struct TaskContainer { tasks: BinaryHeap, } impl TaskContainer { pub fn new() -> Self { Self { tasks: BinaryHeap::new(), } } pub fn enqueue(&mut self, task: TaskType) { self.tasks.push(AppTask { not_before: Instant::now(), task, }); } pub fn enqueue_at(&mut self, task: TaskType, not_before: Instant) { self.tasks.push(AppTask { not_before, task }); } pub fn retrieve_due(&mut self, dest_buf: &mut VecDeque) { let now = Instant::now(); while let Some(task) = self.tasks.peek() { if task.not_before > now { break; } // Safe unwrap because we peeked. dest_buf.push_back(self.tasks.pop().unwrap().task); } } } pub fn raycast( source: &Affine3A, source_fwd: Vec3A, plane: &Affine3A, plane_norm: Vec3A, ) -> Option<(Vec3A, f32)> { let plane_normal = plane.transform_vector3a(plane_norm); let ray_dir = source.transform_vector3a(source_fwd); let d = plane.translation.dot(-plane_normal); let dist = -(d + source.translation.dot(plane_normal)) / ray_dir.dot(plane_normal); if dist < 0.0 { // plane is behind the caster return None; } let hit_pos = source.translation + ray_dir * dist; Some((hit_pos, dist)) }