use std::cell::RefCell;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::sync::{Mutex, OnceLock};
use guinea_core::actor::{UiDispatcher, UiTask, set_ui_dispatcher};
struct ChannelDispatcher(Sender<UiTask>);
impl UiDispatcher for ChannelDispatcher {
fn init(&self) {}
fn dispatch(&self, task: UiTask) {
if self.0.send(task).is_ok() {
wake();
}
}
}
thread_local! {
static TASKS: RefCell<Option<Receiver<UiTask>>> = const { RefCell::new(None) };
}
fn waker() -> &'static Mutex<Option<egui::Context>> {
static WAKER: OnceLock<Mutex<Option<egui::Context>>> = OnceLock::new();
WAKER.get_or_init(|| Mutex::new(None))
}
pub(crate) fn install() {
static INIT: std::sync::Once = std::sync::Once::new();
INIT.call_once(|| {
let (tx, rx) = channel();
TASKS.with(|slot| *slot.borrow_mut() = Some(rx));
set_ui_dispatcher(ChannelDispatcher(tx));
});
}
pub(crate) fn wake_with(context: egui::Context) {
if let Ok(mut waker) = waker().lock() {
*waker = Some(context);
}
}
pub(crate) fn forget_waker() {
if let Ok(mut waker) = waker().lock() {
*waker = None;
}
}
fn wake() {
if let Ok(waker) = waker().lock()
&& let Some(context) = waker.as_ref()
{
context.request_repaint();
}
}
pub(crate) fn drain() -> bool {
let ready: Vec<UiTask> = TASKS.with(|slot| match slot.borrow().as_ref() {
Some(rx) => rx.try_iter().collect(),
None => Vec::new(),
});
let ran = !ready.is_empty();
for task in ready {
task();
}
ran
}