playdate_display/
lib.rs

1#![cfg_attr(not(test), no_std)]
2extern crate sys;
3
4use core::ffi::c_float;
5use core::ffi::c_int;
6use core::ffi::c_uint;
7
8
9#[derive(Debug, Clone, Copy)]
10pub struct Display<Api = api::Default>(Api);
11
12impl Display<api::Default> {
13	/// Creates default [`Display`] without type parameter requirement.
14	///
15	/// Uses ZST [`api::Default`].
16	#[allow(non_snake_case)]
17	pub fn Default() -> Self { Self(Default::default()) }
18}
19
20impl Display<api::Cache> {
21	/// Creates [`Display`] without type parameter requirement.
22	///
23	/// Uses [`api::Cache`].
24	#[allow(non_snake_case)]
25	pub fn Cached() -> Self { Self(Default::default()) }
26}
27
28impl<Api: Default + api::Api> Default for Display<Api> {
29	fn default() -> Self { Self(Default::default()) }
30}
31
32impl<Api: Default + api::Api> Display<Api> {
33	pub fn new() -> Self { Self(Default::default()) }
34}
35
36impl<Api: api::Api> Display<Api> {
37	pub fn new_with(api: Api) -> Self { Self(api) }
38}
39
40
41impl Display<api::Default> {
42	pub const COLUMNS: u32 = sys::ffi::LCD_COLUMNS;
43	pub const ROWS: u32 = sys::ffi::LCD_ROWS;
44	pub const ROW_SIZE: u32 = sys::ffi::LCD_ROWSIZE;
45	pub const SCREEN_RECT: sys::ffi::LCDRect = sys::ffi::LCDRect { left: 0,
46	                                                               right: 0,
47	                                                               top: Self::COLUMNS as _,
48	                                                               bottom: Self::ROWS as _ };
49}
50
51
52impl<Api: api::Api> Display<Api> {
53	/// Returns the width of the display, taking the current scale into account;
54	///
55	/// e.g., if the scale is `2`, this function returns `200` instead of `400`.
56	///
57	/// See also [`Display::COLUMNS`].
58	///
59	/// Equivalent to [`sys::ffi::playdate_display::getWidth`]
60	#[doc(alias = "sys::ffi::playdate_display::getWidth")]
61	pub fn width(&self) -> c_int {
62		let f = self.0.get_width();
63		unsafe { f() }
64	}
65
66	/// Returns the height of the display, taking the current scale into account;
67	///
68	/// e.g., if the scale is `2`, this function returns `120` instead of `240`.
69	///
70	/// See also [`Display::ROWS`] and [`Display::ROW_SIZE`].
71	///
72	/// Equivalent to [`sys::ffi::playdate_display::getHeight`]
73	#[doc(alias = "sys::ffi::playdate_display::getHeight")]
74	pub fn height(&self) -> c_int {
75		let f = self.0.get_height();
76		unsafe { f() }
77	}
78
79	/// Sets the nominal refresh rate in frames per second.
80	///
81	/// Default is 20 fps, the maximum rate supported by the hardware for full-frame updates.
82	///
83	/// Equivalent to [`sys::ffi::playdate_display::setRefreshRate`]
84	#[doc(alias = "sys::ffi::playdate_display::setRefreshRate")]
85	pub fn set_refresh_rate(&self, rate: c_float) {
86		let f = self.0.set_refresh_rate();
87		unsafe { f(rate) }
88	}
89
90	/// If `value` is `true`, the frame buffer is drawn inverted—black instead of white, and vice versa.
91	///
92	/// Equivalent to [`sys::ffi::playdate_display::setInverted`]
93	#[doc(alias = "sys::ffi::playdate_display::setInverted")]
94	pub fn set_inverted(&self, value: bool) {
95		let f = self.0.set_inverted();
96		unsafe { f(value as _) }
97	}
98
99	/// Sets the display scale factor.
100	///
101	/// The top-left corner of the frame buffer is scaled up to fill the display;
102	///
103	/// e.g., if the scale is set to [`DisplayScale::Quad`],
104	/// the pixels in rectangle `[0, 100] x [0, 60]` are drawn on the screen as `4 x 4` squares.
105	///
106	/// Equivalent to [`sys::ffi::playdate_display::setScale`]
107	#[doc(alias = "sys::ffi::playdate_display::setScale")]
108	pub fn set_scale(&self, scale: DisplayScale) { self.set_scale_raw(scale.into()); }
109
110	/// Sets the display scale factor.
111	///
112	/// Valid values for `scale` are `1`, `2`, `4`, and `8`.
113	///
114	/// The top-left corner of the frame buffer is scaled up to fill the display;
115	/// e.g., if the scale is set to `4`, the pixels in rectangle `[0, 100] x [0, 60]` are drawn on the screen as `4 x 4` squares.
116	///
117	/// See also [`Display::set_scale`].
118	///
119	/// Equivalent to [`sys::ffi::playdate_display::setScale`]
120	#[doc(alias = "sys::ffi::playdate_display::setScale")]
121	pub fn set_scale_raw(&self, scale: c_uint) {
122		let f = self.0.set_scale();
123		unsafe { f(scale) }
124	}
125
126	/// Adds a mosaic effect to the display.
127	///
128	/// Valid `x` and `y` values are between `0` and `3`, inclusive.
129	///
130	/// Equivalent to [`sys::ffi::playdate_display::setMosaic`]
131	#[doc(alias = "sys::ffi::playdate_display::setMosaic")]
132	pub fn set_mosaic(&self, x: c_uint, y: c_uint) {
133		let f = self.0.set_mosaic();
134		unsafe { f(x, y) }
135	}
136
137	/// Flips the display on the `x` or `y` axis, or both.
138	///
139	/// Equivalent to [`sys::ffi::playdate_display::setFlipped`]
140	#[doc(alias = "sys::ffi::playdate_display::setFlipped")]
141	pub fn set_flipped(&self, x: bool, y: bool) {
142		let f = self.0.set_flipped();
143		unsafe { f(x as _, y as _) }
144	}
145
146	/// Offsets the display by the given amount.
147	///
148	/// Areas outside of the displayed area are filled with the current background color.
149	///
150	/// See also [`playdate-graphics::set_background_color`].
151	///
152	/// Equivalent to [`sys::ffi::playdate_display::setOffset`]
153	#[doc(alias = "sys::ffi::playdate_display::setOffset")]
154	pub fn set_offset(&self, x: c_int, y: c_int) {
155		let f = self.0.set_offset();
156		unsafe { f(x, y) }
157	}
158}
159
160
161#[repr(u8)]
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum DisplayScale {
164	Normal = 1,
165	Double = 2,
166	Quad = 4,
167	Eight = 8,
168}
169
170impl Into<c_uint> for DisplayScale {
171	#[inline(always)]
172	fn into(self) -> c_uint { (self as u8).into() }
173}
174
175impl core::fmt::Display for DisplayScale {
176	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", *self as u8) }
177}
178
179impl DisplayScale {
180	#[inline(always)]
181	pub const fn as_u8(self) -> u8 { self as u8 }
182	#[inline(always)]
183	pub const fn as_int(self) -> c_int { self as u8 as _ }
184}
185
186
187pub mod api {
188	use core::ffi::c_float;
189	use core::ffi::c_int;
190	use core::ffi::c_uint;
191	use core::ptr::NonNull;
192	use sys::ffi::playdate_display;
193
194
195	/// Default display api end-point, ZST.
196	///
197	/// All calls approximately costs ~3 derefs.
198	#[derive(Debug, Clone, Copy, core::default::Default)]
199	pub struct Default;
200	impl Api for Default {}
201
202
203	/// Cached display api end-point.
204	///
205	/// Stores one reference, so size on stack is eq `usize`.
206	///
207	/// All calls approximately costs ~1 deref.
208	#[derive(Clone, Copy)]
209	#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
210	pub struct Cache(&'static playdate_display);
211
212	impl core::default::Default for Cache {
213		fn default() -> Self { Self(sys::api!(display)) }
214	}
215
216	impl From<*const playdate_display> for Cache {
217		#[inline(always)]
218		fn from(ptr: *const playdate_display) -> Self { Self(unsafe { ptr.as_ref() }.expect("display")) }
219	}
220
221	impl From<&'static playdate_display> for Cache {
222		#[inline(always)]
223		fn from(r: &'static playdate_display) -> Self { Self(r) }
224	}
225
226	impl From<NonNull<playdate_display>> for Cache {
227		#[inline(always)]
228		fn from(ptr: NonNull<playdate_display>) -> Self { Self(unsafe { ptr.as_ref() }) }
229	}
230
231	impl From<&'_ NonNull<playdate_display>> for Cache {
232		#[inline(always)]
233		fn from(ptr: &NonNull<playdate_display>) -> Self { Self(unsafe { ptr.as_ref() }) }
234	}
235
236
237	impl Api for Cache {
238		#[inline(always)]
239		fn get_width(&self) -> unsafe extern "C" fn() -> c_int { self.0.getWidth.expect("getWidth") }
240
241		#[inline(always)]
242		fn get_height(&self) -> unsafe extern "C" fn() -> c_int { self.0.getHeight.expect("getHeight") }
243
244		#[inline(always)]
245		fn set_refresh_rate(&self) -> unsafe extern "C" fn(rate: c_float) {
246			self.0.setRefreshRate.expect("setRefreshRate")
247		}
248
249		#[inline(always)]
250		fn set_inverted(&self) -> unsafe extern "C" fn(flag: c_int) { self.0.setInverted.expect("setInverted") }
251
252		#[inline(always)]
253		fn set_scale(&self) -> unsafe extern "C" fn(s: c_uint) { self.0.setScale.expect("setScale") }
254
255		#[inline(always)]
256		fn set_mosaic(&self) -> unsafe extern "C" fn(x: c_uint, y: c_uint) { self.0.setMosaic.expect("setMosaic") }
257
258		#[inline(always)]
259		fn set_flipped(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.setFlipped.expect("setFlipped") }
260
261		#[inline(always)]
262		fn set_offset(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.setOffset.expect("setOffset") }
263	}
264
265
266	pub trait Api {
267		/// Returns [`sys::ffi::playdate_display::getWidth`]
268		#[doc(alias = "sys::ffi::playdate_display::getWidth")]
269		fn get_width(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(display.getWidth) }
270		/// Returns [`sys::ffi::playdate_display::getHeight`]
271		#[doc(alias = "sys::ffi::playdate_display::getHeight")]
272		fn get_height(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(display.getHeight) }
273		/// Returns [`sys::ffi::playdate_display::setRefreshRate`]
274		#[doc(alias = "sys::ffi::playdate_display::setRefreshRate")]
275		fn set_refresh_rate(&self) -> unsafe extern "C" fn(rate: c_float) { *sys::api!(display.setRefreshRate) }
276		/// Returns [`sys::ffi::playdate_display::setInverted`]
277		#[doc(alias = "sys::ffi::playdate_display::setInverted")]
278		fn set_inverted(&self) -> unsafe extern "C" fn(flag: c_int) { *sys::api!(display.setInverted) }
279		/// Returns [`sys::ffi::playdate_display::setScale`]
280		#[doc(alias = "sys::ffi::playdate_display::setScale")]
281		fn set_scale(&self) -> unsafe extern "C" fn(s: c_uint) { *sys::api!(display.setScale) }
282		/// Returns [`sys::ffi::playdate_display::setMosaic`]
283		#[doc(alias = "sys::ffi::playdate_display::setMosaic")]
284		fn set_mosaic(&self) -> unsafe extern "C" fn(x: c_uint, y: c_uint) { *sys::api!(display.setMosaic) }
285		/// Returns [`sys::ffi::playdate_display::setFlipped`]
286		#[doc(alias = "sys::ffi::playdate_display::setFlipped")]
287		fn set_flipped(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { *sys::api!(display.setFlipped) }
288		/// Returns [`sys::ffi::playdate_display::setOffset`]
289		#[doc(alias = "sys::ffi::playdate_display::setOffset")]
290		fn set_offset(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { *sys::api!(display.setOffset) }
291	}
292}