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) { remove_all_menu_items_with(self.inner()) }
415
416 #[inline(always)]
417 fn set_menu_image(&self, bitmap: impl AnyBitmap, x_offset: c_int) {
418 set_menu_image_with(self.inner(), bitmap, x_offset)
419 }
420}
421
422
423type CallbackUserData<UserData> = (MenuItemCallback<UserData>, UserData);
424type MenuItemCallback<T> = fn(userdata: &mut T);
425
426
427pub mod kind {
428 pub trait Kind {}
429
430
431 #[derive(Debug)]
432 pub struct Simple;
433
434 #[derive(Debug)]
435 pub struct Check;
436
437 #[derive(Debug)]
438 pub struct Options;
439
440 impl Kind for Simple {}
441 impl Kind for Check {}
442 impl Kind for Options {}
443}
444
445
446pub mod api {
447 use core::ffi::c_char;
448 use core::ffi::c_int;
449 use core::ffi::c_void;
450 use core::ptr::NonNull;
451 use sys::ffi::LCDBitmap;
452 use sys::ffi::PDMenuItem;
453 use sys::ffi::PDMenuItemCallbackFunction;
454 use sys::ffi::playdate_sys;
455
456
457 #[derive(Debug, Clone, Copy, core::default::Default)]
461 pub struct Default;
462 impl Api for Default {}
463
464
465 #[derive(Clone, Copy)]
471 #[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
472 pub struct Cache(&'static playdate_sys);
473
474 impl core::default::Default for Cache {
475 fn default() -> Self { Self(api!(system)) }
476 }
477
478 impl From<*const playdate_sys> for Cache {
479 #[inline(always)]
480 fn from(ptr: *const playdate_sys) -> Self { Self(unsafe { ptr.as_ref() }.expect("system")) }
481 }
482
483 impl From<&'static playdate_sys> for Cache {
484 #[inline(always)]
485 fn from(r: &'static playdate_sys) -> Self { Self(r) }
486 }
487
488 impl From<NonNull<playdate_sys>> for Cache {
489 #[inline(always)]
490 fn from(ptr: NonNull<playdate_sys>) -> Self { Self(unsafe { ptr.as_ref() }) }
491 }
492
493 impl From<&'_ NonNull<playdate_sys>> for Cache {
494 #[inline(always)]
495 fn from(ptr: &NonNull<playdate_sys>) -> Self { Self(unsafe { ptr.as_ref() }) }
496 }
497
498 impl From<system::api::Cache> for Cache {
499 #[inline(always)]
500 fn from(api: system::api::Cache) -> Self { Self(api.as_inner()) }
501 }
502
503 impl Api for system::api::Default {}
504
505 impl Api for system::api::Cache {
506 #[inline(always)]
507 fn add_menu_item(
508 &self)
509 -> unsafe extern "C" fn(title: *const c_char,
510 callback: PDMenuItemCallbackFunction,
511 userdata: *mut c_void) -> *mut PDMenuItem {
512 self.as_inner().addMenuItem.expect("addMenuItem")
513 }
514
515 #[inline(always)]
516 fn add_checkmark_menu_item(
517 &self)
518 -> unsafe extern "C" fn(title: *const c_char,
519 value: c_int,
520 callback: PDMenuItemCallbackFunction,
521 userdata: *mut c_void) -> *mut PDMenuItem {
522 self.as_inner()
523 .addCheckmarkMenuItem
524 .expect("addCheckmarkMenuItem")
525 }
526
527 #[inline(always)]
528 fn add_options_menu_item(
529 &self)
530 -> unsafe extern "C" fn(title: *const c_char,
531 optionTitles: *mut *const c_char,
532 optionsCount: c_int,
533 f: PDMenuItemCallbackFunction,
534 userdata: *mut c_void) -> *mut PDMenuItem {
535 self.as_inner().addOptionsMenuItem.expect("addOptionsMenuItem")
536 }
537
538 #[inline(always)]
539 fn remove_menu_item(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) {
540 self.as_inner().removeMenuItem.expect("removeMenuItem")
541 }
542
543 #[inline(always)]
544 fn get_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> c_int {
545 self.as_inner().getMenuItemValue.expect("getMenuItemValue")
546 }
547
548 #[inline(always)]
549 fn set_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, value: c_int) {
550 self.as_inner().setMenuItemValue.expect("setMenuItemValue")
551 }
552
553 #[inline(always)]
554 fn get_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *const c_char {
555 self.as_inner().getMenuItemTitle.expect("getMenuItemTitle")
556 }
557
558 #[inline(always)]
559 fn set_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, title: *const c_char) {
560 self.as_inner().setMenuItemTitle.expect("setMenuItemTitle")
561 }
562
563 #[inline(always)]
564 fn get_menu_item_userdata(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *mut c_void {
565 self.as_inner().getMenuItemUserdata.expect("getMenuItemUserdata")
566 }
567
568 #[inline(always)]
569 fn remove_all_menu_items(&self) -> unsafe extern "C" fn() {
570 self.as_inner().removeAllMenuItems.expect("removeAllMenuItems")
571 }
572
573 #[inline(always)]
574 fn set_menu_image(&self) -> unsafe extern "C" fn(*mut LCDBitmap, i32) {
575 self.as_inner().setMenuImage.expect("setMenuImage")
576 }
577 }
578
579
580 impl Api for Cache {
581 #[inline(always)]
582 fn add_menu_item(
583 &self)
584 -> unsafe extern "C" fn(title: *const c_char,
585 callback: PDMenuItemCallbackFunction,
586 userdata: *mut c_void) -> *mut PDMenuItem {
587 self.0.addMenuItem.expect("addMenuItem")
588 }
589
590 #[inline(always)]
591 fn add_checkmark_menu_item(
592 &self)
593 -> unsafe extern "C" fn(title: *const c_char,
594 value: c_int,
595 callback: PDMenuItemCallbackFunction,
596 userdata: *mut c_void) -> *mut PDMenuItem {
597 self.0.addCheckmarkMenuItem.expect("addCheckmarkMenuItem")
598 }
599
600 #[inline(always)]
601 fn add_options_menu_item(
602 &self)
603 -> unsafe extern "C" fn(title: *const c_char,
604 optionTitles: *mut *const c_char,
605 optionsCount: c_int,
606 f: PDMenuItemCallbackFunction,
607 userdata: *mut c_void) -> *mut PDMenuItem {
608 self.0.addOptionsMenuItem.expect("addOptionsMenuItem")
609 }
610
611 #[inline(always)]
612 fn remove_menu_item(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) {
613 self.0.removeMenuItem.expect("removeMenuItem")
614 }
615
616 #[inline(always)]
617 fn get_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> c_int {
618 self.0.getMenuItemValue.expect("getMenuItemValue")
619 }
620
621 #[inline(always)]
622 fn set_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, value: c_int) {
623 self.0.setMenuItemValue.expect("setMenuItemValue")
624 }
625
626 #[inline(always)]
627 fn get_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *const c_char {
628 self.0.getMenuItemTitle.expect("getMenuItemTitle")
629 }
630
631 #[inline(always)]
632 fn set_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, title: *const c_char) {
633 self.0.setMenuItemTitle.expect("setMenuItemTitle")
634 }
635
636 #[inline(always)]
637 fn get_menu_item_userdata(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *mut c_void {
638 self.0.getMenuItemUserdata.expect("getMenuItemUserdata")
639 }
640
641 #[inline(always)]
642 fn remove_all_menu_items(&self) -> unsafe extern "C" fn() {
643 self.0.removeAllMenuItems.expect("removeAllMenuItems")
644 }
645 }
646
647
648 pub trait Api {
649 #[doc(alias = "sys::ffi::playdate_sys::addMenuItem")]
651 fn add_menu_item(
652 &self)
653 -> unsafe extern "C" fn(title: *const c_char,
654 callback: PDMenuItemCallbackFunction,
655 userdata: *mut c_void) -> *mut PDMenuItem {
656 *sys::api!(system.addMenuItem)
657 }
658
659 #[doc(alias = "sys::ffi::playdate_sys::addCheckmarkMenuItem")]
661 fn add_checkmark_menu_item(
662 &self)
663 -> unsafe extern "C" fn(title: *const c_char,
664 value: c_int,
665 callback: PDMenuItemCallbackFunction,
666 userdata: *mut c_void) -> *mut PDMenuItem {
667 *sys::api!(system.addCheckmarkMenuItem)
668 }
669
670 #[doc(alias = "sys::ffi::playdate_sys::addOptionsMenuItem")]
672 fn add_options_menu_item(
673 &self)
674 -> unsafe extern "C" fn(title: *const c_char,
675 optionTitles: *mut *const c_char,
676 optionsCount: c_int,
677 f: PDMenuItemCallbackFunction,
678 userdata: *mut c_void) -> *mut PDMenuItem {
679 *sys::api!(system.addOptionsMenuItem)
680 }
681
682 #[doc(alias = "sys::ffi::playdate_sys::removeMenuItem")]
684 fn remove_menu_item(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) {
685 *sys::api!(system.removeMenuItem)
686 }
687
688 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemValue")]
690 fn get_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> c_int {
691 *sys::api!(system.getMenuItemValue)
692 }
693
694 #[doc(alias = "sys::ffi::playdate_sys::setMenuItemValue")]
696 fn set_menu_item_value(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, value: c_int) {
697 *sys::api!(system.setMenuItemValue)
698 }
699
700 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemTitle")]
702 fn get_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *const c_char {
703 *sys::api!(system.getMenuItemTitle)
704 }
705
706 #[doc(alias = "sys::ffi::playdate_sys::setMenuItemTitle")]
708 fn set_menu_item_title(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem, title: *const c_char) {
709 *sys::api!(system.setMenuItemTitle)
710 }
711
712 #[doc(alias = "sys::ffi::playdate_sys::getMenuItemUserdata")]
714 fn get_menu_item_userdata(&self) -> unsafe extern "C" fn(menuItem: *mut PDMenuItem) -> *mut c_void {
715 *sys::api!(system.getMenuItemUserdata)
716 }
717
718 #[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
720 fn remove_all_menu_items(&self) -> unsafe extern "C" fn() { *sys::api!(system.removeAllMenuItems) }
721
722 #[doc(alias = "sys::ffi::playdate_sys::setMenuImage")]
724 fn set_menu_image(&self) -> unsafe extern "C" fn(*mut LCDBitmap, i32) { *sys::api!(system.setMenuImage) }
725 }
726}