1use core::ffi::{c_char, c_void, CStr};
2
3use alloc::{ffi::CString, vec::Vec};
4use sys::PDButtons;
5pub use sys::{
6 LCDFontData as FontData, PDDateTime as DateTime, PDLanguage as Language,
7 PDPeripherals as Peripherals, PDSystemEvent as SystemEvent,
8};
9
10use crate::{graphics::Bitmap, math::Vec2, PLAYDATE};
11
12pub struct PlaydateSystem {
13 handle: *const sys::playdate_sys,
14}
15
16impl PlaydateSystem {
17 pub(crate) fn new(handle: *const sys::playdate_sys) -> Self {
18 Self { handle }
19 }
20
21 pub(crate) fn realloc(&self, ptr: *mut c_void, size: usize) -> *mut c_void {
23 unsafe { (*self.handle).realloc.unwrap()(ptr, size) }
24 }
25
26 pub fn log_to_console(&self, msg: impl AsRef<str>) {
28 unsafe {
29 let c_string = CString::new(msg.as_ref()).unwrap();
30 (*self.handle).logToConsole.unwrap()(c_string.as_ptr() as *mut c_char);
31 }
32 }
33
34 pub fn error(&self, msg: impl AsRef<str>) {
36 unsafe {
37 let c_string = CString::new(msg.as_ref()).unwrap();
38 (*self.handle).error.unwrap()(c_string.as_ptr() as *mut c_char);
39 }
40 }
41
42 pub fn get_language(&self) -> Language {
44 unsafe { (*self.handle).getLanguage.unwrap()() }
45 }
46
47 pub fn get_current_time_milliseconds(&self) -> usize {
49 unsafe { (*self.handle).getCurrentTimeMilliseconds.unwrap()() as _ }
50 }
51
52 pub fn get_seconds_since_epoch(&self) -> (usize, usize) {
54 let mut ms = 0;
55 unsafe {
56 let s = (*self.handle).getSecondsSinceEpoch.unwrap()(&mut ms);
57 (s as _, ms as _)
58 }
59 }
60
61 pub fn draw_fps(&self, pos: Vec2<i32>) {
63 unsafe { (*self.handle).drawFPS.unwrap()(pos.x, pos.y) }
64 }
65
66 pub(crate) fn set_update_callback(&self, update: sys::PDCallbackFunction) {
68 unsafe {
69 (*self.handle).setUpdateCallback.unwrap()(update, core::ptr::null_mut());
70 }
71 }
72
73 pub fn get_button_state(&self) -> ButtonState {
75 let mut current = PDButtons(0);
76 let mut pushed = PDButtons(0);
77 let mut released = PDButtons(0);
78 unsafe {
79 (*self.handle).getButtonState.unwrap()(&mut current, &mut pushed, &mut released);
80 }
81 ButtonState {
82 current: Buttons::from(current.0 as u8),
83 pushed: Buttons::from(pushed.0 as u8),
84 released: Buttons::from(released.0 as u8),
85 }
86 }
87
88 pub fn set_peripherals_enabled(&self, mask: Peripherals) {
90 unsafe {
91 (*self.handle).setPeripheralsEnabled.unwrap()(mask);
92 }
93 }
94
95 pub fn get_accelerometer(&self) -> (f32, f32, f32) {
97 let x = core::ptr::null_mut();
98 let y = core::ptr::null_mut();
99 let z = core::ptr::null_mut();
100 unsafe {
101 (*self.handle).getAccelerometer.unwrap()(x, y, z);
102 (*x, *y, *z)
103 }
104 }
105
106 pub fn get_crank_angle(&self) -> f32 {
108 unsafe { (*self.handle).getCrankAngle.unwrap()() }
109 }
110
111 pub fn get_crank_change(&self) -> f32 {
113 unsafe { (*self.handle).getCrankChange.unwrap()() }
114 }
115
116 pub fn is_crank_docked(&self) -> bool {
118 unsafe {
119 let result = (*self.handle).isCrankDocked.unwrap()();
120 result == 1
121 }
122 }
123
124 pub fn set_crank_sounds_disabled(&self, flag: bool) -> bool {
126 unsafe {
127 let result = (*self.handle).setCrankSoundsDisabled.unwrap()(flag as i32);
128 result == 1
129 }
130 }
131
132 pub fn get_flipped(&self) -> bool {
134 unsafe {
135 let result = (*self.handle).getFlipped.unwrap()();
136 result == 1
137 }
138 }
139
140 pub fn set_auto_lock_disabled(&self, disable: bool) {
142 unsafe { (*self.handle).setAutoLockDisabled.unwrap()(disable as i32) }
143 }
144
145 pub fn set_menu_image(&self, bitmap: impl AsRef<Bitmap>, x_offset: i32) {
151 unsafe { (*self.handle).setMenuImage.unwrap()(bitmap.as_ref().handle, x_offset) }
152 }
153
154 pub fn add_menu_item(&self, title: impl AsRef<str>, callback: fn()) -> MenuItem {
163 extern "C" fn callback_impl(payload: *mut c_void) {
164 let f: fn() = unsafe { core::mem::transmute(payload) };
165 f();
166 }
167 MenuItem::new(unsafe {
168 let c_string = CString::new(title.as_ref()).unwrap();
169 (*self.handle).addMenuItem.unwrap()(
170 c_string.as_ptr() as *mut c_char,
171 Some(callback_impl),
172 callback as _,
173 )
174 })
175 }
176
177 pub fn add_checkmark_menu_item(
185 &self,
186 title: impl AsRef<str>,
187 value: bool,
188 callback: fn(),
189 ) -> MenuItem {
190 extern "C" fn callback_impl(payload: *mut c_void) {
191 let f: fn() = unsafe { core::mem::transmute(payload) };
192 f();
193 }
194 MenuItem::new(unsafe {
195 let c_string = CString::new(title.as_ref()).unwrap();
196 (*self.handle).addCheckmarkMenuItem.unwrap()(
197 c_string.as_ptr() as *mut c_char,
198 value as _,
199 Some(callback_impl),
200 callback as _,
201 )
202 })
203 }
204
205 pub fn add_options_menu_item(
215 &self,
216 title: impl AsRef<str>,
217 option_titles: &[&str],
218 callback: fn(),
219 ) -> MenuItem {
220 extern "C" fn callback_impl(payload: *mut c_void) {
221 let f: fn() = unsafe { core::mem::transmute(payload) };
222 f();
223 }
224 MenuItem::new(unsafe {
225 let c_string = CString::new(title.as_ref()).unwrap();
226 let title_cstrings = option_titles
227 .iter()
228 .map(|s| CString::new(*s).unwrap())
229 .collect::<Vec<_>>();
230 let mut title_ptrs = title_cstrings
231 .iter()
232 .map(|s| s.as_ptr() as *const c_char)
233 .collect::<Vec<_>>();
234 (*self.handle).addOptionsMenuItem.unwrap()(
235 c_string.as_ptr() as *mut c_char,
236 title_ptrs.as_mut_ptr(),
237 option_titles.len() as _,
238 Some(callback_impl),
239 callback as _,
240 )
241 })
242 }
243
244 #[allow(unused)]
246 pub(crate) fn remove_all_menu_items(&self) {
247 unsafe { (*self.handle).removeAllMenuItems.unwrap()() }
248 }
249
250 pub fn get_reduce_flashing(&self) -> bool {
252 unsafe {
253 let result = (*self.handle).getReduceFlashing.unwrap()();
254 result == 1
255 }
256 }
257
258 pub fn get_elapsed_time(&self) -> f32 {
260 unsafe { (*self.handle).getElapsedTime.unwrap()() }
261 }
262
263 pub fn reset_elapsed_time(&self) {
265 unsafe { (*self.handle).resetElapsedTime.unwrap()() }
266 }
267
268 pub fn get_battery_percentage(&self) -> f32 {
270 unsafe { (*self.handle).getBatteryPercentage.unwrap()() }
271 }
272
273 pub fn get_battery_voltage(&self) -> f32 {
275 unsafe { (*self.handle).getBatteryVoltage.unwrap()() }
276 }
277
278 pub fn get_timezone_offset(&self) -> i32 {
280 unsafe { (*self.handle).getTimezoneOffset.unwrap()() }
281 }
282
283 pub fn should_display_24_hour_time(&self) -> bool {
285 unsafe {
286 let result = (*self.handle).shouldDisplay24HourTime.unwrap()();
287 result == 1
288 }
289 }
290
291 pub fn convert_epoch_to_date_time(&self, epoch: u32) -> DateTime {
293 let mut datetime = DateTime::default();
294 unsafe {
295 (*self.handle).convertEpochToDateTime.unwrap()(epoch, &mut datetime);
296 datetime
297 }
298 }
299
300 pub fn convert_date_time_to_epoch(&self, mut datetime: DateTime) -> u32 {
302 unsafe { (*self.handle).convertDateTimeToEpoch.unwrap()(&mut datetime) }
303 }
304
305 pub fn clear_icache(&self) {
307 unsafe { (*self.handle).clearICache.unwrap()() }
308 }
309}
310
311#[derive(PartialEq, Eq, Debug)]
312pub struct MenuItem {
313 handle: *mut sys::PDMenuItem,
314}
315
316unsafe impl Send for MenuItem {}
317unsafe impl Sync for MenuItem {}
318
319impl MenuItem {
320 fn new(handle: *mut sys::PDMenuItem) -> Self {
321 MenuItem { handle }
322 }
323
324 pub fn get_value(&self) -> i32 {
328 unsafe { (*PLAYDATE.system.handle).getMenuItemValue.unwrap()(self.handle) }
329 }
330
331 pub fn set_value(&self, value: i32) {
335 unsafe { (*PLAYDATE.system.handle).setMenuItemValue.unwrap()(self.handle, value) }
336 }
337
338 pub fn get_title(&self) -> &str {
340 let c_buf = unsafe { (*PLAYDATE.system.handle).getMenuItemTitle.unwrap()(self.handle) };
341 let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) };
342 let s: &str = c_str.to_str().unwrap();
343 s
344 }
345
346 pub fn set_title(&self, title: impl AsRef<str>) {
348 let c_string = CString::new(title.as_ref()).unwrap();
349 unsafe {
350 (*PLAYDATE.system.handle).setMenuItemTitle.unwrap()(
351 self.handle,
352 c_string.as_ptr() as *mut c_char,
353 )
354 }
355 }
356
357 #[allow(unused)]
359 pub(crate) fn get_userdata(&self) -> *mut c_void {
360 unsafe { (*PLAYDATE.system.handle).getMenuItemUserdata.unwrap()(self.handle) }
361 }
362
363 #[allow(unused)]
365 pub(crate) fn set_userdata(&self, userdata: *mut c_void) {
366 unsafe { (*PLAYDATE.system.handle).setMenuItemUserdata.unwrap()(self.handle, userdata) }
367 }
368}
369
370impl Drop for MenuItem {
371 fn drop(&mut self) {
372 unsafe { (*PLAYDATE.system.handle).removeMenuItem.unwrap()(self.handle) }
373 }
374}
375
376#[derive(Debug)]
377pub struct ButtonState {
378 pub current: Buttons,
379 pub pushed: Buttons,
380 pub released: Buttons,
381}
382
383#[bitmask_enum::bitmask(u8)]
384pub enum Buttons {
385 Left = 1 << 0,
386 Right = 1 << 1,
387 Up = 1 << 2,
388 Down = 1 << 3,
389 B = 1 << 4,
390 A = 1 << 5,
391}