playdate_rs/
lib.rs

1#![cfg_attr(all(target_arch = "arm", target_os = "none"), no_std)]
2
3extern crate alloc;
4pub extern crate num_traits;
5#[doc(hidden)]
6pub extern crate playdate_rs_sys as sys;
7pub extern crate rand;
8
9#[macro_use]
10#[doc(hidden)]
11pub mod print;
12
13#[macro_use]
14pub mod math;
15
16pub mod display;
17pub mod error;
18pub mod fs;
19pub mod graphics;
20pub mod lua;
21mod memory;
22pub mod scoreboards;
23pub mod sound;
24pub mod sprite;
25pub mod system;
26pub mod util;
27pub mod video;
28
29use core::{cell::UnsafeCell, ops::Deref};
30
31use alloc::{boxed::Box, format};
32pub use no_std_io::io;
33pub use playdate_rs_macros::app;
34
35pub struct PlaydateAPI {
36    raw_api: *mut sys::PlaydateAPI,
37    /// System interaction
38    pub system: system::PlaydateSystem,
39    /// Filesystem operations
40    pub file: fs::PlaydateFileSystem,
41    /// Graphics operations and drawing functions
42    pub graphics: graphics::PlaydateGraphics,
43    /// Sprite and global sprite display list operations
44    pub sprite: sprite::PlaydateSprite,
45    /// Display operations and management
46    pub display: display::PlaydateDisplay,
47    /// Sound controls
48    pub sound: sound::PlaydateSound,
49    /// Scoreboard operations (unimplemented)
50    pub scoreboards: scoreboards::PlaydateScoreboards,
51    /// Lua VM interactions (unimplemented)
52    pub lua: lua::Lua,
53    // The playdate JSON lib is not supported. Please use serde instead:
54    // pub json: *const playdate_json,
55}
56
57unsafe impl Sync for PlaydateAPI {}
58unsafe impl Send for PlaydateAPI {}
59
60impl PlaydateAPI {
61    fn new(playdate: *mut sys::PlaydateAPI) -> Self {
62        let playdate_ref = unsafe { &*playdate };
63        Self {
64            raw_api: playdate,
65            system: system::PlaydateSystem::new(playdate_ref.system),
66            file: fs::PlaydateFileSystem::new(playdate_ref.file),
67            graphics: graphics::PlaydateGraphics::new(playdate_ref.graphics),
68            sprite: sprite::PlaydateSprite::new(playdate_ref.sprite),
69            display: display::PlaydateDisplay::new(playdate_ref.display),
70            sound: sound::PlaydateSound::new(playdate_ref.sound),
71            scoreboards: scoreboards::PlaydateScoreboards::new(playdate_ref.scoreboards),
72            lua: lua::Lua::new(playdate_ref.lua),
73        }
74    }
75
76    /// Returns a raw pointer to the raw playdate-rs-sys API.
77    pub fn get_raw_api(&self) -> *mut sys::PlaydateAPI {
78        self.raw_api
79    }
80}
81
82pub static PLAYDATE: Playdate = Playdate {
83    _p: UnsafeCell::new(None),
84};
85
86pub struct Playdate {
87    _p: UnsafeCell<Option<PlaydateAPI>>,
88}
89
90unsafe impl Sync for Playdate {}
91unsafe impl Send for Playdate {}
92
93impl Deref for Playdate {
94    type Target = PlaydateAPI;
95
96    fn deref(&self) -> &Self::Target {
97        unsafe { (*self._p.get()).as_ref().unwrap() }
98    }
99}
100
101pub trait App: Sized + 'static {
102    /// Constructor for the app. This is called once when the app is loaded.
103    fn new() -> Self;
104
105    /// Returns a reference to the app singleton.
106    fn get() -> &'static Self {
107        unsafe { &*(APP.unwrap() as *const Self) }
108    }
109
110    /// # Safety
111    ///
112    /// Should always be safe as this is a single-threaded environment.
113    unsafe fn get_mut() -> &'static mut Self {
114        unsafe { &mut *(APP.unwrap() as *mut Self) }
115    }
116
117    /// Called once when the app is loaded.
118    fn init(&mut self) {}
119
120    /// Called once per frame.
121    ///
122    /// `delta` is the time in seconds since the last frame.
123    fn update(&mut self, _delta: f32) {}
124
125    /// Called when a system event occurs.
126    fn handle_event(&mut self, _event: system::SystemEvent, _arg: u32) {}
127}
128
129static mut APP: Option<*mut ()> = None;
130
131unsafe extern "C" fn update<T: App>(_: *mut core::ffi::c_void) -> i32 {
132    let app = T::get_mut();
133    // calculate delta time since last frame
134    let delta_time = {
135        static mut LAST_FRAME_TIME: Option<usize> = None;
136        let current_time = PLAYDATE.system.get_current_time_milliseconds();
137        let delta = if let Some(last_frame_time) = LAST_FRAME_TIME {
138            (current_time - last_frame_time) as f32 / 1000.0
139        } else {
140            0.0
141        };
142        LAST_FRAME_TIME = Some(current_time);
143        delta
144    };
145    // update frame
146    app.update(delta_time);
147    1
148}
149
150fn start_playdate_app<T: App>(pd: *mut sys::PlaydateAPI) {
151    // Initialize playdate singleton
152    unsafe {
153        *PLAYDATE._p.get() = Some(PlaydateAPI::new(pd));
154    }
155    // Create app instance
156    let app = Box::leak(Box::new(T::new()));
157    unsafe {
158        APP = Some(app as *mut T as *mut ());
159    }
160    // Initialize app
161    app.init();
162    PLAYDATE.system.set_update_callback(Some(update::<T>));
163}
164
165#[doc(hidden)]
166pub fn __playdate_handle_event<T: App>(
167    pd: *mut ::core::ffi::c_void,
168    event: system::SystemEvent,
169    arg: u32,
170) {
171    let pd = pd as *mut sys::PlaydateAPI;
172    if event == system::SystemEvent::Init {
173        start_playdate_app::<T>(pd);
174    }
175    unsafe { T::get_mut().handle_event(event, arg) };
176}
177
178#[doc(hidden)]
179pub fn __playdate_handle_panic(info: &core::panic::PanicInfo) -> ! {
180    PLAYDATE.system.error(format!("{}", info));
181    unreachable!()
182}
183
184#[macro_export]
185macro_rules! register_playdate_app {
186    ($app: ident) => {
187        mod __playdate_api {
188            #[no_mangle]
189            unsafe extern "C" fn eventHandler(
190                pd: *mut ::core::ffi::c_void,
191                event: $crate::system::SystemEvent,
192                arg: u32,
193            ) {
194                $crate::__playdate_handle_event::<super::$app>(pd, event, arg);
195            }
196        }
197
198        #[cfg(all(target_arch = "arm", target_os = "none"))]
199        #[panic_handler]
200        #[doc(hidden)]
201        fn __panic_handler(info: &core::panic::PanicInfo) -> ! {
202            $crate::__playdate_handle_panic(info);
203        }
204
205        #[cfg(all(target_arch = "arm", target_os = "none"))]
206        #[no_mangle]
207        pub extern "C" fn _sbrk() {}
208
209        #[cfg(all(target_arch = "arm", target_os = "none"))]
210        #[no_mangle]
211        extern "C" fn _exit() {}
212
213        #[cfg(all(target_arch = "arm", target_os = "none"))]
214        #[no_mangle]
215        extern "C" fn _kill() {}
216
217        #[cfg(all(target_arch = "arm", target_os = "none"))]
218        #[no_mangle]
219        extern "C" fn _getpid() {}
220
221        #[cfg(all(target_arch = "arm", target_os = "none"))]
222        #[no_mangle]
223        extern "C" fn __exidx_start() {
224            unimplemented!();
225        }
226
227        #[cfg(all(target_arch = "arm", target_os = "none"))]
228        #[no_mangle]
229        extern "C" fn __exidx_end() {
230            unimplemented!();
231        }
232    };
233}