use std::{
pin::pin,
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
use boppo_core::log;
use edge_executor::LocalExecutor;
use crate::internal::{HostEvent, buttons::broadcast_event, host_ffi};
use crate::internal::timer::{next_timeout, wake_and_clean_expired_timers};
const MAX_TASKS: usize = 32;
static EXECUTOR: AtomicPtr<LocalExecutor<'static, MAX_TASKS>> =
AtomicPtr::new(std::ptr::null_mut());
pub fn init() {
let executor = Box::leak(Box::new(LocalExecutor::<MAX_TASKS>::new()));
EXECUTOR.store(executor as *mut _, std::sync::atomic::Ordering::Relaxed);
boppo_core::internal::set_executor(executor);
}
fn executor() -> &'static LocalExecutor<'static, MAX_TASKS> {
unsafe { &*EXECUTOR.load(std::sync::atomic::Ordering::SeqCst) }
}
static WOKEN: AtomicBool = AtomicBool::new(false);
fn signal_waker() -> Waker {
static VTABLE: RawWakerVTable = RawWakerVTable::new(
|_| RawWaker::new(std::ptr::null(), &VTABLE),
|_| {
WOKEN.store(true, Ordering::Relaxed);
},
|_| {
WOKEN.store(true, Ordering::Relaxed);
},
|_| {},
);
unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &VTABLE)) }
}
pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
let mut top = pin!(executor().run(fut));
let waker = signal_waker();
let mut cx = Context::from_waker(&waker);
loop {
WOKEN.store(false, Ordering::Relaxed);
if let Poll::Ready(v) = top.as_mut().poll(&mut cx) {
return v;
}
let timeout = if WOKEN.load(Ordering::Relaxed) {
continue;
} else {
next_timeout()
};
let raw: Result<HostEvent, u8> = unsafe { host_ffi::boppo_poll(timeout) }.try_into();
match raw {
Ok(HostEvent::Button(e)) => broadcast_event(e),
Ok(HostEvent::Timeout) => wake_and_clean_expired_timers(),
Ok(HostEvent::FinishedAudio(controller_id)) => {
boppo_core::internal::on_sound_controller_finished(controller_id);
}
Ok(HostEvent::Exit) => {
std::process::exit(0);
}
Err(e) => log::debug!("skipping unknown host event: {e}"),
Ok(_) => unreachable!(), }
}
}