#![allow(unused)] use std::{ borrow::BorrowMut, cell::RefCell, ops::DerefMut, sync::{mpsc::Receiver as MpscReceiver, Arc, Mutex}, time::Duration, }; use crossbeam::{ channel::{Receiver, Sender}, select, }; // The prelude module re-exports the most common used items from statig. use statig::prelude::*; use tauri::{PhysicalPosition, PhysicalSize, Window}; use underlayer::{Bounds, UnderlayEvent}; use crate::overlay; pub enum StateEvent { Visible, Hidden, Interactable, } pub enum EnforceableEvent { Force(StateEvent), Request(StateEvent), Relax, Bounds(Bounds), AutoHide(bool), Return } #[derive(Clone)] pub struct Overlay { bounds: Bounds, window: Window, auto_hide: bool, previous: State, } fn wrap_underlay_rx(rx: MpscReceiver) -> Receiver { let (cb_underlay_tx, cb_underlay_rx) = crossbeam::channel::unbounded(); std::thread::spawn(move || { while let Ok(event) = rx.recv() { cb_underlay_tx.send(event).ok(); } }); cb_underlay_rx } impl Overlay { pub fn new(window: Window, target_title: &str) -> Sender { log::warn!("New overlay object"); let mut overlay = Self { window: window.clone(), bounds: Bounds::default(), auto_hide: true, previous: State::hidden(), }; let mut fsm = Overlay::uninitialized_state_machine(overlay).init(); let underlay_rx = wrap_underlay_rx(underlayer::register(target_title)); let (ee_tx, ee_rx) = crossbeam::channel::unbounded(); let (window_event_tx, window_event_rx) = crossbeam::channel::unbounded(); window.on_window_event(move |event| match event { tauri::WindowEvent::Focused(focus) => { window_event_tx.send(*focus).ok(); } _ => {} }); std::thread::spawn(move || loop { select! { recv(underlay_rx) -> msg => { match msg { Ok(event) => { Self::handle_underlay_event(&mut fsm, event); }, Err(_) => break, } }, recv(ee_rx) -> msg => { match msg { Ok(event) => { Self::handle_enforceable_event(&mut fsm, event); }, Err(_) => break, } }, recv(window_event_rx) -> msg => { match msg { Ok(focus) => Self::handle_overlay_focused(&mut fsm, focus), Err(_) => break, } } } }); ee_tx } fn handle_overlay_focused(fsm: &mut InitializedStateMachine, focus: bool) { match fsm.state() { State::Visible {} | State::EnforcedVisible {} => { if focus { fsm.window.set_ignore_cursor_events(true); underlayer::focus_target(); } } _ => {} } } fn handle_underlay_event(fsm: &mut InitializedStateMachine, event: UnderlayEvent) { log::info!("EVENT: {event:?}"); match event { UnderlayEvent::Attach { bounds, has_access, is_fullscreen, } => { fsm.handle(&EnforceableEvent::Bounds(bounds)); } UnderlayEvent::MoveResize(bounds) => fsm.handle(&EnforceableEvent::Bounds(bounds)), UnderlayEvent::Detach => fsm.handle(&EnforceableEvent::Request(StateEvent::Hidden)), UnderlayEvent::Focus => fsm.handle(&EnforceableEvent::Request(StateEvent::Visible)), UnderlayEvent::Blur => { if !fsm.window.is_focused().unwrap() && fsm.auto_hide { fsm.handle(&EnforceableEvent::Request(StateEvent::Hidden)) } } UnderlayEvent::X11FullscreenEvent { is_fullscreen } => {} } } fn handle_enforceable_event( fsm: &mut InitializedStateMachine, event: EnforceableEvent, ) { fsm.handle(&event); } } // let mut fsm = fsm::Overlay::uninitialized_state_machine(Default::default()).init(); // fsm.handle(&EnforceableEvent::Force(fsm::StateEvent::Visible)); // fsm.handle(&EnforceableEvent::Relax); // fsm.handle(&EnforceableEvent::Request(fsm::StateEvent::Interactable)); // fsm.handle(&EnforceableEvent::Request(fsm::StateEvent::Hidden)); /// The `state_machine` procedural macro generates the `State` and `Superstate` /// enums by parsing the function signatures with a `state`, `superstate` or /// `action` attribute. It also implements the `statig::State` and /// `statig::Superstate` traits. We also pass an argument that will add the /// derive macro with the Debug trait to the `State` enum. #[state_machine( initial = "State::hidden()", state(derive(Debug, Clone, PartialEq, Eq)), superstate(derive(Debug)), on_transition = "Self::on_transition" )] impl Overlay { #[action] fn on_enter_visible(&mut self) { self.window.set_always_on_top(true); self.window.show(); self.window.set_resizable(true); if self.bounds.width > 0 && self.bounds.height > 0 { self.window .set_position(PhysicalPosition::new(self.bounds.x, self.bounds.y)); self.window .set_size(PhysicalSize::new(self.bounds.width, self.bounds.height)); } log::info!("on_enter_visible"); } #[action] fn on_enter_interactable(&mut self) { log::info!("on_enter_interactable"); self.window.set_ignore_cursor_events(false); } #[action] fn on_enter_hidden(&mut self) { log::info!("on_enter_hidden"); self.window.hide(); self.window.set_resizable(false); } #[action] fn on_exit_interactable(&mut self) { log::info!("on_exit_interactable"); self.window.set_ignore_cursor_events(true); } #[state(superstate = "super_visible")] fn enforced_visible(&mut self, event: &EnforceableEvent) -> Response { match event { EnforceableEvent::Force(StateEvent::Visible) => Super, EnforceableEvent::Force(event) => Self::match_forced(event), EnforceableEvent::Request(_) => Handled, EnforceableEvent::Relax => Transition(State::visible()), _ => Super, } } #[state(superstate = "super_visible")] fn visible(&mut self, event: &EnforceableEvent) -> Response { match event { EnforceableEvent::Force(underlying) => Self::match_forced(underlying), EnforceableEvent::Request(underlying) => match underlying { StateEvent::Visible => Super, StateEvent::Hidden => Transition(State::hidden()), StateEvent::Interactable => Transition(State::interactable()), }, EnforceableEvent::Relax => Super, _ => Super, } } #[state(superstate = "super_hidden")] fn enforced_hidden(&mut self, event: &EnforceableEvent) -> Response { match event { EnforceableEvent::Force(StateEvent::Hidden) => Super, EnforceableEvent::Force(event) => Self::match_forced(event), EnforceableEvent::Request(_) => Handled, EnforceableEvent::Relax => Transition(State::hidden()), _ => Super, } } #[state(superstate = "super_hidden")] fn hidden(&mut self, event: &EnforceableEvent) -> Response { match event { EnforceableEvent::Force(underlying) => Self::match_forced(underlying), EnforceableEvent::Request(underlying) => match underlying { StateEvent::Visible => Transition(State::visible()), StateEvent::Hidden => Super, StateEvent::Interactable => Transition(State::interactable()), }, EnforceableEvent::Relax => Super, _ => Super, } } #[state(superstate = "super_interactable")] fn enforced_interactable(&mut self, event: &EnforceableEvent) -> Response { match event { EnforceableEvent::Force(StateEvent::Interactable) => Super, EnforceableEvent::Force(event) => Self::match_forced(event), EnforceableEvent::Request(_) => Handled, EnforceableEvent::Relax => Transition(State::interactable()), _ => Super, } } #[state(superstate = "super_interactable")] fn interactable(&mut self, event: &EnforceableEvent) -> Response { match event { EnforceableEvent::Force(underlying) => Self::match_forced(underlying), EnforceableEvent::Request(underlying) => match underlying { StateEvent::Visible => Transition(State::visible()), StateEvent::Hidden => Transition(State::hidden()), StateEvent::Interactable => Super, }, EnforceableEvent::Relax => Super, _ => Super, } } fn match_forced(event: &StateEvent) -> Response { match event { StateEvent::Hidden => Transition(State::enforced_hidden()), StateEvent::Interactable => Transition(State::enforced_interactable()), StateEvent::Visible => Transition(State::enforced_visible()), } } #[superstate(superstate = "common", entry_action = "on_enter_visible")] fn super_visible(&mut self, event: &EnforceableEvent) -> Response { log::info!("in super_visible"); Super } #[superstate( superstate = "super_visible", entry_action = "on_enter_interactable", exit_action = "on_exit_interactable" )] fn super_interactable(&mut self, event: &EnforceableEvent) -> Response { log::info!("in super_interactable"); Super } #[superstate( superstate = "common", entry_action = "on_visible", entry_action = "on_enter_hidden" )] fn super_hidden(&mut self, event: &EnforceableEvent) -> Response { log::info!("in super_hidden"); Super } #[superstate] fn common(&mut self, event: &EnforceableEvent) -> Response { log::info!("in common"); match event { EnforceableEvent::Bounds(bounds) => { self.bounds = *bounds; if bounds.width > 0 && bounds.height > 0 { self.window .set_position(PhysicalPosition::new(bounds.x, bounds.y)); self.window .set_size(PhysicalSize::new(bounds.width, bounds.height)); } Super } EnforceableEvent::Relax => Super, EnforceableEvent::AutoHide(auto_hide) => { self.auto_hide = *auto_hide; Super } EnforceableEvent::Return => { log::info!("Returning to: {:?}", self.previous); Transition(self.previous.clone()) } _ => Super, } } fn on_transition(&mut self, src: &State, dest: &State) { log::info!("from {src:?} to {dest:?}"); self.previous = src.clone(); } }