Files
wayvr/src/state.rs

229 lines
6.7 KiB
Rust

use anyhow::bail;
use glam::Affine3A;
use idmap::IdMap;
use rodio::{Decoder, OutputStream, OutputStreamHandle, Source};
use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec};
use std::{io::Cursor, sync::Arc};
use vulkano::image::view::ImageView;
#[cfg(feature = "wayvr")]
use {
crate::config_wayvr::{self, WayVRConfig},
crate::overlays::wayvr::WayVRState,
std::{cell::RefCell, rc::Rc},
};
use crate::{
backend::{input::InputState, overlay::OverlayID, task::TaskContainer},
config::{AStrMap, GeneralConfig},
config_io,
graphics::WlxGraphics,
gui::font::FontCache,
hid::HidProvider,
overlays::toast::{DisplayMethod, ToastTopic},
shaders::{
frag_color, frag_glyph, frag_grid, frag_screen, frag_sprite, frag_sprite2, frag_sprite2_hl,
frag_swapchain, vert_common,
},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum KeyboardFocus {
PhysicalScreen,
#[allow(dead_code)] // Not available if "wayvr" feature is disabled
WayVR, // (for now without wayland window id data, it's handled internally),
}
pub struct AppState {
pub fc: FontCache,
pub session: AppSession,
pub tasks: TaskContainer,
pub graphics: Arc<WlxGraphics>,
pub input_state: InputState,
pub hid_provider: Box<dyn HidProvider>,
pub audio: AudioOutput,
pub screens: SmallVec<[ScreenMeta; 8]>,
pub anchor: Affine3A,
pub sprites: AStrMap<Arc<ImageView>>,
pub keyboard_focus: KeyboardFocus,
#[cfg(feature = "wayvr")]
pub wayvr: Option<Rc<RefCell<WayVRState>>>, // Dynamically created if requested
}
impl AppState {
pub fn from_graphics(graphics: Arc<WlxGraphics>) -> anyhow::Result<Self> {
// insert shared resources
{
let Ok(mut shaders) = graphics.shared_shaders.write() else {
bail!("Failed to lock shared shaders");
};
let shader = vert_common::load(graphics.device.clone())?;
shaders.insert("vert_common", shader);
let shader = frag_color::load(graphics.device.clone())?;
shaders.insert("frag_color", shader);
let shader = frag_glyph::load(graphics.device.clone())?;
shaders.insert("frag_glyph", shader);
let shader = frag_grid::load(graphics.device.clone())?;
shaders.insert("frag_grid", shader);
let shader = frag_sprite::load(graphics.device.clone())?;
shaders.insert("frag_sprite", shader);
let shader = frag_sprite2::load(graphics.device.clone())?;
shaders.insert("frag_sprite2", shader);
let shader = frag_sprite2_hl::load(graphics.device.clone())?;
shaders.insert("frag_sprite2_hl", shader);
let shader = frag_screen::load(graphics.device.clone())?;
shaders.insert("frag_screen", shader);
let shader = frag_swapchain::load(graphics.device.clone())?;
shaders.insert("frag_swapchain", shader);
}
#[cfg(feature = "wayvr")]
let mut tasks = TaskContainer::new();
#[cfg(not(feature = "wayvr"))]
let tasks = TaskContainer::new();
let session = AppSession::load();
#[cfg(feature = "wayvr")]
let wayvr = session
.wayvr_config
.post_load(&session.config, &mut tasks)?;
Ok(AppState {
fc: FontCache::new(session.config.primary_font.clone())?,
session,
tasks,
graphics,
input_state: InputState::new(),
hid_provider: crate::hid::initialize(),
audio: AudioOutput::new(),
screens: smallvec![],
anchor: Affine3A::IDENTITY,
sprites: AStrMap::new(),
keyboard_focus: KeyboardFocus::PhysicalScreen,
#[cfg(feature = "wayvr")]
wayvr,
})
}
#[cfg(feature = "wayvr")]
#[allow(dead_code)]
pub fn get_wayvr(&mut self) -> anyhow::Result<Rc<RefCell<WayVRState>>> {
if let Some(wvr) = &self.wayvr {
Ok(wvr.clone())
} else {
let wayvr = Rc::new(RefCell::new(WayVRState::new(
WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config),
)?));
self.wayvr = Some(wayvr.clone());
Ok(wayvr)
}
}
}
pub struct AppSession {
pub config: GeneralConfig,
#[cfg(feature = "wayvr")]
pub wayvr_config: WayVRConfig,
pub toast_topics: IdMap<ToastTopic, DisplayMethod>,
}
impl AppSession {
pub fn load() -> Self {
let config_root_path = config_io::ensure_config_root();
log::info!("Config root path: {}", config_root_path.to_string_lossy());
let config = GeneralConfig::load_from_disk();
let mut toast_topics = IdMap::new();
toast_topics.insert(ToastTopic::System, DisplayMethod::Center);
toast_topics.insert(ToastTopic::DesktopNotification, DisplayMethod::Center);
toast_topics.insert(ToastTopic::XSNotification, DisplayMethod::Center);
config.notification_topics.iter().for_each(|(k, v)| {
toast_topics.insert(*k, *v);
});
#[cfg(feature = "wayvr")]
let wayvr_config = config_wayvr::load_wayvr();
AppSession {
config,
toast_topics,
#[cfg(feature = "wayvr")]
wayvr_config,
}
}
}
pub struct AudioOutput {
audio_stream: Option<(OutputStream, OutputStreamHandle)>,
first_try: bool,
}
impl AudioOutput {
pub fn new() -> Self {
AudioOutput {
audio_stream: None,
first_try: true,
}
}
fn get_handle(&mut self) -> Option<&OutputStreamHandle> {
if self.audio_stream.is_none() && self.first_try {
self.first_try = false;
if let Ok((stream, handle)) = OutputStream::try_default() {
self.audio_stream = Some((stream, handle));
} else {
log::error!("Failed to open audio stream. Audio will not work.");
return None;
}
}
self.audio_stream.as_ref().map(|(_, h)| h)
}
pub fn play(&mut self, wav_bytes: &'static [u8]) {
let Some(handle) = self.get_handle() else {
return;
};
let cursor = Cursor::new(wav_bytes);
let source = match Decoder::new_wav(cursor) {
Ok(source) => source,
Err(e) => {
log::error!("Failed to play sound: {:?}", e);
return;
}
};
let _ = handle.play_raw(source.convert_samples());
}
}
pub struct ScreenMeta {
pub name: Arc<str>,
pub id: OverlayID,
pub native_handle: u32,
}
#[derive(Serialize, Deserialize, Clone, Copy, Default)]
#[repr(u8)]
pub enum LeftRight {
#[default]
Left,
Right,
}