some cleanup of the messy overlay state machine

merge-notes
isark 2 years ago
parent 9c514830bc
commit 08f0da9746

@ -1,80 +1,41 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::{sync::Mutex, time::Duration};
use crossbeam::channel::Sender; use crossbeam::channel::Sender;
use overlay::{EnforceableEvent, Overlay}; use overlay::{Event, LockedOverlaySettings, Overlay};
use log::Level;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
// use overlay::{Overlay, Manager}; // use overlay::{Overlay, Manager};
use tauri::{LogicalSize, Manager, PhysicalPosition, PhysicalSize, Window, StateManager}; use tauri::Manager;
use underlayer::Bounds;
use x11rb::protocol::xproto::ConnectionExt;
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
mod overlay; mod overlay;
// fn show(window: &Window) {
// window.set_always_on_top(true);
// window.show();
// window.set_resizable(true);
// if let Ok(mut state) = window.state::<RuntimeWrapper>().lock() {
// if state.bounds.height == 0 || state.bounds.width == 0 {
// return;
// }
// window
// .set_position(PhysicalPosition {
// x: state.bounds.x,
// y: state.bounds.y,
// })
// .expect("msg");
// window
// .set_size(PhysicalSize {
// width: state.bounds.width,
// height: state.bounds.height,
// })
// .expect("msg");
// }
// std::thread::spawn(|| {
// std::thread::sleep(Duration::from_millis(50));
// underlayer::focus_target();
// });
// }
// fn hide(window: &Window) {
// window.hide();
// window.set_resizable(false);
// }
// type RuntimeWrapper = Mutex<Overlay>;
// remember to call `.manage(MyState::default())`
#[tauri::command] #[tauri::command]
fn set_interactable(interactable: bool, state: tauri::State<Sender<EnforceableEvent>>) { fn set_interactable(interactable: bool, state: tauri::State<Sender<Event>>) {
log::info!("set_interactable: {interactable:?}"); log::info!("set_interactable: {interactable:?}");
if interactable { if interactable {
state.send(EnforceableEvent::Force(overlay::StateEvent::Interactable)); state
.send(Event::State(overlay::State::Interactable{}))
.ok();
} else { } else {
state.send(EnforceableEvent::Return); state.send(Event::Return).ok();
} }
} }
#[tauri::command] #[tauri::command]
fn set_auto_hide(auto_hide: bool, state: tauri::State<Sender<EnforceableEvent>>) { fn set_auto_hide(auto_hide: bool, state: tauri::State<LockedOverlaySettings>) {
log::info!("set_auto_hide: {auto_hide:?}"); log::info!("set_auto_hide: {auto_hide:?}");
state.send(EnforceableEvent::AutoHide(auto_hide)); if let Ok(mut settings) = state.lock() {
settings.auto_hide = auto_hide;
}
} }
fn main() { fn main() {
SimpleLogger::new().with_module_level("underlayer", log::LevelFilter::Info).with_level(log::LevelFilter::Trace).init().expect("Could not init logger"); SimpleLogger::new()
.with_module_level("underlayer", log::LevelFilter::Info)
.with_level(log::LevelFilter::Trace)
.init()
.expect("Could not init logger");
tauri::Builder::default() tauri::Builder::default()
.setup(|app| { .setup(|app| {
@ -83,65 +44,7 @@ fn main() {
.expect("Could not get main overlay window"), .expect("Could not get main overlay window"),
"Untitled 1 - Mousepad", "Untitled 1 - Mousepad",
); );
app.manage(tx); app.manage(tx);
// std::thread::spawn(move || {
// log::info!("Running event thread");
// let mut target_is_focused = false;
// let underlay: std::sync::mpsc::Receiver<underlayer::UnderlayEvent> =
// underlayer::register("Untitled 1 - Mousepad");
// while let Ok(event) = underlay.recv() {
// log::info!("Got event: {event:?}");
// match event {
// underlayer::UnderlayEvent::Attach {
// bounds,
// has_access,
// is_fullscreen,
// } => {
// if let Ok(mut state) = window.state::<RuntimeWrapper>().lock() {
// state.bounds = bounds;
// }
// }
// underlayer::UnderlayEvent::Detach => {
// hide(&window);
// }
// underlayer::UnderlayEvent::MoveResize(bounds) => {
// if let Ok(mut state) = window.state::<RuntimeWrapper>().lock() {
// state.bounds = bounds;
// if bounds.height == 0 || bounds.width == 0 {
// return;
// }
// window
// .set_position(PhysicalPosition {
// x: bounds.x,
// y: bounds.y,
// })
// .expect("msg");
// window
// .set_size(PhysicalSize {
// width: bounds.width + 10,
// height: bounds.height,
// })
// .expect("msg");
// }
// }
// underlayer::UnderlayEvent::Focus => {
// target_is_focused = true;
// show(&window);
// }
// underlayer::UnderlayEvent::Blur => {
// target_is_focused = false;
// if !window.is_focused().unwrap() {
// hide(&window);
// }
// }
// underlayer::UnderlayEvent::X11FullscreenEvent { is_fullscreen } => {}
// }
// }
// });
Ok(()) Ok(())
}) })

@ -9,39 +9,35 @@ use std::{
}; };
use crossbeam::{ use crossbeam::{
channel::{Receiver, Sender}, channel::{Receiver, Select, Sender},
select, select,
}; };
use serde::Serialize;
// The prelude module re-exports the most common used items from statig. // The prelude module re-exports the most common used items from statig.
use statig::prelude::*; use statig::prelude::*;
use tauri::{PhysicalPosition, PhysicalSize, Window}; use tauri::{Manager, PhysicalPosition, PhysicalSize, Window};
use underlayer::{Bounds, UnderlayEvent}; use underlayer::{Bounds, UnderlayEvent};
use crate::overlay; use crate::overlay;
pub enum StateEvent { pub enum Event {
Visible, State(State),
Hidden,
Interactable,
}
pub enum EnforceableEvent {
Force(StateEvent),
Request(StateEvent),
Relax,
Bounds(Bounds), Bounds(Bounds),
AutoHide(bool), Return,
Return
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Overlay { pub struct Overlay {
bounds: Bounds, bounds: Bounds,
window: Window, window: Window,
auto_hide: bool,
previous: State, previous: State,
} }
pub struct OverlaySettings {
pub auto_hide: bool,
}
pub type LockedOverlaySettings = Mutex<OverlaySettings>;
fn wrap_underlay_rx(rx: MpscReceiver<UnderlayEvent>) -> Receiver<UnderlayEvent> { fn wrap_underlay_rx(rx: MpscReceiver<UnderlayEvent>) -> Receiver<UnderlayEvent> {
let (cb_underlay_tx, cb_underlay_rx) = crossbeam::channel::unbounded(); let (cb_underlay_tx, cb_underlay_rx) = crossbeam::channel::unbounded();
std::thread::spawn(move || { std::thread::spawn(move || {
@ -53,15 +49,18 @@ fn wrap_underlay_rx(rx: MpscReceiver<UnderlayEvent>) -> Receiver<UnderlayEvent>
} }
impl Overlay { impl Overlay {
pub fn new(window: Window, target_title: &str) -> Sender<EnforceableEvent> { pub fn new(window: Window, target_title: &str) -> Sender<Event> {
log::warn!("New overlay object"); log::warn!("New overlay object");
let mut overlay = Self { let mut overlay = Self {
window: window.clone(), window: window.clone(),
bounds: Bounds::default(), bounds: Bounds::default(),
auto_hide: true,
previous: State::hidden(), previous: State::hidden(),
}; };
window.manage(Mutex::new(OverlaySettings {
auto_hide: true,
}));
let mut fsm = Overlay::uninitialized_state_machine(overlay).init(); let mut fsm = Overlay::uninitialized_state_machine(overlay).init();
let underlay_rx = wrap_underlay_rx(underlayer::register(target_title)); let underlay_rx = wrap_underlay_rx(underlayer::register(target_title));
@ -88,7 +87,7 @@ impl Overlay {
recv(ee_rx) -> msg => { recv(ee_rx) -> msg => {
match msg { match msg {
Ok(event) => { Ok(event) => {
Self::handle_enforceable_event(&mut fsm, event); Self::handle_event(&mut fsm, event);
}, },
Err(_) => break, Err(_) => break,
} }
@ -107,60 +106,54 @@ impl Overlay {
fn handle_overlay_focused(fsm: &mut InitializedStateMachine<Overlay>, focus: bool) { fn handle_overlay_focused(fsm: &mut InitializedStateMachine<Overlay>, focus: bool) {
match fsm.state() { match fsm.state() {
State::Visible {} | State::EnforcedVisible {} => { State::Visible {} => {
if focus { if focus {
fsm.window.set_ignore_cursor_events(true); fsm.window.set_ignore_cursor_events(true);
underlayer::focus_target(); underlayer::focus_target();
} }
} },
_ => {} _ => {}
} }
} }
fn handle_underlay_event(fsm: &mut InitializedStateMachine<Overlay>, event: UnderlayEvent) { fn handle_underlay_event(fsm: &mut InitializedStateMachine<Overlay>, event: UnderlayEvent) {
log::info!("EVENT: {event:?}"); let settings = fsm.window.state::<LockedOverlaySettings>();
match event { match event {
UnderlayEvent::Attach { UnderlayEvent::Attach {
bounds, bounds,
has_access, has_access,
is_fullscreen, is_fullscreen,
} => { } => {
fsm.handle(&EnforceableEvent::Bounds(bounds)); fsm.handle(&Event::Bounds(bounds));
}
UnderlayEvent::MoveResize(bounds) => fsm.handle(&Event::Bounds(bounds)),
UnderlayEvent::Detach => fsm.handle(&Event::State(State::hidden())),
UnderlayEvent::Focus => {
fsm.handle(&Event::State(State::Visible {}))
} }
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 => { UnderlayEvent::Blur => {
if !fsm.window.is_focused().unwrap() && fsm.auto_hide { if !fsm.window.is_focused().unwrap()
fsm.handle(&EnforceableEvent::Request(StateEvent::Hidden)) && fsm
.window
.state::<LockedOverlaySettings>()
.lock()
.is_ok_and(|s| s.auto_hide)
{
fsm.handle(&Event::State(State::Hidden {}))
} }
} }
UnderlayEvent::X11FullscreenEvent { is_fullscreen } => {} UnderlayEvent::X11FullscreenEvent { is_fullscreen } => {}
} }
} }
fn handle_enforceable_event( fn handle_event(fsm: &mut InitializedStateMachine<Overlay>, event: Event) {
fsm: &mut InitializedStateMachine<Overlay>,
event: EnforceableEvent,
) {
fsm.handle(&event); 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( #[state_machine(
initial = "State::hidden()", initial = "State::hidden()",
state(derive(Debug, Clone, PartialEq, Eq)), state(derive(Debug, Clone, PartialEq, Eq, Serialize)),
superstate(derive(Debug)), superstate(derive(Debug)),
on_transition = "Self::on_transition" on_transition = "Self::on_transition"
)] )]
@ -177,114 +170,68 @@ impl Overlay {
.set_size(PhysicalSize::new(self.bounds.width, self.bounds.height)); .set_size(PhysicalSize::new(self.bounds.width, self.bounds.height));
} }
log::info!("on_enter_visible"); log::trace!("on_enter_visible");
} }
#[action] #[action]
fn on_enter_interactable(&mut self) { fn on_enter_interactable(&mut self) {
log::info!("on_enter_interactable"); log::trace!("on_enter_interactable");
self.window.set_ignore_cursor_events(false); self.window.set_ignore_cursor_events(false);
self.window.set_focus();
} }
#[action] #[action]
fn on_enter_hidden(&mut self) { fn on_enter_hidden(&mut self) {
log::info!("on_enter_hidden"); log::trace!("on_enter_hidden");
self.window.hide(); self.window.hide();
self.window.set_resizable(false); self.window.set_resizable(false);
} }
#[action] #[action]
fn on_exit_interactable(&mut self) { fn on_exit_interactable(&mut self) {
log::info!("on_exit_interactable"); log::trace!("on_exit_interactable");
self.window.set_ignore_cursor_events(true); self.window.set_ignore_cursor_events(true);
} }
#[state(superstate = "super_visible")] #[state(superstate = "super_visible")]
fn enforced_visible(&mut self, event: &EnforceableEvent) -> Response<State> { fn visible(&mut self, event: &Event) -> Response<State> {
match event { match event {
EnforceableEvent::Force(StateEvent::Visible) => Super, Event::State(underlying) => match underlying {
EnforceableEvent::Force(event) => Self::match_forced(event), State::Visible {} => Handled,
EnforceableEvent::Request(_) => Handled, State::Hidden {} => Transition(State::hidden()),
EnforceableEvent::Relax => Transition(State::visible()), State::Interactable {} => Transition(State::interactable()),
_ => Super,
}
}
#[state(superstate = "super_visible")]
fn visible(&mut self, event: &EnforceableEvent) -> Response<State> {
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<State> {
match event {
EnforceableEvent::Force(StateEvent::Hidden) => Super,
EnforceableEvent::Force(event) => Self::match_forced(event),
EnforceableEvent::Request(_) => Handled,
EnforceableEvent::Relax => Transition(State::hidden()),
_ => Super, _ => Super,
} }
} }
#[state(superstate = "super_hidden")] #[state(superstate = "super_hidden")]
fn hidden(&mut self, event: &EnforceableEvent) -> Response<State> { fn hidden(&mut self, event: &Event) -> Response<State> {
match event { match event {
EnforceableEvent::Force(underlying) => Self::match_forced(underlying), Event::State(underlying) => match underlying {
EnforceableEvent::Request(underlying) => match underlying { State::Visible {} => Transition(State::visible()),
StateEvent::Visible => Transition(State::visible()), State::Hidden {} => Handled,
StateEvent::Hidden => Super, State::Interactable {} => Transition(State::interactable()),
StateEvent::Interactable => Transition(State::interactable()),
}, },
EnforceableEvent::Relax => Super,
_ => Super,
}
}
#[state(superstate = "super_interactable")]
fn enforced_interactable(&mut self, event: &EnforceableEvent) -> Response<State> {
match event {
EnforceableEvent::Force(StateEvent::Interactable) => Super,
EnforceableEvent::Force(event) => Self::match_forced(event),
EnforceableEvent::Request(_) => Handled,
EnforceableEvent::Relax => Transition(State::interactable()),
_ => Super, _ => Super,
} }
} }
#[state(superstate = "super_interactable")] #[state(superstate = "super_interactable")]
fn interactable(&mut self, event: &EnforceableEvent) -> Response<State> { fn interactable(&mut self, event: &Event) -> Response<State> {
match event { match event {
EnforceableEvent::Force(underlying) => Self::match_forced(underlying), Event::State(underlying) => match underlying {
EnforceableEvent::Request(underlying) => match underlying { State::Visible {} => Transition(State::visible()),
StateEvent::Visible => Transition(State::visible()), State::Hidden {} => Transition(State::hidden()),
StateEvent::Hidden => Transition(State::hidden()), State::Interactable {} => Handled,
StateEvent::Interactable => Super,
}, },
EnforceableEvent::Relax => Super,
_ => Super, _ => Super,
} }
} }
fn match_forced(event: &StateEvent) -> Response<State> {
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")] #[superstate(superstate = "common", entry_action = "on_enter_visible")]
fn super_visible(&mut self, event: &EnforceableEvent) -> Response<State> { fn super_visible(&mut self, event: &Event) -> Response<State> {
log::info!("in super_visible"); log::trace!("in super_visible");
Super Super
} }
@ -293,8 +240,8 @@ impl Overlay {
entry_action = "on_enter_interactable", entry_action = "on_enter_interactable",
exit_action = "on_exit_interactable" exit_action = "on_exit_interactable"
)] )]
fn super_interactable(&mut self, event: &EnforceableEvent) -> Response<State> { fn super_interactable(&mut self, event: &Event) -> Response<State> {
log::info!("in super_interactable"); log::trace!("in super_interactable");
Super Super
} }
@ -303,16 +250,16 @@ impl Overlay {
entry_action = "on_visible", entry_action = "on_visible",
entry_action = "on_enter_hidden" entry_action = "on_enter_hidden"
)] )]
fn super_hidden(&mut self, event: &EnforceableEvent) -> Response<State> { fn super_hidden(&mut self, event: &Event) -> Response<State> {
log::info!("in super_hidden"); log::trace!("in super_hidden");
Super Super
} }
#[superstate] #[superstate]
fn common(&mut self, event: &EnforceableEvent) -> Response<State> { fn common(&mut self, event: &Event) -> Response<State> {
log::info!("in common"); log::trace!("in common");
match event { match event {
EnforceableEvent::Bounds(bounds) => { Event::Bounds(bounds) => {
self.bounds = *bounds; self.bounds = *bounds;
if bounds.width > 0 && bounds.height > 0 { if bounds.width > 0 && bounds.height > 0 {
self.window self.window
@ -322,21 +269,19 @@ impl Overlay {
} }
Super Super
} }
EnforceableEvent::Relax => Super, Event::Return => {
EnforceableEvent::AutoHide(auto_hide) => { log::trace!("Returning to: {:?}", self.previous);
self.auto_hide = *auto_hide;
Super
}
EnforceableEvent::Return => {
log::info!("Returning to: {:?}", self.previous);
Transition(self.previous.clone()) Transition(self.previous.clone())
} }
_ => Super, _ => Super,
} }
} }
fn on_transition(&mut self, src: &State, dest: &State) { fn on_transition(&mut self, src: &State, dest: &State) {
log::info!("from {src:?} to {dest:?}"); log::trace!("from {src:?} to {dest:?}");
self.previous = src.clone(); if src != dest {
self.previous = src.clone();
}
self.window.emit("OverlayStateChange", dest);
} }
} }

@ -2,6 +2,7 @@ import { AfterViewInit, Component, OnInit } from "@angular/core";
import { app } from "@tauri-apps/api"; import { app } from "@tauri-apps/api";
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from "@tauri-apps/api/tauri";
import { register } from "@tauri-apps/api/globalShortcut"; import { register } from "@tauri-apps/api/globalShortcut";
import { listen } from '@tauri-apps/api/event';
@Component({ @Component({
selector: "app-root", selector: "app-root",
@ -19,6 +20,10 @@ export class AppComponent implements OnInit {
}).then(); }).then();
invoke("set_auto_hide", {auto_hide: this.auto_hide}).then(); invoke("set_auto_hide", {auto_hide: this.auto_hide}).then();
listen('OverlayStateChange', (event) => {
console.log(event, 'OverlayStateChange');
}).then();
} }
toggleAutoHide() { toggleAutoHide() {

Loading…
Cancel
Save