use core::cell::Cell;
use crate::{Context, Game, Graphics};
pub(crate) fn boot_rate() -> u32 {
BOOT_RATE.0.get()
}
struct BootRate(Cell<u32>);
unsafe impl Sync for BootRate {}
static BOOT_RATE: BootRate = BootRate(Cell::new(0));
#[doc(hidden)]
pub mod __internal {
use super::*;
use core::cell::UnsafeCell;
pub use crate::fmt::{format_args_to_buf, FmtBuf, LINE_CAP};
pub struct Slot<G>(UnsafeCell<Option<G>>);
unsafe impl<G> Sync for Slot<G> {}
impl<G> Slot<G>
where
G: Game,
{
pub const fn preset(game: G) -> Self {
Slot(UnsafeCell::new(Some(game)))
}
pub const fn new() -> Self {
Slot(UnsafeCell::new(None))
}
pub fn start(&self) {
hook();
self.boot();
}
pub fn init(&self, make: impl FnOnce() -> G) {
hook();
*self.get() = Some(make());
self.boot();
}
pub fn fps(&self) -> u32 {
G::FRAME_RATE.fps()
}
pub fn update(&self) {
if let Some(game) = self.get() {
game.update(&mut Context { _private: () });
}
}
pub fn draw(&self) {
if let Some(game) = self.get() {
game.draw(&mut Graphics { _private: () });
}
}
fn boot(&self) {
if let Some(game) = self.get() {
super::BOOT_RATE.0.set(G::FRAME_RATE.fps());
game.boot(&mut Context { _private: () });
super::BOOT_RATE.0.set(0);
}
}
#[allow(clippy::mut_from_ref)]
fn get(&self) -> &mut Option<G> {
unsafe { &mut *self.0.get() }
}
}
impl<G> Default for Slot<G>
where
G: Game,
{
fn default() -> Self {
Self::new()
}
}
fn hook() {
#[cfg(feature = "std")]
std::panic::set_hook(std::boxed::Box::new(|info| {
let msg = info.to_string();
unsafe { crate::ffi::panic(msg.as_ptr(), msg.len() as u32) };
}));
}
}
#[cfg(not(feature = "std"))]
#[panic_handler]
fn handle_panic(info: &core::panic::PanicInfo) -> ! {
use core::fmt::Write;
let mut buf = crate::fmt::FmtBuf::<256>::new();
let _ = write!(buf, "{info}");
let msg = buf.as_str();
unsafe { crate::ffi::panic(msg.as_ptr(), msg.len() as u32) };
#[cfg(target_arch = "wasm32")]
core::arch::wasm32::unreachable();
#[cfg(not(target_arch = "wasm32"))]
loop {}
}
#[cfg(test)]
mod tests {
use core::sync::atomic::{AtomicU32, Ordering};
use super::__internal::Slot;
use crate::{Context, FrameRate, Game, Graphics};
static SEEN_AT_BOOT: AtomicU32 = AtomicU32::new(u32::MAX);
struct HalfRate;
impl Game for HalfRate {
const FRAME_RATE: FrameRate = FrameRate::Fps30;
fn boot(&mut self, ctx: &mut Context) {
SEEN_AT_BOOT.store(ctx.fps() as u32, Ordering::Relaxed);
}
fn update(&mut self, _: &mut Context) {}
fn draw(&self, _: &mut Graphics) {}
}
#[test]
fn boot_is_answered_the_carts_own_rate() {
let slot: Slot<HalfRate> = Slot::new();
slot.init(|| HalfRate);
assert_eq!(SEEN_AT_BOOT.load(Ordering::Relaxed), 30);
assert_eq!(super::boot_rate(), 0, "the boot rate outlived boot");
assert_eq!(Context { _private: () }.fps(), 0.0);
}
}