diff --git a/api/sys/Cargo.toml b/api/sys/Cargo.toml index b9c752e7..be519d59 100644 --- a/api/sys/Cargo.toml +++ b/api/sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-sys" -version = "0.4.0" +version = "0.4.1" build = "src/build.rs" readme = "README.md" description = "Low-level Playdate API bindings" diff --git a/api/sys/src/build.rs b/api/sys/src/build.rs index dc917731..a0b246d1 100644 --- a/api/sys/src/build.rs +++ b/api/sys/src/build.rs @@ -30,6 +30,11 @@ const USE_BUILT_BINDINGS: &str = "PD_BUILD_BINDINGS_ONCE"; fn main() { println!("cargo:rerun-if-env-changed={SDK_PATH_ENV_VAR}"); + println!("cargo::rustc-check-cfg=cfg(playdate)"); + if matches!(Target::from_env_target(), Ok(Target::Playdate)) { + println!("cargo::rustc-cfg=playdate") + } + let mut cfg = Cfg::default(); cfg.derive.default = feature_derive_default(); cfg.derive.eq = feature_derive_eq(); diff --git a/api/sys/src/lib.rs b/api/sys/src/lib.rs index 56f2787e..72c29cea 100644 --- a/api/sys/src/lib.rs +++ b/api/sys/src/lib.rs @@ -70,10 +70,21 @@ pub extern "C" fn eventHandlerShim(api: *const ffi::PlaydateAPI, -> core::ffi::c_int { extern "Rust" { fn event_handler(api: *const ffi::PlaydateAPI, event: ffi::PDSystemEvent, arg: u32) -> EventLoopCtrl; + + #[cfg(not(playdate))] + // This is atomic in the `sys::proc::error`. + static END_WITH_ERR: core::sync::atomic::AtomicBool; } + if let ffi::PDSystemEvent::kEventInit = event { unsafe { API = api } } + + #[cfg(not(playdate))] + if unsafe { END_WITH_ERR.load(core::sync::atomic::Ordering::Relaxed) } { + return EventLoopCtrl::Stop.into(); + } + unsafe { event_handler(api, event, arg) }.into() } diff --git a/api/sys/src/sys/proc.rs b/api/sys/src/sys/proc.rs index 88238f19..31831cb5 100644 --- a/api/sys/src/sys/proc.rs +++ b/api/sys/src/sys/proc.rs @@ -12,17 +12,35 @@ pub fn abort() -> ! { core::intrinsics::abort() } /// In case of missed [`crate::sys::API`] (doesn't set) uses [`abort`]. #[track_caller] pub fn error>(text: S) -> ! { + #[cfg(not(playdate))] + { + use core::sync::atomic::AtomicBool; + use core::sync::atomic::Ordering; + + // This is unreachable on the device, + // `API.system.error` interrupts the execution. + // But simulator doesn't stops. + // So, instead of spin-loop just save the flag and use later in the event-handler. + + #[no_mangle] + static END_WITH_ERR: AtomicBool = AtomicBool::new(false); + + END_WITH_ERR.store(true, Ordering::Relaxed); + } + + if let Some(f) = unsafe { (*(*crate::sys::API).system).error } { + // Casting fn->void to fn->! + // This ugly cast is safe because of `!` is a magic compile-time marker, not a type. + let f: unsafe extern "C" fn(*const i8, ...) = f; + let p = core::ptr::addr_of!(f) as *const _ as *const unsafe extern "C" fn(*const i8, ...) -> !; + let f = unsafe { p.as_ref() }.unwrap(); + if let Ok(s) = alloc::ffi::CString::new(text.as_ref()) { unsafe { f(s.as_ptr() as *mut core::ffi::c_char) } } else { unsafe { f(text.as_ref().as_ptr() as *mut core::ffi::c_char) } } - loop { - // This is unreachable or the device, - // `API.system.error` interrupts the execution. - // But simulator doesn't stops on `error`, so just spin-loop here. - } } else { // In case of `crate::sys::API` is missed (doesn't set) just abort the process. abort()