1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// https://doc.rust-lang.org/core/ffi/index.html
// https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#![cfg_attr(not(test), no_std)]
#![allow(internal_features)]
#![feature(lang_items)]
#![feature(core_intrinsics)]
// panic
#![cfg_attr(feature = "panic-handler", feature(panic_info_message))]
// allocator
#![cfg_attr(feature = "allocator", feature(alloc_error_handler))]
#![cfg_attr(feature = "allocator", feature(allocator_api))]
#![cfg_attr(feature = "allocator", feature(alloc_layout_extra))]
// error
#![feature(error_in_core)]
#![cfg_attr(feature = "try-trait-v2", feature(try_trait_v2))]
// experimental features
#![cfg_attr(feature = "bindings-derive-constparamty",
            feature(adt_const_params),
            allow(incomplete_features))]


#[allow(unused_imports)]
#[macro_use]
pub extern crate alloc;

pub mod log;
pub mod traits;

mod sys;
pub use sys::*;


pub mod ffi {
	#![allow(non_upper_case_globals)]
	#![allow(non_camel_case_types)]
	#![allow(non_snake_case)]
	#![cfg_attr(test, allow(deref_nullptr))]
	#![allow(clippy::all, clippy::pedantic, clippy::nursery)]
	//! Low-level Playdate C-API.
	//!
	#![doc = concat!("\nSDK version: `", env!("PD_SDK_VERSION"), "`\n")]
	//!
	//! Original official docs: [latest][], [current][].
	//!
	//! [latest]: https://sdk.play.date/Inside%20Playdate%20with%20C.html
	#![doc = concat!("[current]: https://sdk.play.date/", env!("PD_SDK_VERSION"), "/Inside%20Playdate%20with%20C.html")]
	//!

	// include bindings:
	include!(env!("PD_BINDINGS_PATH"));

	/// Preferred `CString` to use.
	pub use alloc::ffi::CString;
	/// Preferred `CStr` to use.
	pub use core::ffi::CStr;
	pub use core::str::Utf8Error;
}


#[cfg(feature = "entry-point")]
#[no_mangle]
/// Simple minimal proxy entry point.
/// Registers API endpoint when called with `event` matches `PDSystemEvent::kEventInit`.
/// It needed for allocators and panic handler.
///
/// Linking requires rust-abi symbol `event_handler: fn(*const PlaydateAPI, PDSystemEvent, arg: u32) -> c_int`
pub extern "C" fn eventHandlerShim(api: *const ffi::PlaydateAPI,
                                   event: ffi::PDSystemEvent,
                                   arg: u32)
                                   -> core::ffi::c_int {
	extern "Rust" {
		fn event_handler(api: *const ffi::PlaydateAPI, event: ffi::PDSystemEvent, arg: u32) -> EventLoopCtrl;
	}
	if let ffi::PDSystemEvent::kEventInit = event {
		unsafe { API = api }
	}
	unsafe { event_handler(api, event, arg) }.into()
}

#[cfg(feature = "entry-point")]
pub use entry_point_ctrl::*;
#[cfg(feature = "entry-point")]
mod entry_point_ctrl {
	use core::ffi::c_int;


	#[repr(i32)]
	pub enum EventLoopCtrl {
		Continue = 0,
		Stop = 1,
	}

	impl EventLoopCtrl {
		pub const fn into(self) -> c_int { self as _ }
	}

	impl From<bool> for EventLoopCtrl {
		fn from(value: bool) -> Self { if value { Self::Continue } else { Self::Stop } }
	}

	impl<T, E> From<Result<T, E>> for EventLoopCtrl {
		fn from(res: Result<T, E>) -> Self { if res.is_ok() { Self::Continue } else { Self::Stop } }
	}

	#[cfg(feature = "try-trait-v2")]
	mod impl_trait_v2 {
		use super::*;
		use core::fmt::Display;
		use core::ops::FromResidual;
		use core::convert::Infallible;

		impl<E: Display> FromResidual<Result<Infallible, E>> for EventLoopCtrl {
			#[track_caller]
			fn from_residual(residual: Result<Infallible, E>) -> Self {
				if let Err(err) = residual {
					crate::println!("Error: {err}");
					// panic!("{err}");
					Self::Stop
				} else {
					Self::Continue
				}
			}
		}
	}
}


pub mod info {
	//! Build info.
	/// Version of the Playdate SDK used for build.
	pub const SDK_VERSION: &str = env!("PD_SDK_VERSION");
}


/// Needed to build debug & executable binaries.
#[cfg(feature = "eh-personality")]
#[lang = "eh_personality"]
#[cfg(all(not(test), not(doc)))]
// #[cfg(not(target = "thumbv7em-none-eabihf"))]
extern "C" fn eh_personality() {}

pub mod misc {
	#[macro_export]
	/// Adds low-level symbols required by gcc for unwinding & exceptions (if `-fno-exceptions` or `-fno-rtti` not set).
	///
	/// There's just dummy- empty- no-op- implementation.
	/// Anyway these symbols will be removed at the final (thanks DCE, LTO, linking with SDK/link_map.ld).
	macro_rules! ll_symbols {
		() => {
			#[doc(hidden)]
			#[no_mangle]
			// GCC unwinding
			pub extern "C" fn __exidx_start() { unimplemented!() }

			#[no_mangle]
			#[doc(hidden)]
			// GCC unwinding
			pub extern "C" fn __exidx_end() { unimplemented!() }

			#[doc(hidden)]
			#[no_mangle]
			#[cfg(not(target_os = "windows"))]
			// there should be loop
			pub extern "C" fn _exit() {}

			#[doc(hidden)]
			#[no_mangle]
			// there should be loop
			pub extern "C" fn _kill() {}

			#[doc(hidden)]
			#[no_mangle]
			pub extern "C" fn _getpid() -> core::ffi::c_int { 0 }

			#[doc(hidden)]
			#[no_mangle]
			// it not needed on MacOS
			#[cfg(not(target_os = "macos"))]
			// TODO: Somehow link with proper impl: https://stackoverflow.com/q/76439798/829264
			pub extern "C" fn _sbrk() {}
		};
	}
}


#[cfg(target_os = "macos")]
#[link(name = "System")]
extern "C" {}
#[cfg(all(target_os = "windows", target_feature = "crt-static"))]
#[link(name = "libcmt")]
extern "C" {}
#[cfg(all(target_os = "windows", not(target_feature = "crt-static")))]
#[link(name = "msvcrt")]
extern "C" {}