refactor tasks

This commit is contained in:
galister
2025-12-10 17:12:26 +09:00
parent c6aa84f6cb
commit 01fea96545
16 changed files with 255 additions and 259 deletions

View File

@@ -8,6 +8,7 @@ use idmap_derive::IntegerId;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use wlx_common::windowing::{OverlayWindowState, Positioning}; use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::backend::task::OverlayTask;
use crate::overlays::anchor::ANCHOR_NAME; use crate::overlays::anchor::ANCHOR_NAME;
use crate::state::{AppSession, AppState}; use crate::state::{AppSession, AppState};
use crate::subsystem::hid::WheelDelta; use crate::subsystem::hid::WheelDelta;
@@ -630,12 +631,12 @@ fn start_grab(
}; };
// Show anchor // Show anchor
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ANCHOR_NAME.clone()), OverlaySelector::Name(ANCHOR_NAME.clone()),
Box::new(|app, o| { Box::new(|app, o| {
o.activate(app); o.activate(app);
}), }),
)); )));
} }
fn handle_scale(transform: &mut Affine3A, scroll_y: f32) { fn handle_scale(transform: &mut Affine3A, scroll_y: f32) {
@@ -711,12 +712,12 @@ where
} }
// Hide anchor // Hide anchor
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ANCHOR_NAME.clone()), OverlaySelector::Name(ANCHOR_NAME.clone()),
Box::new(|_app, o| { Box::new(|_app, o| {
o.deactivate(); o.deactivate();
}), }),
)); )));
log::debug!("Hand {}: dropped {}", idx, overlay.config.name); log::debug!("Hand {}: dropped {}", idx, overlay.config.name);
} }
} }

View File

@@ -1,10 +1,7 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
ops::Add, ops::Add,
sync::{ sync::atomic::Ordering,
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc,
},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@@ -26,7 +23,7 @@ use crate::{
manifest::{install_manifest, uninstall_manifest}, manifest::{install_manifest, uninstall_manifest},
overlay::OpenVrOverlayData, overlay::OpenVrOverlayData,
}, },
task::{ManagerTask, SystemTask, TaskType}, task::{OpenVrTask, OverlayTask, TaskType},
BackendError, BackendError,
}, },
config::save_state, config::save_state,
@@ -40,8 +37,8 @@ use crate::{
windowing::{ windowing::{
backend::{RenderResources, ShouldRender}, backend::{RenderResources, ShouldRender},
manager::OverlayWindowManager, manager::OverlayWindowManager,
window::OverlayWindowData,
}, },
RUNNING,
}; };
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
@@ -54,8 +51,6 @@ pub mod manifest;
pub mod overlay; pub mod overlay;
pub mod playspace; pub mod playspace;
static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub fn openvr_uninstall() { pub fn openvr_uninstall() {
let app_type = EVRApplicationType::VRApplication_Overlay; let app_type = EVRApplicationType::VRApplication_Overlay;
let Ok(context) = ovr_overlay::Context::init(app_type) else { let Ok(context) = ovr_overlay::Context::init(app_type) else {
@@ -68,11 +63,7 @@ pub fn openvr_uninstall() {
} }
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn openvr_run( pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendError> {
running: Arc<AtomicBool>,
show_by_default: bool,
headless: bool,
) -> Result<(), BackendError> {
let app_type = EVRApplicationType::VRApplication_Overlay; let app_type = EVRApplicationType::VRApplication_Overlay;
let Ok(context) = ovr_overlay::Context::init(app_type) else { let Ok(context) = ovr_overlay::Context::init(app_type) else {
log::warn!("Will not use OpenVR: Context init failed"); log::warn!("Will not use OpenVR: Context init failed");
@@ -107,7 +98,7 @@ pub fn openvr_run(
if show_by_default { if show_by_default {
app.tasks.enqueue_at( app.tasks.enqueue_at(
TaskType::Manager(ManagerTask::ShowHide), TaskType::Overlay(OverlayTask::ShowHide),
Instant::now().add(Duration::from_secs(1)), Instant::now().add(Duration::from_secs(1)),
); );
} }
@@ -159,13 +150,11 @@ pub fn openvr_run(
'main_loop: loop { 'main_loop: loop {
let _ = overlay_mgr.wait_frame_sync(frame_timeout); let _ = overlay_mgr.wait_frame_sync(frame_timeout);
if !running.load(Ordering::Relaxed) { if !RUNNING.load(Ordering::Relaxed) {
log::warn!("Received shutdown signal."); log::warn!("Received shutdown signal.");
break 'main_loop; break 'main_loop;
} }
let cur_frame = FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);
while let Some(event) = system_mgr.poll_next_event() { while let Some(event) = system_mgr.poll_next_event() {
match event.event_type { match event.event_type {
EVREventType::VREvent_Quit => { EVREventType::VREvent_Quit => {
@@ -215,52 +204,17 @@ pub fn openvr_run(
while let Some(task) = due_tasks.pop_front() { while let Some(task) = due_tasks.pop_front() {
match task { match task {
TaskType::Overlay(sel, f) => { TaskType::Overlay(task) => {
if let Some(o) = overlays.mut_by_selector(&sel) {
f(&mut app, &mut o.config);
} else {
log::warn!("Overlay not found for task: {sel:?}");
}
}
TaskType::CreateOverlay(sel, f) => {
let None = overlays.mut_by_selector(&sel) else {
continue;
};
let Some(overlay_config) = f(&mut app) else {
continue;
};
overlays.add(
OverlayWindowData {
birthframe: cur_frame,
..OverlayWindowData::from_config(overlay_config)
},
&mut app,
);
}
TaskType::DropOverlay(sel) => {
if let Some(o) = overlays.mut_by_selector(&sel)
&& o.birthframe < cur_frame
{
o.destroy(&mut overlay_mgr);
overlays.remove_by_selector(&sel, &mut app);
}
}
TaskType::System(task) => match task {
SystemTask::ColorGain(channel, value) => {
let _ = adjust_gain(&mut settings_mgr, channel, value);
}
SystemTask::FixFloor => {
playspace.fix_floor(&mut chaperone_mgr, &app.input_state);
}
SystemTask::ResetPlayspace => {
playspace.reset_offset(&mut chaperone_mgr, &app.input_state);
}
},
TaskType::Manager(task) => {
overlays.handle_task(&mut app, task)?; overlays.handle_task(&mut app, task)?;
} }
TaskType::Playspace(task) => {
playspace.handle_task(&mut app, &mut chaperone_mgr, task);
}
TaskType::OpenVR(task) => match task {
OpenVrTask::ColorGain(channel, value) => {
let _ = adjust_gain(&mut settings_mgr, channel, value);
}
},
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
TaskType::WayVR(action) => { TaskType::WayVR(action) => {
wayvr_action(&mut app, &mut overlays, &action); wayvr_action(&mut app, &mut overlays, &action);
@@ -268,6 +222,10 @@ pub fn openvr_run(
} }
} }
while let Some(mut o) = overlays.pop_dropped() {
o.destroy(&mut overlay_mgr);
}
let universe = playspace.get_universe(); let universe = playspace.get_universe();
app.input_state.pre_update(); app.input_state.pre_update();

View File

@@ -6,7 +6,9 @@ use ovr_overlay::{
}; };
use crate::{ use crate::{
backend::input::InputState, state::AppState, windowing::manager::OverlayWindowManager, backend::{input::InputState, task::PlayspaceTask},
state::AppState,
windowing::manager::OverlayWindowManager,
}; };
use super::{helpers::Affine3AConvert, overlay::OpenVrOverlayData}; use super::{helpers::Affine3AConvert, overlay::OpenVrOverlayData};
@@ -32,6 +34,22 @@ impl PlayspaceMover {
} }
} }
pub fn handle_task(
&mut self,
app: &AppState,
chaperone_mgr: &mut ChaperoneSetupManager,
task: PlayspaceTask,
) {
match task {
PlayspaceTask::FixFloor => {
self.fix_floor(chaperone_mgr, &app.input_state);
}
PlayspaceTask::ResetPlayspace => {
self.reset_offset(chaperone_mgr, &app.input_state);
}
}
}
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn update( pub fn update(
&mut self, &mut self,

View File

@@ -2,7 +2,7 @@ use std::{
collections::VecDeque, collections::VecDeque,
ops::Add, ops::Add,
sync::{ sync::{
atomic::{AtomicBool, AtomicUsize, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc,
}, },
time::{Duration, Instant}, time::{Duration, Instant},
@@ -20,7 +20,7 @@ use crate::{
backend::{ backend::{
input::interact, input::interact,
openxr::{lines::LinePool, overlay::OpenXrOverlayData}, openxr::{lines::LinePool, overlay::OpenXrOverlayData},
task::{ManagerTask, SystemTask, TaskType}, task::{OverlayTask, TaskType},
BackendError, BackendError,
}, },
config::save_state, config::save_state,
@@ -34,8 +34,8 @@ use crate::{
windowing::{ windowing::{
backend::{RenderResources, ShouldRender}, backend::{RenderResources, ShouldRender},
manager::OverlayWindowManager, manager::OverlayWindowManager,
window::OverlayWindowData,
}, },
FRAME_COUNTER, RUNNING,
}; };
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
@@ -51,7 +51,6 @@ mod skybox;
mod swapchain; mod swapchain;
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO; const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
struct XrState { struct XrState {
instance: xr::Instance, instance: xr::Instance,
@@ -63,11 +62,7 @@ struct XrState {
} }
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn openxr_run( pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendError> {
running: Arc<AtomicBool>,
show_by_default: bool,
headless: bool,
) -> Result<(), BackendError> {
let (xr_instance, system) = match helpers::init_xr() { let (xr_instance, system) = match helpers::init_xr() {
Ok((xr_instance, system)) => (xr_instance, system), Ok((xr_instance, system)) => (xr_instance, system),
Err(e) => { Err(e) => {
@@ -95,7 +90,7 @@ pub fn openxr_run(
if show_by_default { if show_by_default {
app.tasks.enqueue_at( app.tasks.enqueue_at(
TaskType::Manager(ManagerTask::ShowHide), TaskType::Overlay(OverlayTask::ShowHide),
Instant::now().add(Duration::from_secs(1)), Instant::now().add(Duration::from_secs(1)),
); );
} }
@@ -178,7 +173,7 @@ pub fn openxr_run(
'main_loop: loop { 'main_loop: loop {
let cur_frame = FRAME_COUNTER.fetch_add(1, Ordering::Relaxed); let cur_frame = FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);
if !running.load(Ordering::Relaxed) { if !RUNNING.load(Ordering::Relaxed) {
log::warn!("Received shutdown signal."); log::warn!("Received shutdown signal.");
match xr_state.session.request_exit() { match xr_state.session.request_exit() {
Ok(()) => log::info!("OpenXR session exit requested."), Ok(()) => log::info!("OpenXR session exit requested."),
@@ -493,58 +488,16 @@ pub fn openxr_run(
app.tasks.retrieve_due(&mut due_tasks); app.tasks.retrieve_due(&mut due_tasks);
while let Some(task) = due_tasks.pop_front() { while let Some(task) = due_tasks.pop_front() {
match task { match task {
TaskType::Overlay(sel, f) => { TaskType::Overlay(task) => {
if let Some(o) = overlays.mut_by_selector(&sel) {
f(&mut app, &mut o.config);
} else {
log::warn!("Overlay not found for task: {sel:?}");
}
}
TaskType::CreateOverlay(sel, f) => {
let None = overlays.mut_by_selector(&sel) else {
continue;
};
let Some(overlay_config) = f(&mut app) else {
continue;
};
overlays.add(
OverlayWindowData {
birthframe: cur_frame,
..OverlayWindowData::from_config(overlay_config)
},
&mut app,
);
}
TaskType::DropOverlay(sel) => {
if let Some(o) = overlays.mut_by_selector(&sel)
&& o.birthframe < cur_frame
{
log::debug!("{}: destroy", o.config.name);
if let Some(o) = overlays.remove_by_selector(&sel, &mut app) {
// set for deletion after all images are done showing
delete_queue.push((o, cur_frame + 5));
}
}
}
TaskType::System(task) => match task {
SystemTask::FixFloor => {
if let Some(ref mut playspace) = playspace {
playspace.fix_floor(
&app.input_state,
monado.as_mut().unwrap(), // safe
);
}
}
SystemTask::ResetPlayspace => {
if let Some(ref mut playspace) = playspace {
playspace.reset_offset(monado.as_mut().unwrap()); // safe
}
}
_ => {}
},
TaskType::Manager(task) => {
overlays.handle_task(&mut app, task)?; overlays.handle_task(&mut app, task)?;
} }
TaskType::Playspace(task) => {
if let (Some(playspace), Some(monado)) = (playspace.as_mut(), monado.as_mut()) {
playspace.handle_task(&mut app, monado, task);
}
}
#[cfg(feature = "openvr")]
TaskType::OpenVR(_) => {}
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
TaskType::WayVR(action) => { TaskType::WayVR(action) => {
wayvr_action(&mut app, &mut overlays, &action); wayvr_action(&mut app, &mut overlays, &action);
@@ -552,6 +505,10 @@ pub fn openxr_run(
} }
} }
while let Some(o) = overlays.pop_dropped() {
delete_queue.push((o, cur_frame + 5));
}
delete_queue.retain(|(_, frame)| *frame > cur_frame); delete_queue.retain(|(_, frame)| *frame > cur_frame);
//FIXME: Temporary workaround for Monado bug //FIXME: Temporary workaround for Monado bug

View File

@@ -2,7 +2,9 @@ use glam::{Affine3A, Quat, Vec3A};
use libmonado::{Monado, Pose, ReferenceSpaceType}; use libmonado::{Monado, Pose, ReferenceSpaceType};
use crate::{ use crate::{
backend::input::InputState, state::AppState, windowing::manager::OverlayWindowManager, backend::{input::InputState, task::PlayspaceTask},
state::AppState,
windowing::manager::OverlayWindowManager,
}; };
use super::overlay::OpenXrOverlayData; use super::overlay::OpenXrOverlayData;
@@ -41,6 +43,17 @@ impl PlayspaceMover {
}) })
} }
pub fn handle_task(&mut self, app: &AppState, monado: &mut Monado, task: PlayspaceTask) {
match task {
PlayspaceTask::FixFloor => {
self.fix_floor(&app.input_state, monado);
}
PlayspaceTask::ResetPlayspace => {
self.reset_offset(monado);
}
}
}
pub fn update( pub fn update(
&mut self, &mut self,
overlays: &mut OverlayWindowManager<OpenXrOverlayData>, overlays: &mut OverlayWindowManager<OpenXrOverlayData>,

View File

@@ -43,28 +43,34 @@ impl Ord for AppTask {
} }
} }
pub enum SystemTask { #[cfg(feature = "openvr")]
pub enum OpenVrTask {
ColorGain(ColorChannel, f32), ColorGain(ColorChannel, f32),
}
pub enum PlayspaceTask {
ResetPlayspace, ResetPlayspace,
FixFloor, FixFloor,
} }
pub type OverlayTask = dyn FnOnce(&mut AppState, &mut OverlayWindowConfig) + Send; pub type ModifyOverlayTask = dyn FnOnce(&mut AppState, &mut OverlayWindowConfig) + Send;
pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowConfig> + Send; pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<OverlayWindowConfig> + Send;
pub enum ManagerTask { pub enum OverlayTask {
AddSet, AddSet,
ToggleSet(usize), ToggleSet(usize),
DeleteActiveSet, DeleteActiveSet,
ToggleEditMode, ToggleEditMode,
ShowHide, ShowHide,
Modify(OverlaySelector, Box<ModifyOverlayTask>),
Create(OverlaySelector, Box<CreateOverlayTask>),
Drop(OverlaySelector),
} }
pub enum TaskType { pub enum TaskType {
Overlay(OverlaySelector, Box<OverlayTask>), Overlay(OverlayTask),
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>), Playspace(PlayspaceTask),
DropOverlay(OverlaySelector), #[cfg(feature = "openvr")]
Manager(ManagerTask), OpenVR(OpenVrTask),
System(SystemTask),
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
WayVR(WayVRAction), WayVR(WayVRAction),
} }

View File

@@ -13,7 +13,7 @@ use wgui::{
}; };
use crate::{ use crate::{
backend::task::{ManagerTask, TaskType}, backend::task::{OverlayTask, TaskType},
state::AppState, state::AppState,
windowing::OverlaySelector, windowing::OverlaySelector,
}; };
@@ -58,7 +58,7 @@ pub(super) fn setup_custom_button<S: 'static>(
}; };
Box::new(move |_common, _data, app, _| { Box::new(move |_common, _data, app, _| {
app.tasks app.tasks
.enqueue(TaskType::Manager(ManagerTask::ToggleSet(set_idx))); .enqueue(TaskType::Overlay(OverlayTask::ToggleSet(set_idx)));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}) })
} }
@@ -69,7 +69,7 @@ pub(super) fn setup_custom_button<S: 'static>(
}; };
Box::new(move |_common, _data, app, _| { Box::new(move |_common, _data, app, _| {
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(arg.clone()), OverlaySelector::Name(arg.clone()),
Box::new(move |app, owc| { Box::new(move |app, owc| {
if owc.active_state.is_none() { if owc.active_state.is_none() {
@@ -78,13 +78,13 @@ pub(super) fn setup_custom_button<S: 'static>(
owc.deactivate(); owc.deactivate();
} }
}), }),
)); )));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}) })
} }
"::EditToggle" => Box::new(move |_common, _data, app, _| { "::EditToggle" => Box::new(move |_common, _data, app, _| {
app.tasks app.tasks
.enqueue(TaskType::Manager(ManagerTask::ToggleEditMode)); .enqueue(TaskType::Overlay(OverlayTask::ToggleEditMode));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),
"::WatchHide" => todo!(), "::WatchHide" => todo!(),

View File

@@ -33,10 +33,7 @@ mod config_wayvr;
use std::{ use std::{
path::PathBuf, path::PathBuf,
sync::{ sync::atomic::{AtomicBool, AtomicUsize, Ordering},
atomic::{AtomicBool, Ordering},
Arc,
},
}; };
use clap::Parser; use clap::Parser;
@@ -45,6 +42,9 @@ use sysinfo::Pid;
use tracing::level_filters::LevelFilter; use tracing::level_filters::LevelFilter;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
pub static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub static RUNNING: AtomicBool = AtomicBool::new(true);
/// The lightweight desktop overlay for OpenVR and OpenXR /// The lightweight desktop overlay for OpenVR and OpenXR
#[derive(Default, Parser, Debug)] #[derive(Default, Parser, Debug)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
@@ -82,11 +82,6 @@ struct Args {
/// Path to write logs to /// Path to write logs to
#[arg(short, long, value_name = "FILE_PATH")] #[arg(short, long, value_name = "FILE_PATH")]
log_to: Option<String>, log_to: Option<String>,
#[cfg(feature = "uidev")]
/// Show a desktop window of a UI panel for development
#[arg(short, long, value_name = "UI_NAME")]
uidev: Option<String>,
} }
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
@@ -118,21 +113,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return Ok(()); return Ok(());
} }
let running = Arc::new(AtomicBool::new(true));
let _ = ctrlc::set_handler({ let _ = ctrlc::set_handler({
let running = running.clone(); || {
move || { RUNNING.store(false, Ordering::Relaxed);
running.store(false, Ordering::Relaxed);
} }
}); });
auto_run(running, args); auto_run(args);
Ok(()) Ok(())
} }
#[allow(unused_mut, clippy::similar_names)] #[allow(unused_mut, clippy::similar_names)]
fn auto_run(running: Arc<AtomicBool>, args: Args) { fn auto_run(args: Args) {
let mut tried_xr = false; let mut tried_xr = false;
let mut tried_vr = false; let mut tried_vr = false;
@@ -140,7 +133,7 @@ fn auto_run(running: Arc<AtomicBool>, args: Args) {
if !args_get_openvr(&args) { if !args_get_openvr(&args) {
use crate::backend::{openxr::openxr_run, BackendError}; use crate::backend::{openxr::openxr_run, BackendError};
tried_xr = true; tried_xr = true;
match openxr_run(running.clone(), args.show, args.headless) { match openxr_run(args.show, args.headless) {
Ok(()) => return, Ok(()) => return,
Err(BackendError::NotSupported) => (), Err(BackendError::NotSupported) => (),
Err(e) => { Err(e) => {
@@ -154,7 +147,7 @@ fn auto_run(running: Arc<AtomicBool>, args: Args) {
if !args_get_openxr(&args) { if !args_get_openxr(&args) {
use crate::backend::{openvr::openvr_run, BackendError}; use crate::backend::{openvr::openvr_run, BackendError};
tried_vr = true; tried_vr = true;
match openvr_run(running, args.show, args.headless) { match openvr_run(args.show, args.headless) {
Ok(()) => return, Ok(()) => return,
Err(BackendError::NotSupported) => (), Err(BackendError::NotSupported) => (),
Err(e) => { Err(e) => {

View File

@@ -7,7 +7,7 @@ use wgui::{
widget::rectangle::WidgetRectangle, widget::rectangle::WidgetRectangle,
}; };
use crate::{backend::task::OverlayTask, overlays::edit::EditModeWrapPanel, state::AppState}; use crate::{backend::task::ModifyOverlayTask, overlays::edit::EditModeWrapPanel, state::AppState};
#[derive(Default)] #[derive(Default)]
pub(super) struct InteractLockHandler { pub(super) struct InteractLockHandler {
@@ -53,7 +53,7 @@ impl InteractLockHandler {
&mut self, &mut self,
common: &mut CallbackDataCommon, common: &mut CallbackDataCommon,
app: &mut AppState, app: &mut AppState,
) -> Box<OverlayTask> { ) -> Box<ModifyOverlayTask> {
let defaults = app.wgui_globals.get().defaults.clone(); let defaults = app.wgui_globals.get().defaults.clone();
let rect_color = self.color; let rect_color = self.color;

View File

@@ -18,7 +18,7 @@ use wgui::{
use crate::{ use crate::{
backend::{ backend::{
input::HoverResult, input::HoverResult,
task::{TaskContainer, TaskType}, task::{OverlayTask, TaskContainer, TaskType},
}, },
gui::panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc}, gui::panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
overlays::edit::{ overlays::edit::{
@@ -247,7 +247,8 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
"::EditModeToggleLock" => Box::new(move |common, _data, app, state| { "::EditModeToggleLock" => Box::new(move |common, _data, app, state| {
let sel = OverlaySelector::Id(*state.id.borrow()); let sel = OverlaySelector::Id(*state.id.borrow());
let task = state.lock.toggle(common, app); let task = state.lock.toggle(common, app);
app.tasks.enqueue(TaskType::Overlay(sel, task)); app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),
"::EditModeTab" => { "::EditModeTab" => {
@@ -262,7 +263,8 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
Box::new(move |common, _data, app, state| { Box::new(move |common, _data, app, state| {
let sel = OverlaySelector::Id(*state.id.borrow()); let sel = OverlaySelector::Id(*state.id.borrow());
let task = state.pos.pos_button_clicked(common, &pos_key); let task = state.pos.pos_button_clicked(common, &pos_key);
app.tasks.enqueue(TaskType::Overlay(sel, task)); app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}) })
} }
@@ -275,12 +277,12 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
if state.delete.pressed.elapsed() < Duration::from_secs(1) { if state.delete.pressed.elapsed() < Duration::from_secs(1) {
return Ok(EventResult::Pass); return Ok(EventResult::Pass);
} }
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(*state.id.borrow()), OverlaySelector::Id(*state.id.borrow()),
Box::new(move |_app, owc| { Box::new(move |_app, owc| {
owc.active_state = None; owc.active_state = None;
}), }),
)); )));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),
_ => return, _ => return,
@@ -384,10 +386,10 @@ fn set_up_slider(
let mut tasks = tasks.borrow_mut(); let mut tasks = tasks.borrow_mut();
let e_value = e.value; let e_value = e.value;
tasks.enqueue(TaskType::Overlay( tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(*overlay_id.borrow()), OverlaySelector::Id(*overlay_id.borrow()),
Box::new(move |app, owc| callback(app, owc, e_value)), Box::new(move |app, owc| callback(app, owc, e_value)),
)); )));
Ok(()) Ok(())
})); }));
@@ -408,10 +410,10 @@ fn set_up_checkbox(
let mut tasks = tasks.borrow_mut(); let mut tasks = tasks.borrow_mut();
let e_checked = e.checked; let e_checked = e.checked;
tasks.enqueue(TaskType::Overlay( tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(*overlay_id.borrow()), OverlaySelector::Id(*overlay_id.borrow()),
Box::new(move |app, owc| callback(app, owc, e_checked)), Box::new(move |app, owc| callback(app, owc, e_checked)),
)); )));
Ok(()) Ok(())
})); }));

View File

@@ -7,7 +7,9 @@ use wgui::{
}; };
use wlx_common::{common::LeftRight, windowing::Positioning}; use wlx_common::{common::LeftRight, windowing::Positioning};
use crate::{backend::task::OverlayTask, overlays::edit::EditModeWrapPanel, windowing::window}; use crate::{
backend::task::ModifyOverlayTask, overlays::edit::EditModeWrapPanel, windowing::window,
};
static POS_NAMES: [&str; 6] = ["static", "anchored", "floating", "hmd", "hand_l", "hand_r"]; static POS_NAMES: [&str; 6] = ["static", "anchored", "floating", "hmd", "hand_l", "hand_r"];
@@ -89,7 +91,7 @@ impl PositioningHandler {
&mut self, &mut self,
common: &mut CallbackDataCommon, common: &mut CallbackDataCommon,
key: &str, key: &str,
) -> Box<OverlayTask> { ) -> Box<ModifyOverlayTask> {
self.change_highlight(common, key); self.change_highlight(common, key);
let pos = key_to_pos(key); let pos = key_to_pos(key);

View File

@@ -11,7 +11,7 @@ use wlx_common::windowing::OverlayWindowState;
use crate::{ use crate::{
backend::{ backend::{
input::{HoverResult, PointerHit}, input::{HoverResult, PointerHit},
task::TaskType, task::{OverlayTask, TaskType},
}, },
state::{AppSession, AppState}, state::{AppSession, AppState},
subsystem::hid::WheelDelta, subsystem::hid::WheelDelta,
@@ -73,21 +73,21 @@ impl OverlayBackend for MirrorBackend {
let capture = PipewireCapture::new(self.name.clone(), node_id); let capture = PipewireCapture::new(self.name.clone(), node_id);
self.renderer = self.renderer =
Some(ScreenBackend::new_raw(self.name.clone(), Box::new(capture))); Some(ScreenBackend::new_raw(self.name.clone(), Box::new(capture)));
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(self.name.clone()), OverlaySelector::Name(self.name.clone()),
Box::new(|app, o| { Box::new(|app, o| {
o.activate(app); o.activate(app);
}), }),
)); )));
} }
Err(e) => { Err(e) => {
log::warn!("Failed to create mirror due to PipeWire error: {e:?}"); log::warn!("Failed to create mirror due to PipeWire error: {e:?}");
self.renderer = None; self.renderer = None;
// drop self // drop self
app.tasks app.tasks
.enqueue(TaskType::DropOverlay(OverlaySelector::Name( .enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Name(
self.name.clone(), self.name.clone(),
))); ))));
} }
} }
} }

View File

@@ -5,7 +5,7 @@ use std::{
time::Instant, time::Instant,
}; };
use glam::{Affine3A, Quat, Vec3, vec3}; use glam::{vec3, Affine3A, Quat, Vec3};
use wgui::{ use wgui::{
i18n::Translation, i18n::Translation,
parser::parse_color_hex, parser::parse_color_hex,
@@ -27,10 +27,10 @@ use wlx_common::{
}; };
use crate::{ use crate::{
backend::task::TaskType, backend::task::{OverlayTask, TaskType},
gui::panel::GuiPanel, gui::panel::GuiPanel,
state::AppState, state::AppState,
windowing::{OverlaySelector, Z_ORDER_TOAST, window::OverlayWindowConfig}, windowing::{window::OverlayWindowConfig, OverlaySelector, Z_ORDER_TOAST},
}; };
const FONT_SIZE: isize = 16; const FONT_SIZE: isize = 16;
@@ -86,27 +86,29 @@ impl Toast {
// drop any toast that was created before us. // drop any toast that was created before us.
// (DropOverlay only drops overlays that were // (DropOverlay only drops overlays that were
// created before current frame) // created before current frame)
app.tasks app.tasks.enqueue_at(
.enqueue_at(TaskType::DropOverlay(selector.clone()), instant); TaskType::Overlay(OverlayTask::Drop(selector.clone())),
instant,
);
// CreateOverlay only creates the overlay if // CreateOverlay only creates the overlay if
// the selector doesn't exist yet, so in case // the selector doesn't exist yet, so in case
// multiple toasts are submitted for the same // multiple toasts are submitted for the same
// frame, only the first one gets created // frame, only the first one gets created
app.tasks.enqueue_at( app.tasks.enqueue_at(
TaskType::CreateOverlay( TaskType::Overlay(OverlayTask::Create(
selector.clone(), selector.clone(),
Box::new(move |app| { Box::new(move |app| {
let maybe_toast = new_toast(self, app); let maybe_toast = new_toast(self, app);
app.tasks.enqueue_at( app.tasks.enqueue_at(
// at timeout, drop the overlay by ID instead // at timeout, drop the overlay by ID instead
// in order to avoid dropping any newer toasts // in order to avoid dropping any newer toasts
TaskType::DropOverlay(selector), TaskType::Overlay(OverlayTask::Drop(selector)),
destroy_at, destroy_at,
); );
maybe_toast maybe_toast
}), }),
), )),
instant, instant,
); );
} }

View File

@@ -14,26 +14,26 @@ use wgui::{
parser::Fetchable, parser::Fetchable,
renderer_vk::text::custom_glyph::CustomGlyphData, renderer_vk::text::custom_glyph::CustomGlyphData,
taffy, taffy,
widget::{EventResult, sprite::WidgetSprite}, widget::{sprite::WidgetSprite, EventResult},
}; };
use wlx_common::windowing::{OverlayWindowState, Positioning}; use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::{ use crate::{
backend::{ backend::{
input::TrackedDeviceRole, input::TrackedDeviceRole,
task::{ManagerTask, TaskType}, task::{OverlayTask, TaskType},
}, },
gui::{ gui::{
panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS}, panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
timer::GuiTimer, timer::GuiTimer,
}, },
overlays::edit::LongPressButtonState, overlays::edit::LongPressButtonState,
state::AppState, state::AppState,
windowing::{ windowing::{
OverlaySelector, Z_ORDER_WATCH,
backend::{OverlayEventData, OverlayMeta}, backend::{OverlayEventData, OverlayMeta},
manager::MAX_OVERLAY_SETS, manager::MAX_OVERLAY_SETS,
window::{OverlayWindowConfig, OverlayWindowData}, window::{OverlayWindowConfig, OverlayWindowData},
OverlaySelector, Z_ORDER_WATCH,
}, },
}; };
@@ -81,11 +81,11 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
return Ok(EventResult::Consumed); return Ok(EventResult::Consumed);
} }
app.tasks app.tasks
.enqueue(TaskType::Manager(ManagerTask::DeleteActiveSet)); .enqueue(TaskType::Overlay(OverlayTask::DeleteActiveSet));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),
"::EditModeAddSet" => Box::new(move |_common, _data, app, _state| { "::EditModeAddSet" => Box::new(move |_common, _data, app, _state| {
app.tasks.enqueue(TaskType::Manager(ManagerTask::AddSet)); app.tasks.enqueue(TaskType::Overlay(OverlayTask::AddSet));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}), }),
"::EditModeOverlayToggle" => { "::EditModeOverlayToggle" => {
@@ -100,7 +100,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
return Ok(EventResult::Consumed); return Ok(EventResult::Consumed);
}; };
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay.id), OverlaySelector::Id(overlay.id),
Box::new(move |app, owc| { Box::new(move |app, owc| {
if owc.active_state.is_none() { if owc.active_state.is_none() {
@@ -109,7 +109,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
owc.deactivate(); owc.deactivate();
} }
}), }),
)); )));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}) })
} }

View File

@@ -19,7 +19,7 @@ use wlx_common::windowing::OverlayWindowState;
use crate::{ use crate::{
backend::{ backend::{
input::{self, HoverResult}, input::{self, HoverResult},
task::TaskType, task::{OverlayTask, TaskType},
wayvr::{ wayvr::{
self, display, self, display,
server_ipc::{gen_args_vec, gen_env_vec}, server_ipc::{gen_args_vec, gen_env_vec},
@@ -332,12 +332,12 @@ where
WvrStateChanged::DashboardHidden WvrStateChanged::DashboardHidden
})); }));
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay_id), OverlaySelector::Id(overlay_id),
Box::new(move |app, o| { Box::new(move |app, o| {
o.toggle(app); o.toggle(app);
}), }),
)); )));
Ok(()) Ok(())
} }
@@ -429,12 +429,12 @@ where
.data .data
.state .state
.set_display_visible(display_handle, visible); .set_display_visible(display_handle, visible);
app.tasks.enqueue(TaskType::Overlay( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay_id), OverlaySelector::Id(overlay_id),
Box::new(move |app, o| { Box::new(move |app, o| {
o.toggle(app); o.toggle(app);
}), }),
)); )));
} }
} }
wayvr::WayVRSignal::DisplayWindowLayout(display_handle, layout) => { wayvr::WayVRSignal::DisplayWindowLayout(display_handle, layout) => {
@@ -448,7 +448,9 @@ where
} }
wayvr::WayVRSignal::DropOverlay(overlay_id) => { wayvr::WayVRSignal::DropOverlay(overlay_id) => {
app.tasks app.tasks
.enqueue(TaskType::DropOverlay(OverlaySelector::Id(overlay_id))); .enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
overlay_id,
))));
} }
wayvr::WayVRSignal::Haptics(haptics) => { wayvr::WayVRSignal::Haptics(haptics) => {
wayvr.pending_haptics = Some(haptics); wayvr.pending_haptics = Some(haptics);

View File

@@ -1,11 +1,14 @@
use std::collections::HashMap; use std::{
collections::{HashMap, VecDeque},
sync::atomic::Ordering,
};
use glam::{Affine3A, Vec3, Vec3A}; use glam::{Affine3A, Vec3, Vec3A};
use slotmap::{HopSlotMap, Key, SecondaryMap}; use slotmap::{HopSlotMap, Key, SecondaryMap};
use wlx_common::{config::SerializedWindowSet, overlays::ToastTopic}; use wlx_common::{config::SerializedWindowSet, overlays::ToastTopic};
use crate::{ use crate::{
backend::task::ManagerTask, backend::task::OverlayTask,
overlays::{ overlays::{
anchor::create_anchor, edit::EditWrapperManager, keyboard::builder::create_keyboard, anchor::create_anchor, edit::EditWrapperManager, keyboard::builder::create_keyboard,
screen::create_screens, toast::Toast, watch::create_watch, screen::create_screens, toast::Toast, watch::create_watch,
@@ -18,6 +21,7 @@ use crate::{
window::{OverlayCategory, OverlayWindowData}, window::{OverlayCategory, OverlayWindowData},
OverlayID, OverlaySelector, OverlayID, OverlaySelector,
}, },
FRAME_COUNTER,
}; };
pub const MAX_OVERLAY_SETS: usize = 7; pub const MAX_OVERLAY_SETS: usize = 7;
@@ -34,6 +38,7 @@ pub struct OverlayWindowManager<T> {
anchor_local: Affine3A, anchor_local: Affine3A,
watch_id: OverlayID, watch_id: OverlayID,
edit_mode: bool, edit_mode: bool,
dropped_overlays: VecDeque<OverlayWindowData<T>>,
} }
impl<T> OverlayWindowManager<T> impl<T> OverlayWindowManager<T>
@@ -52,6 +57,7 @@ where
anchor_local: Affine3A::from_translation(Vec3::NEG_Z), anchor_local: Affine3A::from_translation(Vec3::NEG_Z),
watch_id: OverlayID::null(), // set down below watch_id: OverlayID::null(), // set down below
edit_mode: false, edit_mode: false,
dropped_overlays: VecDeque::with_capacity(8),
}; };
if headless { if headless {
@@ -119,9 +125,104 @@ where
Ok(me) Ok(me)
} }
pub fn handle_task(&mut self, app: &mut AppState, task: OverlayTask) -> anyhow::Result<()> {
match task {
OverlayTask::ShowHide => self.show_hide(app),
OverlayTask::ToggleSet(set) => {
self.switch_or_toggle_set(app, set);
}
OverlayTask::ToggleEditMode => {
self.set_edit_mode(!self.edit_mode, app)?;
}
OverlayTask::AddSet => {
self.sets.push(OverlayWindowSet::default());
let len = self.sets.len();
if let Some(watch) = self.mut_by_id(self.watch_id) {
watch
.config
.backend
.notify(app, OverlayEventData::NumSetsChanged(len))?;
}
}
OverlayTask::DeleteActiveSet => {
let Some(set) = self.current_set else {
Toast::new(
ToastTopic::System,
"Can't remove set".into(),
"No set is selected!".into(),
)
.with_timeout(5.)
.with_sound(true)
.submit(app);
return Ok(());
};
if self.sets.len() <= 1 {
Toast::new(
ToastTopic::System,
"Can't remove set".into(),
"This is the last existing set!".into(),
)
.with_timeout(5.)
.with_sound(true)
.submit(app);
return Ok(());
}
self.switch_to_set(app, None);
self.sets.remove(set);
let len = self.sets.len();
if let Some(watch) = self.mut_by_id(self.watch_id) {
watch
.config
.backend
.notify(app, OverlayEventData::NumSetsChanged(len))?;
}
}
OverlayTask::Modify(sel, f) => {
if let Some(o) = self.mut_by_selector(&sel) {
f(app, &mut o.config);
} else {
log::warn!("Overlay not found for task: {sel:?}");
}
}
OverlayTask::Create(sel, f) => {
let None = self.mut_by_selector(&sel) else {
return Ok(());
};
let Some(overlay_config) = f(app) else {
return Ok(());
};
self.add(
OverlayWindowData {
birthframe: FRAME_COUNTER.load(Ordering::Relaxed),
..OverlayWindowData::from_config(overlay_config)
},
app,
);
}
OverlayTask::Drop(sel) => {
if let Some(o) = self.mut_by_selector(&sel)
&& o.birthframe < FRAME_COUNTER.load(Ordering::Relaxed)
{
if let Some(o) = self.remove_by_selector(&sel, app) {
self.dropped_overlays.push_back(o);
}
}
}
}
Ok(())
}
} }
impl<T> OverlayWindowManager<T> { impl<T> OverlayWindowManager<T> {
pub fn pop_dropped(&mut self) -> Option<OverlayWindowData<T>> {
self.dropped_overlays.pop_front()
}
pub fn persist_layout(&mut self, app: &mut AppState) { pub fn persist_layout(&mut self, app: &mut AppState) {
app.session.config.sets.clear(); app.session.config.sets.clear();
app.session.config.sets.reserve(self.sets.len()); app.session.config.sets.reserve(self.sets.len());
@@ -390,65 +491,6 @@ impl<T> OverlayWindowManager<T> {
self.mut_by_id(self.watch_id).unwrap().config.activate(app); self.mut_by_id(self.watch_id).unwrap().config.activate(app);
} }
pub fn handle_task(&mut self, app: &mut AppState, task: ManagerTask) -> anyhow::Result<()> {
match task {
ManagerTask::ShowHide => self.show_hide(app),
ManagerTask::ToggleSet(set) => {
self.switch_or_toggle_set(app, set);
}
ManagerTask::ToggleEditMode => {
self.set_edit_mode(!self.edit_mode, app)?;
}
ManagerTask::AddSet => {
self.sets.push(OverlayWindowSet::default());
let len = self.sets.len();
if let Some(watch) = self.mut_by_id(self.watch_id) {
watch
.config
.backend
.notify(app, OverlayEventData::NumSetsChanged(len))?;
}
}
ManagerTask::DeleteActiveSet => {
let Some(set) = self.current_set else {
Toast::new(
ToastTopic::System,
"Can't remove set".into(),
"No set is selected!".into(),
)
.with_timeout(5.)
.with_sound(true)
.submit(app);
return Ok(());
};
if self.sets.len() <= 1 {
Toast::new(
ToastTopic::System,
"Can't remove set".into(),
"This is the last existing set!".into(),
)
.with_timeout(5.)
.with_sound(true)
.submit(app);
return Ok(());
}
self.switch_to_set(app, None);
self.sets.remove(set);
let len = self.sets.len();
if let Some(watch) = self.mut_by_id(self.watch_id) {
watch
.config
.backend
.notify(app, OverlayEventData::NumSetsChanged(len))?;
}
}
}
Ok(())
}
fn overlays_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> { fn overlays_changed(&mut self, app: &mut AppState) -> anyhow::Result<()> {
let mut meta = Vec::with_capacity(self.overlays.len()); let mut meta = Vec::with_capacity(self.overlays.len());
for (id, data) in &self.overlays { for (id, data) in &self.overlays {