1#![cfg_attr(not(test), no_std)]
2
3#[macro_use]
4extern crate sys;
5extern crate alloc;
6
7pub mod error;
8
9
10use alloc::borrow::Cow;
11use alloc::boxed::Box;
12use alloc::ffi::NulError;
13use core::ffi::c_int;
14use core::ffi::c_char;
15use core::ffi::c_void;
16use core::marker::PhantomData;
17use sys::ffi::PDMenuItemCallbackFunction;
18use sys::ffi::PDMenuItem;
19use sys::ffi::CStr;
20use sys::ffi::CString;
21use gfx::bitmap::AnyBitmap;
22
23use error::{Error, ApiError};
24
25
26pub type SimpleMenuItem<UserData = (), Api = api::Default, const REMOVE_ON_DROP: bool = true> =
27 MenuItem<kind::Simple, UserData, Api, REMOVE_ON_DROP>;
28
29pub type CheckMenuItem<UserData = (), Api = api::Default, const REMOVE_ON_DROP: bool = true> =
30 MenuItem<kind::Check, UserData, Api, REMOVE_ON_DROP>;
31
32pub type OptionsMenuItem<UserData = (), Api = api::Default, const REMOVE_ON_DROP: bool = true> =
33 MenuItem<kind::Options, UserData, Api, REMOVE_ON_DROP>;
34
35
36#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
37pub struct MenuItem<Kind, UserData = (), Api= api::Default, const REMOVE_ON_DROP: bool = true>(*mut PDMenuItem, Api, PhantomData<Kind>, PhantomData<UserData>) where Kind: kind::Kind, UserData: Into<Box<UserData>>, Api: api::Api;
38
39
40impl<UD, K: kind::Kind, Api: api::Api, const REM: bool> MenuItem<K, UD, Api, REM> {
41 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemTitle")]
45 pub fn title(&self) -> Cow<'_, str> {
46 let f = self.1.get_menu_item_title();
47 unsafe { CStr::from_ptr(f(self.0) as _) }.to_string_lossy()
48 }
49
50 #[doc(alias = "sys::ffi::playdate_sys::setMenuItemTitle")]
54 pub fn set_title<S: AsRef<str>>(&self, title: S) -> Result<(), NulError> {
55 let f = self.1.set_menu_item_title();
56 let s = CString::new(title.as_ref())?;
57 unsafe { f(self.0, s.as_ptr() as *mut c_char) };
58 core::mem::drop(s);
59 Ok(())
60 }
61
62 pub fn get_userdata(&self) -> Option<&mut UD> { self.get_userdata_full().map(|(_, ud)| ud) }
63
64 fn get_userdata_full(&self) -> Option<&mut CallbackUserData<UD>> {
65 let f = self.1.get_menu_item_userdata();
66 let ptr = unsafe { f(self.0) };
67 if ptr.is_null()
68 {
70 return None;
71 }
72
73 unsafe { (ptr as *mut CallbackUserData<UD>).as_mut() }
74 }
75
76 pub fn set_userdata(&self, userdata: UD) -> Option<UD> {
78 if let Some(existing) = self.get_userdata() {
79 core::mem::replace(existing, userdata).into()
80 } else {
81 todo!()
82 }
83 }
84
85 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemValue")]
91 pub fn value(&self) -> c_int {
92 let f = self.1.get_menu_item_value();
93 unsafe { f(self.0) }
94 }
95
96 #[doc(alias = "sys::ffi::playdate_sys::setMenuItemValue")]
106 pub fn set_value(&self, value: c_int) {
107 let f = self.1.set_menu_item_value();
108 unsafe { f(self.0, value) }
109 }
110
111
112 fn take_userdata(&mut self) -> Option<UD> {
113 if self.0.is_null() {
114 return None;
115 }
116
117 let f = self.1.get_menu_item_userdata();
118 let ptr = unsafe { f(self.0) };
119 if ptr.is_null() {
120 return None;
121 } else if core::mem::size_of::<UD>() == 0
122 {
124 return None;
126 }
127
128 let ud: CallbackUserData<UD> = *unsafe { Box::from_raw(ptr as *mut CallbackUserData<UD>) };
129 let (_, userdata) = ud;
130 Some(userdata)
131 }
132}
133
134
135impl<UD: Sized, Api: api::Api, const REM: bool> MenuItem<kind::Check, UD, Api, REM> {
136 #[inline(always)]
137 pub fn new<S: AsRef<str>>(title: S,
138 checked: bool,
139 callback: Option<MenuItemCallback<UD>>,
140 userdata: UD)
141 -> Result<Self, ApiError>
142 where Api: Default
143 {
144 Self::new_with(Api::default(), title, checked, callback, userdata)
145 }
146
147 pub fn new_with<S: AsRef<str>>(api: Api,
148 title: S,
149 checked: bool,
150 callback: Option<MenuItemCallback<UD>>,
151 userdata: UD)
152 -> Result<Self, ApiError> {
153 let (callback, userdata) = proxy_menu_parts::<_, _>(callback, userdata);
154 let title = CString::new(title.as_ref())?;
155
156 let ctor = api.add_checkmark_menu_item();
157 let ptr = unsafe { ctor(title.as_ptr() as *mut c_char, checked as _, callback, userdata) };
158
159 if ptr.is_null() {
160 Err(Error::Alloc.into())
161 } else {
162 Ok(MenuItem(ptr, api, PhantomData, PhantomData))
163 }
164 }
165}
166
167
168impl<UD: Sized, Api: api::Api, const REM: bool> MenuItem<kind::Simple, UD, Api, REM> {
169 #[inline(always)]
170 pub fn new<S: AsRef<str>>(title: S,
171 callback: Option<MenuItemCallback<UD>>,
172 userdata: UD)
173 -> Result<Self, ApiError>
174 where Api: Default
175 {
176 Self::new_with(Api::default(), title, callback, userdata)
177 }
178
179 pub fn new_with<S: AsRef<str>>(api: Api,
180 title: S,
181 callback: Option<MenuItemCallback<UD>>,
182 userdata: UD)
183 -> Result<Self, ApiError> {
184 let (callback, userdata) = proxy_menu_parts::<_, _>(callback, userdata);
185 let title = CString::new(title.as_ref())?;
186
187 let ctor = api.add_menu_item();
188 let ptr = unsafe { ctor(title.as_ptr() as *mut c_char, callback, userdata) };
189
190 if ptr.is_null() {
191 Err(Error::Alloc.into())
192 } else {
193 Ok(MenuItem(ptr, api, PhantomData, PhantomData::<UD>))
194 }
195 }
196}
197
198impl<UD: Sized, Api: api::Api, const REM: bool> MenuItem<kind::Options, UD, Api, REM> {
199 #[inline(always)]
200 pub fn new<S: AsRef<str>, O: AsRef<[S]>>(title: S,
201 options: O,
202 callback: Option<MenuItemCallback<UD>>,
203 userdata: UD)
204 -> Result<Self, ApiError>
205 where Api: Default
206 {
207 Self::new_with(Api::default(), title, options, callback, userdata)
208 }
209
210 pub fn new_with<S: AsRef<str>, O: AsRef<[S]>>(api: Api,
211 title: S,
212 options: O,
213 callback: Option<MenuItemCallback<UD>>,
214 userdata: UD)
215 -> Result<Self, ApiError> {
216 #[allow(unused_imports)]
217 use alloc::vec::Vec;
218
219
220 let (callback, userdata) = proxy_menu_parts::<_, _>(callback, userdata);
221 let title = CString::new(title.as_ref())?;
222
223 let options = options.as_ref();
224 let mut opts = Vec::with_capacity(options.len());
225 for opt in options {
226 let opt = CString::new(opt.as_ref())?;
227 opts.push(opt)
228 }
229 let mut ptrs = Vec::with_capacity(options.len());
230 ptrs.extend(opts.iter().map(|s| s.as_ptr()));
231
232 let ctor = api.add_options_menu_item();
233 let ptr = unsafe {
234 ctor(
235 title.as_ptr() as *mut c_char,
236 ptrs.as_mut_ptr() as _,
237 ptrs.len() as _,
238 callback,
239 userdata,
240 )
241 };
242
243 core::mem::drop(ptrs);
244 core::mem::drop(opts);
245
246 if ptr.is_null() {
247 Err(Error::Alloc.into())
248 } else {
249 Ok(MenuItem(ptr, api, PhantomData, PhantomData::<UD>))
250 }
251 }
252}
253
254
255#[inline(always)]
256fn proxy_menu_parts<UD: Sized, F: FnMut(&mut UD)>(cb: Option<F>,
257 ud: UD)
258 -> (PDMenuItemCallbackFunction, *mut c_void) {
259 unsafe extern "C" fn proxy<UserData: Sized>(userdata: *mut c_void) {
260 if let Some((callback, userdata)) = (userdata as *mut CallbackUserData<UserData>).as_mut() {
261 callback(userdata)
262 } else {
263 panic!("user callback missed");
264 }
265 }
266
267 if let Some(callback) = cb {
268 let ptr = Box::into_raw(Box::from((callback, ud)));
270 (Some(proxy::<UD> as _), ptr as *mut _)
271 } else {
272 fn noop<UserData: Sized>(_: &mut UserData) {}
280 let ptr = Box::into_raw(Box::from((noop::<UD>, ud)));
281 (None, ptr as *mut _)
282 }
283}
284
285
286impl<UD, Api: api::Api, const REM: bool> MenuItem<kind::Check, UD, Api, REM> {
287 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemValue")]
289 #[inline(always)]
290 pub fn is_checked(&self) -> bool { self.value() == 1 }
291}
292
293
294impl<UD, Api: api::Api, const REM: bool> MenuItem<kind::Options, UD, Api, REM> {
295 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemValue")]
299 #[inline(always)]
300 pub fn selected_option(&self) -> i32 { self.value() }
301}
302
303
304impl<UD, K: kind::Kind, Api: api::Api, const REM: bool> Drop for MenuItem<K, UD, Api, REM> {
305 fn drop(&mut self) {
306 if REM && !self.0.is_null() {
307 self.take_userdata();
309 let f = self.1.remove_menu_item();
310 unsafe { f(self.0) };
311 }
312 }
313}
314
315impl<UD, K: kind::Kind, Api: api::Api, const REM: bool> MenuItem<K, UD, Api, REM> {
316 #[inline(always)]
317 pub fn remove(mut self) -> Option<UD> {
318 let ud = self.take_userdata();
319
320 let f = self.1.remove_menu_item();
321 unsafe { f(self.0) };
322 self.0 = core::ptr::null_mut() as _;
323
324 ud
325 }
326}
327
328
329#[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
333#[inline(always)]
334pub fn remove_all_menu_items() { remove_all_menu_items_with(api::Default::default()) }
335
336#[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
342pub fn remove_all_menu_items_with<Api: api::Api>(api: Api) {
343 let f = api.remove_all_menu_items();
344 unsafe { f() };
345}
346
347#[doc(alias = "sys::ffi::playdate_sys::setMenuImage")]
361#[inline(always)]
362pub fn set_menu_image(bitmap: impl AnyBitmap, x_offset: c_int) {
363 set_menu_image_with(api::Default::default(), bitmap, x_offset);
364}
365
366
367#[doc(alias = "sys::ffi::playdate_sys::setMenuImage")]
383pub fn set_menu_image_with<Api: api::Api>(api: Api, bitmap: impl AnyBitmap, x_offset: c_int) {
384 let f = api.set_menu_image();
385 unsafe { f(bitmap.as_raw(), x_offset) };
386}
387
388pub trait SystemMenu<Api: api::Api + Copy> {
389 #[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
393 fn remove_all_menu_items(&self);
394
395 #[doc(alias = "sys::ffi::playdate_sys::setMenuImage")]
409 fn set_menu_image(&self, bitmap: impl AnyBitmap, x_offset: c_int);
410}
411
412impl<Api: system::api::Api + api::Api + Copy> SystemMenu<Api> for system::System<Api> {
413 #[inline(always)]
414 fn remove_all_menu_items(&self) {
415 remove_all_menu_items_with(self.inner())
416 }
417
418 #[inline(always)]
419 fn set_menu_image(&self, bitmap: impl AnyBitmap, x_offset: c_int) {
420 set_menu_image_with(self.inner(), bitmap, x_offset)
421 }
422}
423
424
425type CallbackUserData<UserData> = (MenuItemCallback<UserData>, UserData);
426type MenuItemCallback<T> = fn(userdata: &mut T);
427
428
429pub mod kind {
430 pub trait Kind {}
431
432
433 #[derive(Debug)]
434 pub struct Simple;
435
436 #[derive(Debug)]
437 pub struct Check;
438
439 #[derive(Debug)]
440 pub struct Options;
441
442 impl Kind for Simple {}
443 impl Kind for Check {}
444 impl Kind for Options {}
445}
446
447
448pub mod api {
449 use core::ffi::c_char;
450 use core::ffi::c_int;
451 use core::ffi::c_void;
452 use core::ptr::NonNull;
453 use sys::ffi::LCDBitmap;
454 use sys::ffi::PDMenuItem;
455 use sys::ffi::PDMenuItemCallbackFunction;
456 use sys::ffi::playdate_sys;
457
458
459 #[derive(Debug, Clone, Copy, core::default::Default)]
463 pub struct Default;
464 impl Api for Default {}
465
466
467 #[derive(Clone, Copy)]
473 #[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
474 pub struct Cache(&'static playdate_sys);
475
476 impl core::default::Default for Cache {
477 fn default() -> Self { Self(api!(system)) }
478 }
479
480 impl From<*const playdate_sys> for Cache {
481 #[inline(always)]
482 fn from(ptr: *const playdate_sys) -> Self { Self(unsafe { ptr.as_ref() }.expect("system")) }
483 }
484
485 impl From<&'static playdate_sys> for Cache {
486 #[inline(always)]
487 fn from(r: &'static playdate_sys) -> Self { Self(r) }
488 }
489
490 impl From<NonNull<playdate_sys>> for Cache {
491 #[inline(always)]
492 fn from(ptr: NonNull<playdate_sys>) -> Self { Self(unsafe { ptr.as_ref() }) }
493 }
494
495 impl From<&'_ NonNull<playdate_sys>> for Cache {
496 #[inline(always)]
497 fn from(ptr: &NonNull<playdate_sys>) -> Self { Self(unsafe { ptr.as_ref() }) }
498 }
499
500 impl From<system::api::Cache> for Cache {
501 #[inline(always)]
502 fn from(api: system::api::Cache) -> Self { Self(api.as_inner()) }
503 }
504
505 impl Api for system::api::Default {}
506
507 impl Api for system::api::Cache {
508 #[inline(always)]
509 fn add_menu_item(
510 &self)
511 -> unsafe extern "C" fn(title: *const c_char,
512 callback: PDMenuItemCallbackFunction,
513 userdata: *mut c_void) -> *mut PDMenuItem {
514 self.as_inner().addMenuItem.expect("addMenuItem")
515 }
516
517 #[inline(always)]
518 fn add_checkmark_menu_item(
519 &self)
520 -> unsafe extern "C" fn(title: *const c_char,
521 value: c_int,
522 callback: PDMenuItemCallbackFunction,
523 userdata: *mut c_void) -> *mut PDMenuItem {
524 self.as_inner()
525 .addCheckmarkMenuItem
526 .expect("addCheckmarkMenuItem")
527 }
528
529 #[inline(always)]
530 fn add_options_menu_item(
531 &self)
532 -> unsafe extern "C" fn(title: *const c_char,
533 optionTitles: *mut *const c_char,
534 optionsCount: c_int,
535 f: PDMenuItemCallbackFunction,
536 userdata: *mut c_void) -> *mut PDMenuItem {
537 self.as_inner().addOptionsMenuItem.expect("addOptionsMenuItem")
538 }
539
540 #[inline(always)]
541 fn remove_menu_item(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) {
542 self.as_inner().removeMenuItem.expect("removeMenuItem")
543 }
544
545 #[inline(always)]
546 fn get_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> c_int {
547 self.as_inner().getMenuItemValue.expect("getMenuItemValue")
548 }
549
550 #[inline(always)]
551 fn set_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, value: c_int) {
552 self.as_inner().setMenuItemValue.expect("setMenuItemValue")
553 }
554
555 #[inline(always)]
556 fn get_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *const c_char {
557 self.as_inner().getMenuItemTitle.expect("getMenuItemTitle")
558 }
559
560 #[inline(always)]
561 fn set_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, title: *const c_char) {
562 self.as_inner().setMenuItemTitle.expect("setMenuItemTitle")
563 }
564
565 #[inline(always)]
566 fn get_menu_item_userdata(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *mut c_void {
567 self.as_inner().getMenuItemUserdata.expect("getMenuItemUserdata")
568 }
569
570 #[inline(always)]
571 fn remove_all_menu_items(&self) -> unsafe extern "C" fn() {
572 self.as_inner().removeAllMenuItems.expect("removeAllMenuItems")
573 }
574
575 #[inline(always)]
576 fn set_menu_image(&self) -> unsafe extern "C" fn(*mut LCDBitmap, i32) {
577 self.as_inner().setMenuImage.expect("setMenuImage")
578 }
579 }
580
581
582 impl Api for Cache {
583 #[inline(always)]
584 fn add_menu_item(
585 &self)
586 -> unsafe extern "C" fn(title: *const c_char,
587 callback: PDMenuItemCallbackFunction,
588 userdata: *mut c_void) -> *mut PDMenuItem {
589 self.0.addMenuItem.expect("addMenuItem")
590 }
591
592 #[inline(always)]
593 fn add_checkmark_menu_item(
594 &self)
595 -> unsafe extern "C" fn(title: *const c_char,
596 value: c_int,
597 callback: PDMenuItemCallbackFunction,
598 userdata: *mut c_void) -> *mut PDMenuItem {
599 self.0.addCheckmarkMenuItem.expect("addCheckmarkMenuItem")
600 }
601
602 #[inline(always)]
603 fn add_options_menu_item(
604 &self)
605 -> unsafe extern "C" fn(title: *const c_char,
606 optionTitles: *mut *const c_char,
607 optionsCount: c_int,
608 f: PDMenuItemCallbackFunction,
609 userdata: *mut c_void) -> *mut PDMenuItem {
610 self.0.addOptionsMenuItem.expect("addOptionsMenuItem")
611 }
612
613 #[inline(always)]
614 fn remove_menu_item(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) {
615 self.0.removeMenuItem.expect("removeMenuItem")
616 }
617
618 #[inline(always)]
619 fn get_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> c_int {
620 self.0.getMenuItemValue.expect("getMenuItemValue")
621 }
622
623 #[inline(always)]
624 fn set_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, value: c_int) {
625 self.0.setMenuItemValue.expect("setMenuItemValue")
626 }
627
628 #[inline(always)]
629 fn get_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *const c_char {
630 self.0.getMenuItemTitle.expect("getMenuItemTitle")
631 }
632
633 #[inline(always)]
634 fn set_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, title: *const c_char) {
635 self.0.setMenuItemTitle.expect("setMenuItemTitle")
636 }
637
638 #[inline(always)]
639 fn get_menu_item_userdata(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *mut c_void {
640 self.0.getMenuItemUserdata.expect("getMenuItemUserdata")
641 }
642
643 #[inline(always)]
644 fn remove_all_menu_items(&self) -> unsafe extern "C" fn() {
645 self.0.removeAllMenuItems.expect("removeAllMenuItems")
646 }
647 }
648
649
650 pub trait Api {
651 #[doc(alias = "sys::ffi::playdate_sys::addMenuItem")]
653 fn add_menu_item(
654 &self)
655 -> unsafe extern "C" fn(title: *const c_char,
656 callback: PDMenuItemCallbackFunction,
657 userdata: *mut c_void) -> *mut PDMenuItem {
658 *sys::api!(system.addMenuItem)
659 }
660
661 #[doc(alias = "sys::ffi::playdate_sys::addCheckmarkMenuItem")]
663 fn add_checkmark_menu_item(
664 &self)
665 -> unsafe extern "C" fn(title: *const c_char,
666 value: c_int,
667 callback: PDMenuItemCallbackFunction,
668 userdata: *mut c_void) -> *mut PDMenuItem {
669 *sys::api!(system.addCheckmarkMenuItem)
670 }
671
672 #[doc(alias = "sys::ffi::playdate_sys::addOptionsMenuItem")]
674 fn add_options_menu_item(
675 &self)
676 -> unsafe extern "C" fn(title: *const c_char,
677 optionTitles: *mut *const c_char,
678 optionsCount: c_int,
679 f: PDMenuItemCallbackFunction,
680 userdata: *mut c_void) -> *mut PDMenuItem {
681 *sys::api!(system.addOptionsMenuItem)
682 }
683
684 #[doc(alias = "sys::ffi::playdate_sys::removeMenuItem")]
686 fn remove_menu_item(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) {
687 *sys::api!(system.removeMenuItem)
688 }
689
690 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemValue")]
692 fn get_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> c_int {
693 *sys::api!(system.getMenuItemValue)
694 }
695
696 #[doc(alias = "sys::ffi::playdate_sys::setMenuItemValue")]
698 fn set_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, value: c_int) {
699 *sys::api!(system.setMenuItemValue)
700 }
701
702 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemTitle")]
704 fn get_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *const c_char {
705 *sys::api!(system.getMenuItemTitle)
706 }
707
708 #[doc(alias = "sys::ffi::playdate_sys::setMenuItemTitle")]
710 fn set_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, title: *const c_char) {
711 *sys::api!(system.setMenuItemTitle)
712 }
713
714 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemUserdata")]
716 fn get_menu_item_userdata(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *mut c_void {
717 *sys::api!(system.getMenuItemUserdata)
718 }
719
720 #[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
722 fn remove_all_menu_items(&self) -> unsafe extern "C" fn() { *sys::api!(system.removeAllMenuItems) }
723
724 #[doc(alias = "sys::ffi::playdate_sys::setMenuImage")]
726 fn set_menu_image(&self) -> unsafe extern "C" fn(*mut LCDBitmap, i32) { *sys::api!(system.setMenuImage)}
727 }
728}