diff --git a/Cargo.lock b/Cargo.lock index 6c84adc7..3f165209 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2507,7 +2507,7 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "playdate" -version = "0.1.9" +version = "0.1.10" dependencies = [ "playdate-controls", "playdate-display", @@ -2572,7 +2572,7 @@ dependencies = [ [[package]] name = "playdate-controls" -version = "0.2.1" +version = "0.3.0" dependencies = [ "playdate-display", "playdate-graphics", @@ -2582,7 +2582,7 @@ dependencies = [ [[package]] name = "playdate-display" -version = "0.3.2" +version = "0.3.3" dependencies = [ "playdate-sys", ] @@ -2597,7 +2597,7 @@ dependencies = [ [[package]] name = "playdate-graphics" -version = "0.3.7" +version = "0.3.8" dependencies = [ "playdate-color", "playdate-display", @@ -2627,7 +2627,7 @@ dependencies = [ [[package]] name = "playdate-sprite" -version = "0.2.2" +version = "0.2.3" dependencies = [ "playdate-display", "playdate-graphics", @@ -2646,7 +2646,7 @@ dependencies = [ [[package]] name = "playdate-system" -version = "0.3.3" +version = "0.3.4" dependencies = [ "playdate-sys", ] diff --git a/Cargo.toml b/Cargo.toml index b47b02f2..d68addcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ repository = "https://github.com/boozook/playdate.git" [workspace.dependencies] color = { version = "0.2", path = "api/color", package = "playdate-color", default-features = false } -ctrl = { version = "0.2", path = "api/ctrl", package = "playdate-controls", default-features = false } +ctrl = { version = "0.3", path = "api/ctrl", package = "playdate-controls", default-features = false } display = { version = "0.3", path = "api/display", package = "playdate-display", default-features = false } fs = { version = "0.2", path = "api/fs", package = "playdate-fs", default-features = false } gfx = { version = "0.3", path = "api/gfx", package = "playdate-graphics", default-features = false } diff --git a/api/ctrl/Cargo.toml b/api/ctrl/Cargo.toml index 54d91121..d3ccce45 100644 --- a/api/ctrl/Cargo.toml +++ b/api/ctrl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-controls" -version = "0.2.1" +version = "0.3.0" readme = "README.md" description = "High-level controls API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] @@ -22,9 +22,9 @@ bindgen-static = ["sys/bindgen-static"] bindings-derive-debug = ["sys/bindings-derive-debug"] -[dependencies.sys] -workspace = true -default-features = false +[dependencies] +sys = { workspace = true, default-features = false } +system = { workspace = true, default-features = false } [dev-dependencies] gfx = { workspace = true, default-features = false } diff --git a/api/ctrl/examples/accelerometer.rs b/api/ctrl/examples/accelerometer.rs index 737effbf..335339fb 100644 --- a/api/ctrl/examples/accelerometer.rs +++ b/api/ctrl/examples/accelerometer.rs @@ -11,7 +11,6 @@ use controls::peripherals::Accelerometer; use display::Display; use gfx::color::Color; -use sys::error::NullPtrError; use sys::ffi::PlaydateAPI; use sys::EventLoopCtrl; use system::prelude::*; @@ -50,7 +49,7 @@ fn init() -> EventLoopCtrl { graphics.clear(Color::WHITE); // get accelerometer data - let (x, y, z) = Accelerometer::get().ok_or(NullPtrError)?; + let (x, y, z) = Accelerometer::Default().get(); // render state to string let text = format!("[{x:.2},{y:.2},{z:.2}]"); @@ -93,18 +92,18 @@ fn event_handler(_: NonNull, event: SystemEvent, _: u32) -> EventLo match event { SystemEvent::Init => { // turn on the accelerometer - Accelerometer::enable().ok_or(NullPtrError)?; + Accelerometer::Default().enable(); return init(); }, SystemEvent::Resume | SystemEvent::Unlock => { // turn on the accelerometer - Accelerometer::enable().ok_or(NullPtrError)?; + Accelerometer::Default().enable(); }, SystemEvent::Terminate | SystemEvent::Pause | SystemEvent::Lock => { // turn off the accelerometer - Accelerometer::disable().ok_or(NullPtrError)?; + Accelerometer::Default().disable(); }, // Ignore any other events, just for this minimalistic example diff --git a/api/ctrl/examples/buttons.rs b/api/ctrl/examples/buttons.rs index bf176136..5e7400ec 100644 --- a/api/ctrl/examples/buttons.rs +++ b/api/ctrl/examples/buttons.rs @@ -18,7 +18,6 @@ use display::Display; use gfx::color::Color; use sys::EventLoopCtrl; use sys::ffi::PlaydateAPI; -use sys::error::NullPtrError; use system::prelude::*; @@ -54,15 +53,16 @@ fn event_handler(_: NonNull, event: SystemEvent, _: u32) -> EventLo // Create cached end-points that we using every update let system = system::System::Cached(); let graphics = gfx::Graphics::Cached(); + let buttons = Buttons::Cached(); // Register update handler // Just to draw current playback position system.set_update_callback_boxed( - move |pos| { + move |(pos, buttons)| { graphics.clear(Color::WHITE); // Get buttons state - let buttons = Buttons::get().ok_or(NullPtrError)?; + let buttons = buttons.get(); // Render buttons state to string let text: Cow = if buttons.current.is_empty() { @@ -108,7 +108,7 @@ fn event_handler(_: NonNull, event: SystemEvent, _: u32) -> EventLo UpdateCtrl::Continue }, - pos, + (pos, buttons), ); EventLoopCtrl::Continue diff --git a/api/ctrl/src/api.rs b/api/ctrl/src/api.rs new file mode 100644 index 00000000..dce7dd07 --- /dev/null +++ b/api/ctrl/src/api.rs @@ -0,0 +1,181 @@ +use core::ffi::c_float; +use core::ffi::c_int; +use core::ptr::NonNull; + +use sys::ffi::PDButtons; +use sys::ffi::PDPeripherals; +use sys::ffi::playdate_sys; + +/// Default system api end-point, ZST. +/// +/// All calls approximately costs ~3 derefs. +#[derive(Debug, Clone, Copy, core::default::Default)] +pub struct Default; +impl Api for Default {} + + +/// Cached system api end-point. +/// +/// Stores one reference, so size on stack is eq `usize`. +/// +/// All calls approximately costs ~1 deref. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))] +pub struct Cache(&'static playdate_sys); + +impl core::default::Default for Cache { + fn default() -> Self { Self(api!(system)) } +} + +impl From<*const playdate_sys> for Cache { + #[inline(always)] + fn from(ptr: *const playdate_sys) -> Self { Self(unsafe { ptr.as_ref() }.expect("system")) } +} + +impl From<&'static playdate_sys> for Cache { + #[inline(always)] + fn from(r: &'static playdate_sys) -> Self { Self(r) } +} + +impl From> for Cache { + #[inline(always)] + fn from(ptr: NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } +} + +impl From<&'_ NonNull> for Cache { + #[inline(always)] + fn from(ptr: &NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } +} + +impl From for Cache { + #[inline(always)] + fn from(api: system::api::Cache) -> Self { Self(api.as_inner()) } +} + + +impl Api for Cache { + #[inline(always)] + fn set_peripherals_enabled(&self) -> unsafe extern "C" fn(mask: PDPeripherals) { + self.0.setPeripheralsEnabled.expect("setPeripheralsEnabled") + } + + #[inline(always)] + fn get_button_state( + &self) + -> unsafe extern "C" fn(current: *mut PDButtons, pushed: *mut PDButtons, released: *mut PDButtons) { + self.0.getButtonState.expect("getButtonState") + } + + #[inline(always)] + fn get_accelerometer( + &self) + -> unsafe extern "C" fn(out_x: *mut c_float, out_y: *mut c_float, out_z: *mut c_float) { + self.0.getAccelerometer.expect("getAccelerometer") + } + + #[inline(always)] + fn get_crank_change(&self) -> unsafe extern "C" fn() -> c_float { + self.0.getCrankChange.expect("getCrankChange") + } + + #[inline(always)] + fn get_crank_angle(&self) -> unsafe extern "C" fn() -> c_float { self.0.getCrankAngle.expect("getCrankAngle") } + + #[inline(always)] + fn is_crank_docked(&self) -> unsafe extern "C" fn() -> c_int { self.0.isCrankDocked.expect("isCrankDocked") } + + #[inline(always)] + fn set_crank_sounds_disabled(&self) -> unsafe extern "C" fn(flag: c_int) -> c_int { + self.0.setCrankSoundsDisabled.expect("setCrankSoundsDisabled") + } +} + + +impl Api for system::api::Default {} + +impl Api for system::api::Cache { + #[inline(always)] + fn set_peripherals_enabled(&self) -> unsafe extern "C" fn(mask: PDPeripherals) { + self.as_inner() + .setPeripheralsEnabled + .expect("setPeripheralsEnabled") + } + + #[inline(always)] + fn get_button_state( + &self) + -> unsafe extern "C" fn(current: *mut PDButtons, pushed: *mut PDButtons, released: *mut PDButtons) { + self.as_inner().getButtonState.expect("getButtonState") + } + + #[inline(always)] + fn get_accelerometer( + &self) + -> unsafe extern "C" fn(out_x: *mut c_float, out_y: *mut c_float, out_z: *mut c_float) { + self.as_inner().getAccelerometer.expect("getAccelerometer") + } + + #[inline(always)] + fn get_crank_change(&self) -> unsafe extern "C" fn() -> c_float { + self.as_inner().getCrankChange.expect("getCrankChange") + } + + #[inline(always)] + fn get_crank_angle(&self) -> unsafe extern "C" fn() -> c_float { + self.as_inner().getCrankAngle.expect("getCrankAngle") + } + + #[inline(always)] + fn is_crank_docked(&self) -> unsafe extern "C" fn() -> c_int { + self.as_inner().isCrankDocked.expect("isCrankDocked") + } + + #[inline(always)] + fn set_crank_sounds_disabled(&self) -> unsafe extern "C" fn(flag: c_int) -> c_int { + self.as_inner() + .setCrankSoundsDisabled + .expect("setCrankSoundsDisabled") + } +} + +pub trait Api { + /// Returns [`sys::ffi::playdate_sys::setPeripheralsEnabled`] + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + fn set_peripherals_enabled(&self) -> unsafe extern "C" fn(mask: PDPeripherals) { + *sys::api!(system.setPeripheralsEnabled) + } + + /// Returns [`sys::ffi::playdate_sys::getButtonState`] + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] + fn get_button_state( + &self) + -> unsafe extern "C" fn(current: *mut PDButtons, pushed: *mut PDButtons, released: *mut PDButtons) { + *sys::api!(system.getButtonState) + } + + /// Returns [`sys::ffi::playdate_sys::getAccelerometer`] + #[doc(alias = "sys::ffi::playdate_sys::getAccelerometer")] + fn get_accelerometer( + &self) + -> unsafe extern "C" fn(out_x: *mut c_float, out_y: *mut c_float, out_z: *mut c_float) { + *sys::api!(system.getAccelerometer) + } + + /// Returns [`sys::ffi::playdate_sys::getCrankChange`] + #[doc(alias = "sys::ffi::playdate_sys::getCrankChange")] + fn get_crank_change(&self) -> unsafe extern "C" fn() -> c_float { *sys::api!(system.getCrankChange) } + + /// Returns [`sys::ffi::playdate_sys::getCrankAngle`] + #[doc(alias = "sys::ffi::playdate_sys::getCrankAngle")] + fn get_crank_angle(&self) -> unsafe extern "C" fn() -> c_float { *sys::api!(system.getCrankAngle) } + + /// Returns [`sys::ffi::playdate_sys::isCrankDocked`] + #[doc(alias = "sys::ffi::playdate_sys::isCrankDocked")] + fn is_crank_docked(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(system.isCrankDocked) } + + /// Returns [`sys::ffi::playdate_sys::setCrankSoundsDisabled`] + #[doc(alias = "sys::ffi::playdate_sys::setCrankSoundsDisabled")] + fn set_crank_sounds_disabled(&self) -> unsafe extern "C" fn(flag: c_int) -> c_int { + *sys::api!(system.setCrankSoundsDisabled) + } +} diff --git a/api/ctrl/src/lib.rs b/api/ctrl/src/lib.rs index bdb05e7e..4fe5b87c 100644 --- a/api/ctrl/src/lib.rs +++ b/api/ctrl/src/lib.rs @@ -4,6 +4,9 @@ #[macro_use] extern crate alloc; +#[macro_use] extern crate sys; + +pub mod api; pub mod buttons; pub mod peripherals; diff --git a/api/ctrl/src/peripherals.rs b/api/ctrl/src/peripherals.rs index 6da884da..2664f9a5 100644 --- a/api/ctrl/src/peripherals.rs +++ b/api/ctrl/src/peripherals.rs @@ -1,153 +1,308 @@ use core::ffi::c_float; use sys::ffi::PDButtons; use sys::ffi::PDPeripherals; -use sys::api; +use crate::api; -macro_rules! sysfn { - ($name:ident) => {{ unsafe { (*($crate::sys::api()?.system)).$name?() } }}; - ($name:ident, $($arg:tt)*) => {{ unsafe { (*($crate::sys::api()?.system)).$name?($($arg)*) } }}; +/// Peripherals +#[derive(Debug, Clone, Copy)] +pub struct Peripherals(Api); + +impl Peripherals { + /// Creates default [`Peripherals`] without type parameter requirement. + /// + /// Uses ZST [`api::Default`]. + #[allow(non_snake_case)] + pub fn Default() -> Self { Self(Default::default()) } } +impl Peripherals { + /// Creates [`Peripherals`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} -/// Wrapped Peripherals API. -/// -/// Currently there's only one peripheral - accelerometer. -pub struct Peripherals; -impl Peripherals { - #![allow(non_snake_case)] +impl Default for Peripherals { + fn default() -> Self { Self(Default::default()) } +} - pub fn enable(value: PDPeripherals) -> Option<()> { - unsafe { (*(api()?.system)).setPeripheralsEnabled?(value) }.into() - } - pub fn enable_accelerometer() -> Option<()> { - unsafe { (*(api()?.system)).setPeripheralsEnabled?(Self::Accelerometer()) }.into() - } +impl Peripherals { + pub fn new() -> Self { Self(Default::default()) } +} + +impl Peripherals { + pub fn new_with(api: Api) -> Self { Self(api) } +} - pub fn enable_all() -> Option<()> { unsafe { (*(api()?.system)).setPeripheralsEnabled?(Self::All()) }.into() } - pub fn disable_all() -> Option<()> { - unsafe { (*(api()?.system)).setPeripheralsEnabled?(Self::None()) }.into() +impl Peripherals where Api: Copy { + pub fn accelerometer(&self) -> Accelerometer { Accelerometer(self.0) } + pub fn buttons(&self) -> Buttons { Buttons(self.0) } + pub fn crank(&self) -> Crank { Crank(self.0) } +} + +impl Peripherals { + /// Enables specified peripheral. + /// + /// Equivalent to [`sys::ffi::playdate_sys::setPeripheralsEnabled`] + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + pub fn enable(&self, value: PDPeripherals) { + let f = self.0.set_peripherals_enabled(); + unsafe { f(value) } } - pub const fn None() -> PDPeripherals { PDPeripherals::kNone } - pub const fn Accelerometer() -> PDPeripherals { PDPeripherals::kAccelerometer } - pub const fn All() -> PDPeripherals { PDPeripherals::kAllPeripherals } + /// By default, the accelerometer is disabled to save (a small amount of) power. + /// + /// To use a peripheral, it must first be enabled via this function. + /// + /// Accelerometer data is not available until the next update cycle after it’s enabled. + /// + /// Equivalent to [`sys::ffi::playdate_sys::setPeripheralsEnabled`] + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + #[inline(always)] + pub fn enable_accelerometer(&self) { self.enable(Peripherals::Accelerometer) } + + /// Enables all peripherals. + /// + /// Equivalent to [`sys::ffi::playdate_sys::setPeripheralsEnabled`] + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + #[inline(always)] + pub fn enable_all(&self) { self.enable(Peripherals::All) } + + /// Disables all peripherals. + /// + /// Equivalent to [`sys::ffi::playdate_sys::setPeripheralsEnabled`] + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + #[inline(always)] + pub fn disable_all(&self) { self.enable(Peripherals::None) } +} + +impl Peripherals { + #![allow(non_upper_case_globals)] + pub const None: PDPeripherals = PDPeripherals::kNone; + pub const Accelerometer: PDPeripherals = PDPeripherals::kAccelerometer; + pub const All: PDPeripherals = PDPeripherals::kAllPeripherals; +} + + +/// Accelerometer +#[derive(Debug, Clone, Copy)] +pub struct Accelerometer(Api); + +impl Accelerometer { + /// Creates default [`Accelerometer`] without type parameter requirement. + /// + /// Uses ZST [`api::Default`]. + #[allow(non_snake_case)] + pub fn Default() -> Self { Self(Default::default()) } +} + +impl Accelerometer { + /// Creates [`Accelerometer`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} + +impl Default for Accelerometer { + fn default() -> Self { Self(Default::default()) } } +impl Accelerometer { + pub fn new() -> Self { Self(Default::default()) } +} -/// Wrapped Accelerometer API. -pub struct Accelerometer; -impl Accelerometer { - /// Enable accelerometer only. +impl Accelerometer { + pub fn new_with(api: Api) -> Self { Self(api) } +} + +impl Accelerometer { + /// Enables accelerometer only. + /// /// By default, the accelerometer is disabled to save (a small amount of) power. + /// /// To use a peripheral, it must first be enabled via this function. + /// /// Accelerometer data is not available until the next update cycle after it’s enabled. /// - /// Uses [`setPeripheralsEnabled`][crate::sys::ffi::playdate_sys::setPeripheralsEnabled]. - pub fn enable() -> Option<()> { - unsafe { (*(api()?.system)).setPeripheralsEnabled?(PDPeripherals::kAccelerometer) }.into() + /// Equivalent to [`sys::ffi::playdate_sys::setPeripheralsEnabled`]. + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + pub fn enable(&self) { + let f = self.0.set_peripherals_enabled(); + unsafe { f(Peripherals::Accelerometer) } } - /// Uses [`setPeripheralsEnabled`][crate::sys::ffi::playdate_sys::setPeripheralsEnabled]. + /// ⚠️ Disables all peripherals including accelerometer. /// - /// ⚠️ Disables all peripherals. /// Currently it doesn't matter because there's just one peripheral - accelerometer. - pub fn disable() -> Option<()> { - unsafe { (*(api()?.system)).setPeripheralsEnabled?(PDPeripherals::kNone) }.into() + /// + /// Equivalent to [`sys::ffi::playdate_sys::setPeripheralsEnabled`]. + #[doc(alias = "sys::ffi::playdate_sys::setPeripheralsEnabled")] + pub fn disable(&self) { + let f = self.0.set_peripherals_enabled(); + unsafe { f(Peripherals::None) } } - /// Uses [`getAccelerometer`][crate::sys::ffi::playdate_sys::getAccelerometer]. - /// /// Returns `(x, y, z)` accelerometer data. - pub fn get() -> Option<(c_float, c_float, c_float)> { + /// + /// Equivalent to [`sys::ffi::playdate_sys::getAccelerometer`]. + #[doc(alias = "sys::ffi::playdate_sys::getAccelerometer")] + pub fn get(&self) -> (c_float, c_float, c_float) { let mut outx: c_float = 0.0; let mut outy: c_float = 0.0; let mut outz: c_float = 0.0; - unsafe { (*(api()?.system)).getAccelerometer?(&mut outx, &mut outy, &mut outz) } - Some((outx, outy, outz)) + let f = self.0.get_accelerometer(); + unsafe { f(&mut outx, &mut outy, &mut outz) }; + (outx, outy, outz) } /// Gets accelerometer data directly to `x`, `y` and `z`. - pub fn get_to(outx: &mut c_float, outy: &mut c_float, outz: &mut c_float) -> Option<()> { - unsafe { (*(api()?.system)).getAccelerometer?(outx, outy, outz) }.into() + /// + /// Equivalent to [`sys::ffi::playdate_sys::getAccelerometer`]. + #[doc(alias = "sys::ffi::playdate_sys::getAccelerometer")] + pub fn get_to(&self, outx: &mut c_float, outy: &mut c_float, outz: &mut c_float) { + let f = self.0.get_accelerometer(); + unsafe { f(outx, outy, outz) } } } -/// Wrapped Buttons API. -/// -/// Represents buttons state. -/// -/// * `current` indicates which buttons are currently down. -/// * `pushed` and `released` reflects which buttons were pushed or released over the previous update cycle. -/// -/// Note: at the nominal frame rate of 50 ms, fast button presses can be missed if you just poll the instantaneous state. -pub struct Buttons { - /// Indicating which buttons are __currently down__. - pub current: PDButtons, - /// Reflect which buttons were pushed over the previous update cycle. - /// See [struct doc][Self]. - pub pushed: PDButtons, - /// Reflect which buttons were released over the previous update cycle. - /// See [struct doc][Self]. - pub released: PDButtons, +/// Buttons +#[derive(Debug, Clone, Copy)] +pub struct Buttons(Api); + +impl Buttons { + /// Creates default [`Buttons`] without type parameter requirement. + /// + /// Uses ZST [`api::Default`]. + #[allow(non_snake_case)] + pub fn Default() -> Self { Self(Default::default()) } +} + +impl Buttons { + /// Creates [`Buttons`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} + +impl Default for Buttons { + fn default() -> Self { Self(Default::default()) } } -impl Buttons { - /// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState]. - pub fn get() -> Option { +impl Buttons { + pub fn new() -> Self { Self(Default::default()) } +} + +impl Buttons { + pub fn new_with(api: Api) -> Self { Self(api) } +} + +impl Buttons { + /// Returns the current buttons [`State`]. + /// + /// Equivalent to [`sys::ffi::playdate_sys::getButtonState`]. + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] + pub fn get(&self) -> State { let mut current = PDButtons(0); let mut pushed = PDButtons(0); let mut released = PDButtons(0); - sysfn!(getButtonState, &mut current, &mut pushed, &mut released); + let f = self.0.get_button_state(); + unsafe { f(&mut current, &mut pushed, &mut released) } - Self { current, - pushed, - released }.into() + State { current, + pushed, + released } } - /// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState]. - pub fn get_to(current: &mut PDButtons, pushed: &mut PDButtons, released: &mut PDButtons) -> Option<()> { - sysfn!(getButtonState, current, pushed, released).into() + /// Writes the current buttons state to given [`State`]. + /// + /// Updates all (current, pushed and released). + /// + /// Equivalent to [`sys::ffi::playdate_sys::getButtonState`]. + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] + pub fn get_to(&self, state: &mut State) { + let f = self.0.get_button_state(); + unsafe { f(&mut state.current, &mut state.pushed, &mut state.released) } } - /// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState]. + /// Writes the current buttons state to given references. + /// + /// Equivalent to [`sys::ffi::playdate_sys::getButtonState`]. + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] + pub fn get_to_raw(&self, current: &mut PDButtons, pushed: &mut PDButtons, released: &mut PDButtons) { + let f = self.0.get_button_state(); + unsafe { f(current, pushed, released) } + } + + /// Equivalent to [`sys::ffi::playdate_sys::getButtonState`]. + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] /// /// Requests & returns only `current` part of state, see [Self::current] - pub fn get_current() -> Option { + pub fn current(&self) -> PDButtons { use core::ptr::null_mut; let mut current = PDButtons(0); - sysfn!(getButtonState, &mut current, null_mut(), null_mut()); - current.into() + let f = self.0.get_button_state(); + unsafe { f(&mut current, null_mut(), null_mut()) } + current } - /// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState]. + /// Equivalent to [`sys::ffi::playdate_sys::getButtonState`]. + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] /// /// Requests & returns only `current` part of state, see [Self::pushed] - pub fn get_pushed() -> Option { + pub fn pushed(&self) -> PDButtons { use core::ptr::null_mut; let mut pushed = PDButtons(0); - sysfn!(getButtonState, null_mut(), &mut pushed, null_mut()); - pushed.into() + + let f = self.0.get_button_state(); + unsafe { f(null_mut(), &mut pushed, null_mut()) } + + pushed } - /// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState]. + /// Equivalent to [`sys::ffi::playdate_sys::getButtonState`]. + #[doc(alias = "sys::ffi::playdate_sys::getButtonState")] /// /// Requests & returns only `current` part of state, see [Self::released] - pub fn get_released() -> Option { + pub fn get_released(&self) -> PDButtons { use core::ptr::null_mut; let mut released = PDButtons(0); - sysfn!(getButtonState, null_mut(), null_mut(), &mut released); - released.into() + + let f = self.0.get_button_state(); + unsafe { f(null_mut(), null_mut(), &mut released) } + + released } } -impl core::fmt::Debug for Buttons { +/// Represents buttons state. +/// +/// * `current` indicates which buttons are currently down. +/// * `pushed` and `released` reflects which buttons were pushed or released over the previous update cycle. +/// +/// Note: at the nominal frame rate of 50 ms, fast button presses can be missed if you just poll the instantaneous state. +pub struct State { + /// Indicating which buttons are __currently down__. + pub current: PDButtons, + /// Reflect which buttons were pushed over the previous update cycle. + /// See [struct doc][Self]. + pub pushed: PDButtons, + /// Reflect which buttons were released over the previous update cycle. + /// See [struct doc][Self]. + pub released: PDButtons, +} + + +impl core::fmt::Debug for State { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use crate::buttons::PDButtonsFmt; @@ -160,19 +315,45 @@ impl core::fmt::Debug for Buttons { } -/// Wrapped Crank API. -pub struct Crank; -impl Crank { - /// Wrapped [`isCrankDocked`][crate::sys::ffi::playdate_sys::isCrankDocked]. - pub fn docked() -> Option { Some(sysfn!(isCrankDocked) == 1) } - /// Wrapped [`getCrankAngle`][crate::sys::ffi::playdate_sys::getCrankAngle]. - pub fn angle() -> Option { Some(sysfn!(getCrankAngle)).into() } - /// Wrapped [`getCrankChange`][crate::sys::ffi::playdate_sys::getCrankChange]. - pub fn change() -> Option { Some(sysfn!(getCrankChange)).into() } +/// Crank +#[derive(Debug, Clone, Copy)] +pub struct Crank(Api); +impl Crank { + /// Returns boolean indicating whether or not the crank is folded into the unit. + /// + /// Equivalent to [`sys::ffi::playdate_sys::isCrankDocked`]. + #[doc(alias = "sys::ffi::playdate_sys::isCrankDocked")] + pub fn docked(&self) -> bool { + let f = self.0.is_crank_docked(); + unsafe { f() == 1 } + } - /// Wrapped [`setCrankSoundsDisabled`][crate::sys::ffi::playdate_sys::setCrankSoundsDisabled]. - /// * Returns the previous value for this setting. - pub fn disable_sounds(disable: bool) -> Option { - Some(sysfn!(setCrankSoundsDisabled, disable as _) == 1) + /// Returns the current position of the crank, in the range 0-360. + /// Zero is pointing up, and the value increases as the crank moves clockwise, as viewed from the right side of the device. + /// + /// Equivalent to [`sys::ffi::playdate_sys::getCrankAngle`]. + #[doc(alias = "sys::ffi::playdate_sys::getCrankAngle")] + pub fn angle(&self) -> c_float { + let f = self.0.get_crank_angle(); + unsafe { f() } + } + + /// Returns the angle change of the crank since the last time this function was called. + /// Negative values are anti-clockwise. + /// + /// Equivalent to [`sys::ffi::playdate_sys::getCrankChange`]. + #[doc(alias = "sys::ffi::playdate_sys::getCrankChange")] + pub fn change(&self) -> c_float { + let f = self.0.get_crank_change(); + unsafe { f() } + } + + /// Returns the previous value for this setting. + /// + /// Equivalent to [`sys::ffi::playdate_sys::setCrankSoundsDisabled`]. + #[doc(alias = "sys::ffi::playdate_sys::setCrankSoundsDisabled")] + pub fn disable_sounds(&self, disable: bool) -> bool { + let f = self.0.set_crank_sounds_disabled(); + unsafe { f(disable as _) == 1 } } } diff --git a/api/display/Cargo.toml b/api/display/Cargo.toml index 27b25e0d..45040151 100644 --- a/api/display/Cargo.toml +++ b/api/display/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-display" -version = "0.3.2" +version = "0.3.3" readme = "README.md" description = "High-level Display API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] diff --git a/api/display/src/lib.rs b/api/display/src/lib.rs index 48247946..25ac2a61 100644 --- a/api/display/src/lib.rs +++ b/api/display/src/lib.rs @@ -224,73 +224,57 @@ pub mod api { impl Api for Cache { - /// Equivalent to [`sys::ffi::playdate_display::getWidth`] - #[doc(alias = "sys::ffi::playdate_display::getWidth")] #[inline(always)] fn get_width(&self) -> unsafe extern "C" fn() -> c_int { self.0.getWidth.expect("getWidth") } - /// Equivalent to [`sys::ffi::playdate_display::getHeight`] - #[doc(alias = "sys::ffi::playdate_display::getHeight")] #[inline(always)] fn get_height(&self) -> unsafe extern "C" fn() -> c_int { self.0.getHeight.expect("getHeight") } - /// Equivalent to [`sys::ffi::playdate_display::setRefreshRate`] - #[doc(alias = "sys::ffi::playdate_display::setRefreshRate")] #[inline(always)] fn set_refresh_rate(&self) -> unsafe extern "C" fn(rate: c_float) { self.0.setRefreshRate.expect("setRefreshRate") } - /// Equivalent to [`sys::ffi::playdate_display::setInverted`] - #[doc(alias = "sys::ffi::playdate_display::setInverted")] #[inline(always)] fn set_inverted(&self) -> unsafe extern "C" fn(flag: c_int) { self.0.setInverted.expect("setInverted") } - /// Equivalent to [`sys::ffi::playdate_display::setScale`] - #[doc(alias = "sys::ffi::playdate_display::setScale")] #[inline(always)] fn set_scale(&self) -> unsafe extern "C" fn(s: c_uint) { self.0.setScale.expect("setScale") } - /// Equivalent to [`sys::ffi::playdate_display::setMosaic`] - #[doc(alias = "sys::ffi::playdate_display::setMosaic")] #[inline(always)] fn set_mosaic(&self) -> unsafe extern "C" fn(x: c_uint, y: c_uint) { self.0.setMosaic.expect("setMosaic") } - /// Equivalent to [`sys::ffi::playdate_display::setFlipped`] - #[doc(alias = "sys::ffi::playdate_display::setFlipped")] #[inline(always)] fn set_flipped(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.setFlipped.expect("setFlipped") } - /// Equivalent to [`sys::ffi::playdate_display::setOffset`] - #[doc(alias = "sys::ffi::playdate_display::setOffset")] #[inline(always)] fn set_offset(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.setOffset.expect("setOffset") } } pub trait Api { - /// Equivalent to [`sys::ffi::playdate_display::getWidth`] + /// Returns [`sys::ffi::playdate_display::getWidth`] #[doc(alias = "sys::ffi::playdate_display::getWidth")] fn get_width(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(display.getWidth) } - /// Equivalent to [`sys::ffi::playdate_display::getHeight`] + /// Returns [`sys::ffi::playdate_display::getHeight`] #[doc(alias = "sys::ffi::playdate_display::getHeight")] fn get_height(&self) -> unsafe extern "C" fn() -> c_int { *sys::api!(display.getHeight) } - /// Equivalent to [`sys::ffi::playdate_display::setRefreshRate`] + /// Returns [`sys::ffi::playdate_display::setRefreshRate`] #[doc(alias = "sys::ffi::playdate_display::setRefreshRate")] fn set_refresh_rate(&self) -> unsafe extern "C" fn(rate: c_float) { *sys::api!(display.setRefreshRate) } - /// Equivalent to [`sys::ffi::playdate_display::setInverted`] + /// Returns [`sys::ffi::playdate_display::setInverted`] #[doc(alias = "sys::ffi::playdate_display::setInverted")] fn set_inverted(&self) -> unsafe extern "C" fn(flag: c_int) { *sys::api!(display.setInverted) } - /// Equivalent to [`sys::ffi::playdate_display::setScale`] + /// Returns [`sys::ffi::playdate_display::setScale`] #[doc(alias = "sys::ffi::playdate_display::setScale")] fn set_scale(&self) -> unsafe extern "C" fn(s: c_uint) { *sys::api!(display.setScale) } - /// Equivalent to [`sys::ffi::playdate_display::setMosaic`] + /// Returns [`sys::ffi::playdate_display::setMosaic`] #[doc(alias = "sys::ffi::playdate_display::setMosaic")] fn set_mosaic(&self) -> unsafe extern "C" fn(x: c_uint, y: c_uint) { *sys::api!(display.setMosaic) } - /// Equivalent to [`sys::ffi::playdate_display::setFlipped`] + /// Returns [`sys::ffi::playdate_display::setFlipped`] #[doc(alias = "sys::ffi::playdate_display::setFlipped")] fn set_flipped(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { *sys::api!(display.setFlipped) } - /// Equivalent to [`sys::ffi::playdate_display::setOffset`] + /// Returns [`sys::ffi::playdate_display::setOffset`] #[doc(alias = "sys::ffi::playdate_display::setOffset")] fn set_offset(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { *sys::api!(display.setOffset) } } diff --git a/api/gfx/Cargo.toml b/api/gfx/Cargo.toml index e5964b00..4e7e345f 100644 --- a/api/gfx/Cargo.toml +++ b/api/gfx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-graphics" -version = "0.3.7" +version = "0.3.8" readme = "README.md" description = "High-level graphics API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] @@ -60,7 +60,7 @@ features = [ "sys/bindings-derive-ord", "sys/bindings-derive-partialeq", "sys/bindings-derive-partialord", - "sdk_2_1" + # "sdk_2_1" ] rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"] default-target = "thumbv7em-none-eabihf" diff --git a/api/playdate/Cargo.toml b/api/playdate/Cargo.toml index 49b7c24a..02523dce 100644 --- a/api/playdate/Cargo.toml +++ b/api/playdate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate" -version = "0.1.9" +version = "0.1.10" readme = "README.md" description = "High-level Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] @@ -127,7 +127,7 @@ features = [ "sys/bindings-derive-ord", "sys/bindings-derive-partialeq", "sys/bindings-derive-partialord", - "sdk_2_1" + # "sdk_2_1" ] rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"] default-target = "thumbv7em-none-eabihf" diff --git a/api/playdate/src/lib.rs b/api/playdate/src/lib.rs index 3a6b3a35..b78e731b 100644 --- a/api/playdate/src/lib.rs +++ b/api/playdate/src/lib.rs @@ -44,6 +44,9 @@ pub mod ext { /// Playdate System API. fn system(&self) -> system::System; + /// Playdate Peripherals API. + fn peripherals(&self) -> controls::peripherals::Peripherals; + /// Playdate File-system API. fn file(&self) -> fs::Fs; @@ -69,6 +72,11 @@ pub mod ext { system::System::new_with(system::api::Cache::from(unsafe { self.as_ref() }.system)) } + fn peripherals(&self) -> controls::peripherals::Peripherals { + let api = system::api::Cache::from(unsafe { self.as_ref() }.system); + controls::peripherals::Peripherals::new_with(api.into()) + } + fn file(&self) -> fs::Fs { fs::Fs::new_with(fs::api::Cache::from(unsafe { self.as_ref() }.file)) } @@ -91,6 +99,11 @@ pub mod ext { system::System::new_with(system::api::Cache::from(unsafe { self.as_ref() }.expect("api").system)) } + fn peripherals(&self) -> controls::peripherals::Peripherals { + let api = system::api::Cache::from(unsafe { self.as_ref() }.expect("api").system); + controls::peripherals::Peripherals::new_with(api.into()) + } + fn file(&self) -> fs::Fs { fs::Fs::new_with(fs::api::Cache::from(unsafe { self.as_ref() }.expect("api").file)) } diff --git a/api/sprite/Cargo.toml b/api/sprite/Cargo.toml index 7c3c5a59..8851b005 100644 --- a/api/sprite/Cargo.toml +++ b/api/sprite/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-sprite" -version = "0.2.2" +version = "0.2.3" readme = "README.md" description = "High-level sprite API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] @@ -69,7 +69,7 @@ features = [ "sys/bindings-derive-ord", "sys/bindings-derive-partialeq", "sys/bindings-derive-partialord", - "sdk_2_1" + # "sdk_2_1" ] rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"] default-target = "thumbv7em-none-eabihf" diff --git a/api/system/Cargo.toml b/api/system/Cargo.toml index 1e63bde0..1b68332f 100644 --- a/api/system/Cargo.toml +++ b/api/system/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-system" -version = "0.3.3" +version = "0.3.4" readme = "README.md" description = "High-level System API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] diff --git a/api/system/examples/handler-boxed.rs b/api/system/examples/handler-boxed.rs index ea273fde..074aa78a 100644 --- a/api/system/examples/handler-boxed.rs +++ b/api/system/examples/handler-boxed.rs @@ -15,10 +15,10 @@ use system::update::UpdateCtrl; /// Entry point, event handler #[no_mangle] -fn event_handler(api: NonNull, event: PDSystemEvent, arg: u32) -> EventLoopCtrl { +fn event_handler(_api: NonNull, _event: PDSystemEvent, _: u32) -> EventLoopCtrl { println!("Init"); - // Do something good with `api` here... + // Do something good with `_api` here... let system = System::Default(); diff --git a/api/system/examples/handler-pinned.rs b/api/system/examples/handler-pinned.rs index e20221ad..75b02157 100644 --- a/api/system/examples/handler-pinned.rs +++ b/api/system/examples/handler-pinned.rs @@ -17,10 +17,10 @@ use system::update::UpdateCtrl; /// Entry point #[no_mangle] -fn event_handler(api: NonNull, event: PDSystemEvent, arg: u32) -> EventLoopCtrl { +fn event_handler(_api: NonNull, _event: PDSystemEvent, _: u32) -> EventLoopCtrl { println!("Init"); - // Do something good with `api` here... + // Do something good with `_api` here... // Registering update-callback with user-data. diff --git a/api/system/examples/handler-static.rs b/api/system/examples/handler-static.rs index 306fcf27..513082e7 100644 --- a/api/system/examples/handler-static.rs +++ b/api/system/examples/handler-static.rs @@ -15,10 +15,10 @@ use system::update::UpdateCtrl; /// Entry point, event handler #[no_mangle] -fn event_handler(api: NonNull, event: PDSystemEvent, arg: u32) -> EventLoopCtrl { +fn event_handler(_api: NonNull, _event: PDSystemEvent, _: u32) -> EventLoopCtrl { println!("Init"); - // Do something good with `api` here... + // Do something good with `_api` here... // Registering update-callback with user-data. diff --git a/api/system/src/lib.rs b/api/system/src/lib.rs index 16f7c406..26d4cebd 100644 --- a/api/system/src/lib.rs +++ b/api/system/src/lib.rs @@ -258,6 +258,11 @@ pub mod api { fn from(ptr: &NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } } + impl Cache { + #[inline(always)] + pub fn as_inner(&self) -> &'static playdate_sys { self.0 } + } + impl Api for Cache { /// Equivalent to [`sys::ffi::playdate_sys::getLanguage`]