use crate::fiber::FiberId;
use crossterm::event::Event;
use once_cell::sync::Lazy;
use std::sync::{Arc, RwLock};
use tracing::debug;
#[derive(Default)]
pub struct EventState {
pub(crate) event: Option<Arc<Event>>,
pub(crate) propagation_stopped: bool,
pub(crate) stopped_by_fiber: Option<FiberId>,
}
impl EventState {
pub fn new() -> Self {
Self::default()
}
pub fn has_event(&self) -> bool {
self.event.is_some()
}
pub fn reset_propagation(&mut self) {
self.propagation_stopped = false;
self.stopped_by_fiber = None;
}
}
pub(crate) static CURRENT_EVENT: Lazy<RwLock<EventState>> = Lazy::new(Default::default);
pub fn set_current_event(event: Option<Arc<Event>>) {
let event_debug = event.clone();
let mut current_event = CURRENT_EVENT.write().unwrap();
current_event.event = event;
current_event.reset_propagation();
debug!("Set current event: {:?}, propagation reset", event_debug);
}
pub fn get_current_event() -> Option<Arc<Event>> {
use crate::fiber_tree::with_current_fiber;
let event_state = CURRENT_EVENT.read().unwrap();
let event = match event_state.event.as_ref() {
Some(e) => e.clone(),
None => {
debug!("No event available");
return None;
}
};
if event_state.propagation_stopped {
let current_fiber_id = with_current_fiber(|fiber| fiber.id);
if current_fiber_id != event_state.stopped_by_fiber {
debug!("Propagation stopped by another fiber");
return None;
}
}
Some(event)
}
pub fn stop_event_propagation() {
use crate::fiber_tree::with_current_fiber;
let current_fiber_id = with_current_fiber(|fiber| fiber.id);
let mut event_state = CURRENT_EVENT.write().unwrap();
event_state.propagation_stopped = true;
event_state.stopped_by_fiber = current_fiber_id;
debug!("Propagation stopped by fiber {:?}", current_fiber_id);
}
pub fn clear_current_event() {
set_current_event(None);
}
pub fn peek_current_event() -> Option<Arc<Event>> {
let event_state = CURRENT_EVENT.read().unwrap();
event_state.event.clone()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fiber_tree::{FiberTree, clear_fiber_tree, set_fiber_tree, with_fiber_tree_mut};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
pub(super) static TEST_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
fn create_test_key_event(c: char) -> Event {
Event::Key(KeyEvent::new_with_kind(
KeyCode::Char(c),
KeyModifiers::NONE,
KeyEventKind::Press,
))
}
fn setup_test_fiber() -> FiberId {
let mut tree = FiberTree::new();
let fiber_id = tree.mount(None, None);
tree.begin_render(fiber_id);
set_fiber_tree(tree);
fiber_id
}
fn teardown_test_fiber() {
with_fiber_tree_mut(|tree| {
tree.end_render();
});
clear_fiber_tree();
}
#[test]
fn test_set_and_get_event() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
setup_test_fiber();
let event = create_test_key_event('a');
set_current_event(Some(Arc::new(event.clone())));
let retrieved = get_current_event();
assert!(retrieved.is_some());
if let Some(e) = retrieved {
if let Event::Key(key) = &*e {
assert_eq!(key.code, KeyCode::Char('a'));
} else {
panic!("Expected Key event");
}
}
teardown_test_fiber();
}
#[test]
fn test_event_available_multiple_times_per_fiber() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
setup_test_fiber();
let event = create_test_key_event('b');
set_current_event(Some(Arc::new(event)));
let first = get_current_event();
assert!(first.is_some());
let second = get_current_event();
assert!(second.is_some());
let third = get_current_event();
assert!(third.is_some());
teardown_test_fiber();
}
#[test]
fn test_different_fibers_can_read_same_event() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
let mut tree = FiberTree::new();
let fiber1 = tree.mount(None, None);
let fiber2 = tree.mount(None, None);
set_fiber_tree(tree);
let event = create_test_key_event('c');
set_current_event(Some(Arc::new(event)));
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber1);
});
let result1 = get_current_event();
assert!(result1.is_some());
with_fiber_tree_mut(|tree| {
tree.end_render();
});
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber2);
});
let result2 = get_current_event();
assert!(result2.is_some());
with_fiber_tree_mut(|tree| {
tree.end_render();
});
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber1);
});
let result1_again = get_current_event();
assert!(result1_again.is_some());
with_fiber_tree_mut(|tree| {
tree.end_render();
});
clear_fiber_tree();
}
#[test]
fn test_stop_propagation_blocks_other_fibers() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
let mut tree = FiberTree::new();
let fiber1 = tree.mount(None, None);
let fiber2 = tree.mount(None, None);
set_fiber_tree(tree);
let event = create_test_key_event('d');
set_current_event(Some(Arc::new(event)));
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber1);
});
let result1 = get_current_event();
assert!(result1.is_some());
stop_event_propagation();
with_fiber_tree_mut(|tree| {
tree.end_render();
});
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber2);
});
let result2 = get_current_event();
assert!(result2.is_none());
with_fiber_tree_mut(|tree| {
tree.end_render();
});
clear_fiber_tree();
}
#[test]
fn test_stop_propagation_allows_same_fiber() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
let fiber_id = setup_test_fiber();
let event = create_test_key_event('e');
set_current_event(Some(Arc::new(event)));
let result1 = get_current_event();
assert!(result1.is_some());
stop_event_propagation();
let result2 = get_current_event();
assert!(result2.is_some());
with_fiber_tree_mut(|tree| {
assert!(tree.get(fiber_id).is_some());
});
teardown_test_fiber();
}
#[test]
fn test_clear_event() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
setup_test_fiber();
let event = create_test_key_event('f');
set_current_event(Some(Arc::new(event)));
assert!(peek_current_event().is_some());
clear_current_event();
assert!(peek_current_event().is_none());
teardown_test_fiber();
}
#[test]
fn test_new_event_resets_propagation_state() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
let mut tree = FiberTree::new();
let fiber1 = tree.mount(None, None);
let fiber2 = tree.mount(None, None);
set_fiber_tree(tree);
let event1 = create_test_key_event('g');
set_current_event(Some(Arc::new(event1)));
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber1);
});
stop_event_propagation();
with_fiber_tree_mut(|tree| {
tree.end_render();
});
let event2 = create_test_key_event('h');
set_current_event(Some(Arc::new(event2)));
with_fiber_tree_mut(|tree| {
tree.begin_render(fiber2);
});
let result = get_current_event();
assert!(result.is_some());
with_fiber_tree_mut(|tree| {
tree.end_render();
});
clear_fiber_tree();
}
#[test]
fn test_peek_does_not_affect_propagation() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
setup_test_fiber();
let event = create_test_key_event('i');
set_current_event(Some(Arc::new(event)));
let peeked = peek_current_event();
assert!(peeked.is_some());
let retrieved = get_current_event();
assert!(retrieved.is_some());
stop_event_propagation();
let peeked_after = peek_current_event();
assert!(peeked_after.is_some());
teardown_test_fiber();
}
#[test]
fn test_event_state_helpers() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
let state = CURRENT_EVENT.read().unwrap();
assert!(!state.has_event());
drop(state);
let event = create_test_key_event('j');
set_current_event(Some(Arc::new(event)));
let state = CURRENT_EVENT.read().unwrap();
assert!(state.has_event());
assert!(!state.propagation_stopped);
assert!(state.stopped_by_fiber.is_none());
}
#[test]
fn test_propagation_state_reset() {
let _lock = TEST_MUTEX.lock();
clear_current_event();
let mut state = EventState::new();
state.propagation_stopped = true;
state.stopped_by_fiber = Some(FiberId(42));
state.reset_propagation();
assert!(!state.propagation_stopped);
assert!(state.stopped_by_fiber.is_none());
}
}