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

View File

@@ -1,10 +1,7 @@
use std::{
collections::VecDeque,
ops::Add,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc,
},
sync::atomic::Ordering,
time::{Duration, Instant},
};
@@ -26,7 +23,7 @@ use crate::{
manifest::{install_manifest, uninstall_manifest},
overlay::OpenVrOverlayData,
},
task::{ManagerTask, SystemTask, TaskType},
task::{OpenVrTask, OverlayTask, TaskType},
BackendError,
},
config::save_state,
@@ -40,8 +37,8 @@ use crate::{
windowing::{
backend::{RenderResources, ShouldRender},
manager::OverlayWindowManager,
window::OverlayWindowData,
},
RUNNING,
};
#[cfg(feature = "wayvr")]
@@ -54,8 +51,6 @@ pub mod manifest;
pub mod overlay;
pub mod playspace;
static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub fn openvr_uninstall() {
let app_type = EVRApplicationType::VRApplication_Overlay;
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)]
pub fn openvr_run(
running: Arc<AtomicBool>,
show_by_default: bool,
headless: bool,
) -> Result<(), BackendError> {
pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendError> {
let app_type = EVRApplicationType::VRApplication_Overlay;
let Ok(context) = ovr_overlay::Context::init(app_type) else {
log::warn!("Will not use OpenVR: Context init failed");
@@ -107,7 +98,7 @@ pub fn openvr_run(
if show_by_default {
app.tasks.enqueue_at(
TaskType::Manager(ManagerTask::ShowHide),
TaskType::Overlay(OverlayTask::ShowHide),
Instant::now().add(Duration::from_secs(1)),
);
}
@@ -159,13 +150,11 @@ pub fn openvr_run(
'main_loop: loop {
let _ = overlay_mgr.wait_frame_sync(frame_timeout);
if !running.load(Ordering::Relaxed) {
if !RUNNING.load(Ordering::Relaxed) {
log::warn!("Received shutdown signal.");
break 'main_loop;
}
let cur_frame = FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);
while let Some(event) = system_mgr.poll_next_event() {
match event.event_type {
EVREventType::VREvent_Quit => {
@@ -215,52 +204,17 @@ pub fn openvr_run(
while let Some(task) = due_tasks.pop_front() {
match task {
TaskType::Overlay(sel, f) => {
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) => {
TaskType::Overlay(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")]
TaskType::WayVR(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();
app.input_state.pre_update();

View File

@@ -6,7 +6,9 @@ use ovr_overlay::{
};
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};
@@ -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)]
pub fn update(
&mut self,

View File

@@ -2,7 +2,7 @@ use std::{
collections::VecDeque,
ops::Add,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
atomic::{AtomicBool, Ordering},
Arc,
},
time::{Duration, Instant},
@@ -20,7 +20,7 @@ use crate::{
backend::{
input::interact,
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
task::{ManagerTask, SystemTask, TaskType},
task::{OverlayTask, TaskType},
BackendError,
},
config::save_state,
@@ -34,8 +34,8 @@ use crate::{
windowing::{
backend::{RenderResources, ShouldRender},
manager::OverlayWindowManager,
window::OverlayWindowData,
},
FRAME_COUNTER, RUNNING,
};
#[cfg(feature = "wayvr")]
@@ -51,7 +51,6 @@ mod skybox;
mod swapchain;
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
struct XrState {
instance: xr::Instance,
@@ -63,11 +62,7 @@ struct XrState {
}
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn openxr_run(
running: Arc<AtomicBool>,
show_by_default: bool,
headless: bool,
) -> Result<(), BackendError> {
pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendError> {
let (xr_instance, system) = match helpers::init_xr() {
Ok((xr_instance, system)) => (xr_instance, system),
Err(e) => {
@@ -95,7 +90,7 @@ pub fn openxr_run(
if show_by_default {
app.tasks.enqueue_at(
TaskType::Manager(ManagerTask::ShowHide),
TaskType::Overlay(OverlayTask::ShowHide),
Instant::now().add(Duration::from_secs(1)),
);
}
@@ -178,7 +173,7 @@ pub fn openxr_run(
'main_loop: loop {
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.");
match xr_state.session.request_exit() {
Ok(()) => log::info!("OpenXR session exit requested."),
@@ -493,58 +488,16 @@ pub fn openxr_run(
app.tasks.retrieve_due(&mut due_tasks);
while let Some(task) = due_tasks.pop_front() {
match task {
TaskType::Overlay(sel, f) => {
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) => {
TaskType::Overlay(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")]
TaskType::WayVR(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);
//FIXME: Temporary workaround for Monado bug

View File

@@ -2,7 +2,9 @@ use glam::{Affine3A, Quat, Vec3A};
use libmonado::{Monado, Pose, ReferenceSpaceType};
use crate::{
backend::input::InputState, state::AppState, windowing::manager::OverlayWindowManager,
backend::{input::InputState, task::PlayspaceTask},
state::AppState,
windowing::manager::OverlayWindowManager,
};
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(
&mut self,
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),
}
pub enum PlayspaceTask {
ResetPlayspace,
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 enum ManagerTask {
pub enum OverlayTask {
AddSet,
ToggleSet(usize),
DeleteActiveSet,
ToggleEditMode,
ShowHide,
Modify(OverlaySelector, Box<ModifyOverlayTask>),
Create(OverlaySelector, Box<CreateOverlayTask>),
Drop(OverlaySelector),
}
pub enum TaskType {
Overlay(OverlaySelector, Box<OverlayTask>),
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>),
DropOverlay(OverlaySelector),
Manager(ManagerTask),
System(SystemTask),
Overlay(OverlayTask),
Playspace(PlayspaceTask),
#[cfg(feature = "openvr")]
OpenVR(OpenVrTask),
#[cfg(feature = "wayvr")]
WayVR(WayVRAction),
}

View File

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

View File

@@ -33,10 +33,7 @@ mod config_wayvr;
use std::{
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};
use clap::Parser;
@@ -45,6 +42,9 @@ use sysinfo::Pid;
use tracing::level_filters::LevelFilter;
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
#[derive(Default, Parser, Debug)]
#[command(version, about, long_about = None)]
@@ -82,11 +82,6 @@ struct Args {
/// Path to write logs to
#[arg(short, long, value_name = "FILE_PATH")]
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)]
@@ -118,21 +113,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return Ok(());
}
let running = Arc::new(AtomicBool::new(true));
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(())
}
#[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_vr = false;
@@ -140,7 +133,7 @@ fn auto_run(running: Arc<AtomicBool>, args: Args) {
if !args_get_openvr(&args) {
use crate::backend::{openxr::openxr_run, BackendError};
tried_xr = true;
match openxr_run(running.clone(), args.show, args.headless) {
match openxr_run(args.show, args.headless) {
Ok(()) => return,
Err(BackendError::NotSupported) => (),
Err(e) => {
@@ -154,7 +147,7 @@ fn auto_run(running: Arc<AtomicBool>, args: Args) {
if !args_get_openxr(&args) {
use crate::backend::{openvr::openvr_run, BackendError};
tried_vr = true;
match openvr_run(running, args.show, args.headless) {
match openvr_run(args.show, args.headless) {
Ok(()) => return,
Err(BackendError::NotSupported) => (),
Err(e) => {

View File

@@ -7,7 +7,7 @@ use wgui::{
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)]
pub(super) struct InteractLockHandler {
@@ -53,7 +53,7 @@ impl InteractLockHandler {
&mut self,
common: &mut CallbackDataCommon,
app: &mut AppState,
) -> Box<OverlayTask> {
) -> Box<ModifyOverlayTask> {
let defaults = app.wgui_globals.get().defaults.clone();
let rect_color = self.color;

View File

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

View File

@@ -7,7 +7,9 @@ use wgui::{
};
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"];
@@ -89,7 +91,7 @@ impl PositioningHandler {
&mut self,
common: &mut CallbackDataCommon,
key: &str,
) -> Box<OverlayTask> {
) -> Box<ModifyOverlayTask> {
self.change_highlight(common, key);
let pos = key_to_pos(key);

View File

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

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ use wlx_common::windowing::OverlayWindowState;
use crate::{
backend::{
input::{self, HoverResult},
task::TaskType,
task::{OverlayTask, TaskType},
wayvr::{
self, display,
server_ipc::{gen_args_vec, gen_env_vec},
@@ -332,12 +332,12 @@ where
WvrStateChanged::DashboardHidden
}));
app.tasks.enqueue(TaskType::Overlay(
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay_id),
Box::new(move |app, o| {
o.toggle(app);
}),
));
)));
Ok(())
}
@@ -429,12 +429,12 @@ where
.data
.state
.set_display_visible(display_handle, visible);
app.tasks.enqueue(TaskType::Overlay(
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(overlay_id),
Box::new(move |app, o| {
o.toggle(app);
}),
));
)));
}
}
wayvr::WayVRSignal::DisplayWindowLayout(display_handle, layout) => {
@@ -448,7 +448,9 @@ where
}
wayvr::WayVRSignal::DropOverlay(overlay_id) => {
app.tasks
.enqueue(TaskType::DropOverlay(OverlaySelector::Id(overlay_id)));
.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
overlay_id,
))));
}
wayvr::WayVRSignal::Haptics(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 slotmap::{HopSlotMap, Key, SecondaryMap};
use wlx_common::{config::SerializedWindowSet, overlays::ToastTopic};
use crate::{
backend::task::ManagerTask,
backend::task::OverlayTask,
overlays::{
anchor::create_anchor, edit::EditWrapperManager, keyboard::builder::create_keyboard,
screen::create_screens, toast::Toast, watch::create_watch,
@@ -18,6 +21,7 @@ use crate::{
window::{OverlayCategory, OverlayWindowData},
OverlayID, OverlaySelector,
},
FRAME_COUNTER,
};
pub const MAX_OVERLAY_SETS: usize = 7;
@@ -34,6 +38,7 @@ pub struct OverlayWindowManager<T> {
anchor_local: Affine3A,
watch_id: OverlayID,
edit_mode: bool,
dropped_overlays: VecDeque<OverlayWindowData<T>>,
}
impl<T> OverlayWindowManager<T>
@@ -52,6 +57,7 @@ where
anchor_local: Affine3A::from_translation(Vec3::NEG_Z),
watch_id: OverlayID::null(), // set down below
edit_mode: false,
dropped_overlays: VecDeque::with_capacity(8),
};
if headless {
@@ -119,9 +125,104 @@ where
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> {
pub fn pop_dropped(&mut self) -> Option<OverlayWindowData<T>> {
self.dropped_overlays.pop_front()
}
pub fn persist_layout(&mut self, app: &mut AppState) {
app.session.config.sets.clear();
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);
}
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<()> {
let mut meta = Vec::with_capacity(self.overlays.len());
for (id, data) in &self.overlays {