playdate_sys/
lib.rs

1// https://doc.rust-lang.org/core/ffi/index.html
2// https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
3#![cfg_attr(not(test), no_std)]
4#![allow(internal_features)]
5#![feature(lang_items)]
6#![feature(core_intrinsics)]
7// allocator
8#![cfg_attr(feature = "allocator", feature(alloc_error_handler))]
9#![cfg_attr(feature = "allocator", feature(allocator_api))]
10#![cfg_attr(feature = "allocator", feature(alloc_layout_extra))]
11// error
12#![cfg_attr(feature = "try-trait-v2", feature(try_trait_v2))]
13// experimental features
14#![cfg_attr(feature = "bindings-derive-constparamty",
15            feature(adt_const_params),
16            allow(incomplete_features))]
17
18
19#[allow(unused_imports)]
20#[macro_use]
21pub extern crate alloc;
22
23pub mod log;
24pub mod traits;
25
26mod sys;
27pub use sys::*;
28
29
30pub mod ffi {
31	#![allow(non_upper_case_globals)]
32	#![allow(non_camel_case_types)]
33	#![allow(non_snake_case)]
34	#![cfg_attr(test, allow(deref_nullptr))]
35	#![allow(clippy::all, clippy::pedantic, clippy::nursery)]
36	//! Low-level Playdate C-API.
37	//!
38	#![doc = concat!("\nSDK version: `", env!("PD_SDK_VERSION"), "`\n")]
39	//!
40	//! Original official docs: [latest][], [current][].
41	//!
42	//! [latest]: https://sdk.play.date/Inside%20Playdate%20with%20C.html
43	#![doc = concat!("[current]: https://sdk.play.date/", env!("PD_SDK_VERSION"), "/Inside%20Playdate%20with%20C.html")]
44	//!
45
46	// include bindings:
47	include!(env!("PD_BINDINGS_PATH"));
48
49	/// Preferred `CString` to use.
50	pub use alloc::ffi::CString;
51	/// Preferred `CStr` to use.
52	pub use core::ffi::CStr;
53	pub use core::str::Utf8Error;
54}
55
56
57#[cfg(feature = "entry-point")]
58#[no_mangle]
59/// Simple minimal proxy entry point.
60/// Registers API endpoint when called with `event` matches `PDSystemEvent::kEventInit`.
61/// It needed for allocators and panic handler.
62///
63/// Linking requires rust-abi symbol `event_handler: fn(*const Playdate, PDSystemEvent, arg: u32) -> c_int`
64pub extern "C" fn eventHandlerShim(api: *const ffi::PlaydateAPI,
65                                   event: ffi::PDSystemEvent,
66                                   arg: u32)
67                                   -> core::ffi::c_int {
68	extern "Rust" {
69		fn event_handler(api: *const ffi::PlaydateAPI, event: ffi::PDSystemEvent, arg: u32) -> EventLoopCtrl;
70
71		#[cfg(not(playdate))]
72		// This is atomic in the `sys::proc::error`.
73		static END_WITH_ERR: core::sync::atomic::AtomicBool;
74	}
75
76	if let ffi::PDSystemEvent::kEventInit = event {
77		unsafe { API = api }
78	}
79
80	#[cfg(not(playdate))]
81	if unsafe { END_WITH_ERR.load(core::sync::atomic::Ordering::Relaxed) } {
82		return EventLoopCtrl::Stop.into();
83	}
84
85	unsafe { event_handler(api, event, arg) }.into()
86}
87
88#[cfg(feature = "entry-point")]
89pub use entry_point_ctrl::*;
90#[cfg(feature = "entry-point")]
91mod entry_point_ctrl {
92	use core::ffi::c_int;
93
94
95	#[repr(i32)]
96	pub enum EventLoopCtrl {
97		Continue = 0,
98		Stop = 1,
99	}
100
101	impl EventLoopCtrl {
102		pub const fn into(self) -> c_int { self as _ }
103	}
104
105	impl From<bool> for EventLoopCtrl {
106		fn from(value: bool) -> Self { if value { Self::Continue } else { Self::Stop } }
107	}
108
109	impl<T, E> From<Result<T, E>> for EventLoopCtrl {
110		fn from(res: Result<T, E>) -> Self { if res.is_ok() { Self::Continue } else { Self::Stop } }
111	}
112
113	#[cfg(feature = "try-trait-v2")]
114	mod impl_trait_v2 {
115		use super::*;
116		use core::fmt::Display;
117		use core::ops::FromResidual;
118		use core::convert::Infallible;
119
120		impl<E: Display> FromResidual<Result<Infallible, E>> for EventLoopCtrl {
121			#[track_caller]
122			fn from_residual(residual: Result<Infallible, E>) -> Self {
123				let Err(err) = residual;
124				crate::println!("Error: {err}");
125				// panic!("{err}");
126				Self::Stop
127			}
128		}
129	}
130}
131
132
133pub mod info {
134	//! Build info.
135	/// Version of the Playdate SDK used for build.
136	pub const SDK_VERSION: &str = env!("PD_SDK_VERSION");
137}
138
139
140/// Needed to build debug & executable binaries.
141#[cfg(feature = "eh-personality")]
142#[lang = "eh_personality"]
143#[cfg(all(not(test), not(doc)))]
144// #[cfg(not(target = "thumbv7em-none-eabihf"))]
145extern "C" fn eh_personality() {}
146
147pub mod misc {
148	#[macro_export]
149	/// Adds low-level symbols required by gcc for unwinding & exceptions (if `-fno-exceptions` or `-fno-rtti` not set).
150	///
151	/// There's just dummy- empty- no-op- implementation.
152	/// Anyway these symbols will be removed at the final (thanks DCE, LTO, linking with SDK/link_map.ld).
153	macro_rules! ll_symbols {
154		() => {
155			#[doc(hidden)]
156			#[no_mangle]
157			// GCC unwinding
158			pub extern "C" fn __exidx_start() { unimplemented!() }
159
160			#[no_mangle]
161			#[doc(hidden)]
162			// GCC unwinding
163			pub extern "C" fn __exidx_end() { unimplemented!() }
164
165			#[doc(hidden)]
166			#[no_mangle]
167			#[cfg(not(target_os = "windows"))]
168			// there should be loop
169			pub extern "C" fn _exit() {}
170
171			#[doc(hidden)]
172			#[no_mangle]
173			// there should be loop
174			pub extern "C" fn _kill() {}
175
176			#[doc(hidden)]
177			#[no_mangle]
178			pub extern "C" fn _getpid() -> core::ffi::c_int { 0 }
179
180			#[doc(hidden)]
181			#[no_mangle]
182			// it not needed on MacOS
183			#[cfg(not(target_os = "macos"))]
184			// TODO: Somehow link with proper impl: https://stackoverflow.com/q/76439798/829264
185			pub extern "C" fn _sbrk() {}
186		};
187	}
188}
189
190
191#[cfg(target_os = "macos")]
192#[link(name = "System")]
193extern "C" {}
194#[cfg(all(target_os = "windows", target_feature = "crt-static"))]
195#[link(name = "libcmt")]
196extern "C" {}
197#[cfg(all(target_os = "windows", not(target_feature = "crt-static")))]
198#[link(name = "msvcrt")]
199extern "C" {}