playdate_menu/
lib.rs

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	/// Gets the display title of the menu item.
42	///
43	/// Returns [`sys::ffi::playdate_sys::getMenuItemTitle`]
44	#[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	/// Sets the display title of the menu item.
51	///
52	/// Returns [`sys::ffi::playdate_sys::setMenuItemTitle`]
53	#[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		/* TODO: check ptr is miss-aligned */
69		{
70			return None;
71		}
72
73		unsafe { (ptr as *mut CallbackUserData<UD>).as_mut() }
74	}
75
76	/// Set `userdata`, replace and return old userdata.
77	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	/// Gets the integer value of the menu item.
86	///
87	/// See also [`MenuItem::set_value`].
88	///
89	/// Equivalent to [`sys::ffi::playdate_sys::getMenuItemValue`]
90	#[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	/// Sets the integer value of the menu item.
97	///
98	/// For checkmark menu items ([`CheckMenuItem`]), `1` means checked, `0` unchecked.
99	///
100	/// For option menu items ([`OptionsMenuItem`]), the value indicates the array index of the currently selected option.
101	///
102	/// See also [`CheckMenuItem::is_checked`], [`OptionsMenuItem::selected_option`].
103	///
104	/// Equivalent to [`sys::ffi::playdate_sys::setMenuItemValue`]
105	#[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		// || ptr.addr() & (core::mem::align_of::<CallbackUserData<UD>>() - 1) != 0
123		{
124			// invalid pointer, mostly means that the userdata was not set/initialized
125			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		// convert (callback, userdata) -> pointer:
269		let ptr = Box::into_raw(Box::from((callback, ud)));
270		(Some(proxy::<UD> as _), ptr as *mut _)
271	} else {
272		// we can get user data smaller:
273		// convert userdata -> pointer:
274		// let ptr = Box::into_raw(Box::from(userdata));
275		// Ok((None, ptr as _))
276
277		// but better to have same for consistency,
278		// required for get/set userdata:
279		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	/// Equivalent to [`sys::ffi::playdate_sys::getMenuItemValue`]
288	#[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	/// The array index of the currently selected option.
296	///
297	/// Equivalent to [`sys::ffi::playdate_sys::getMenuItemValue`]
298	#[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			// we have to drop userdata:
308			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/// Removes all custom menu items from the system menu.
330///
331/// Equivalent to [`sys::ffi::playdate_sys::removeAllMenuItems`]
332#[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/// Removes all custom menu items from the system menu.
337///
338/// Uses given `api`.
339///
340/// Equivalent to [`sys::ffi::playdate_sys::removeAllMenuItems`]
341#[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/// A game can optionally provide an image to be displayed alongside the system menu.
348/// bitmap must be a 400x240 LCDBitmap. All important content should be in the
349/// left half of the image in an area 200 pixels wide, as the menu will obscure the rest.
350/// The right side of the image will be visible briefly as the menu animates in and out.
351///
352/// Optionally, a non-zero xoffset, can be provided. This must be a number between 0 and 200
353/// and will cause the menu image to animate to a position offset left by xoffset pixels
354/// as the menu is animated in.
355///
356/// This function could be called in response to the kEventPause event in your implementation
357/// of `event_handler()`.
358///
359/// Equivalent to [`sys::ffi::playdate_sys::setMenuImage`]
360#[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/// A game can optionally provide an image to be displayed alongside the system menu.
368/// bitmap must be a 400x240 LCDBitmap. All important content should be in the
369/// left half of the image in an area 200 pixels wide, as the menu will obscure the rest.
370/// The right side of the image will be visible briefly as the menu animates in and out.
371///
372/// Optionally, a non-zero xoffset, can be provided. This must be a number between 0 and 200
373/// and will cause the menu image to animate to a position offset left by xoffset pixels
374/// as the menu is animated in.
375///
376/// This function could be called in response to the kEventPause event in your implementation
377/// of `event_handler()`.
378///
379/// Use given `api`.
380///
381/// Equivalent to [`sys::ffi::playdate_sys::setMenuImage`]
382#[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	/// Removes all custom menu items from the system menu.
390	///
391	/// Equivalent to [`sys::ffi::playdate_sys::removeAllMenuItems`]
392	#[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
393	fn remove_all_menu_items(&self);
394
395	/// A game can optionally provide an image to be displayed alongside the system menu.
396	/// bitmap must be a 400x240 LCDBitmap. All important content should be in the
397	/// left half of the image in an area 200 pixels wide, as the menu will obscure the rest.
398	/// The right side of the image will be visible briefly as the menu animates in and out.
399	///
400	/// Optionally, a non-zero xoffset, can be provided. This must be a number between 0 and 200
401	/// and will cause the menu image to animate to a position offset left by xoffset pixels
402	/// as the menu is animated in.
403	///
404	/// This function could be called in response to the kEventPause event in your implementation
405	/// of `event_handler()`.
406	///
407	/// Equivalent to [`sys::ffi::playdate_sys::setMenuImage`]
408	#[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	/// Default system menu api end-point, ZST.
458	///
459	/// All calls approximately costs ~3 derefs.
460	#[derive(Debug, Clone, Copy, core::default::Default)]
461	pub struct Default;
462	impl Api for Default {}
463
464
465	/// Cached system menu api end-point.
466	///
467	/// Stores one reference, so size on stack is eq `usize`.
468	///
469	/// All calls approximately costs ~1 deref.
470	#[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		/// Returns [`sys::ffi::playdate_sys::addMenuItem`]
650		#[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		/// Returns [`sys::ffi::playdate_sys::addCheckmarkMenuItem`]
660		#[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		/// Returns [`sys::ffi::playdate_sys::addOptionsMenuItem`]
671		#[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		/// Returns [`sys::ffi::playdate_sys::removeMenuItem`]
683		#[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		/// Returns [`sys::ffi::playdate_sys::getMenuItemValue`]
689		#[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		/// Returns [`sys::ffi::playdate_sys::setMenuItemValue`]
695		#[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		/// Returns [`sys::ffi::playdate_sys::getMenuItemTitle`]
701		#[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		/// Returns [`sys::ffi::playdate_sys::setMenuItemTitle`]
707		#[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		/// Returns [`sys::ffi::playdate_sys::getMenuItemUserdata`]
713		#[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		/// Returns [`sys::ffi::playdate_sys::removeAllMenuItems`]
719		#[doc(alias = "sys::ffi::playdate_sys::removeAllMenuItems")]
720		fn remove_all_menu_items(&self) -> unsafe extern "C" fn() { *sys::api!(system.removeAllMenuItems) }
721
722		/// Returns [`sys::ffi::playdate_sys::setMenuImage`]
723		#[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}