1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use core::ffi::c_float;
use sys::ffi::PDButtons;
use sys::ffi::PDPeripherals;
use sys::api;


macro_rules! sysfn {
	($name:ident) => {{ unsafe { (*($crate::sys::api()?.system)).$name?() } }};
	($name:ident, $($arg:tt)*) => {{ unsafe { (*($crate::sys::api()?.system)).$name?($($arg)*) } }};
}


/// Wrapped Peripherals API.
///
/// Currently there's only one peripheral - accelerometer.
pub struct Peripherals;
impl Peripherals {
	#![allow(non_snake_case)]

	pub fn enable(value: PDPeripherals) -> Option<()> {
		unsafe { (*(api()?.system)).setPeripheralsEnabled?(value) }.into()
	}
	pub fn enable_accelerometer() -> Option<()> {
		unsafe { (*(api()?.system)).setPeripheralsEnabled?(Self::Accelerometer()) }.into()
	}

	pub fn enable_all() -> Option<()> { unsafe { (*(api()?.system)).setPeripheralsEnabled?(Self::All()) }.into() }
	pub fn disable_all() -> Option<()> {
		unsafe { (*(api()?.system)).setPeripheralsEnabled?(Self::None()) }.into()
	}

	pub const fn None() -> PDPeripherals { PDPeripherals::kNone }
	pub const fn Accelerometer() -> PDPeripherals { PDPeripherals::kAccelerometer }
	pub const fn All() -> PDPeripherals { PDPeripherals::kAllPeripherals }
}


/// Wrapped Accelerometer API.
pub struct Accelerometer;
impl Accelerometer {
	/// Enable 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()
	}

	/// Uses [`setPeripheralsEnabled`][crate::sys::ffi::playdate_sys::setPeripheralsEnabled].
	///
	/// ⚠️ 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()
	}

	/// Uses [`getAccelerometer`][crate::sys::ffi::playdate_sys::getAccelerometer].
	///
	/// Returns `(x, y, z)` accelerometer data.
	pub fn get() -> Option<(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))
	}

	/// 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()
	}
}


/// 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,
}

impl Buttons {
	/// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState].
	pub fn get() -> Option<Self> {
		let mut current = PDButtons(0);
		let mut pushed = PDButtons(0);
		let mut released = PDButtons(0);

		sysfn!(getButtonState, &mut current, &mut pushed, &mut released);

		Self { current,
		       pushed,
		       released }.into()
	}

	/// 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()
	}

	/// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState].
	///
	/// Requests & returns only `current` part of state, see [Self::current]
	pub fn get_current() -> Option<PDButtons> {
		use core::ptr::null_mut;

		let mut current = PDButtons(0);
		sysfn!(getButtonState, &mut current, null_mut(), null_mut());
		current.into()
	}

	/// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState].
	///
	/// Requests & returns only `current` part of state, see [Self::pushed]
	pub fn get_pushed() -> Option<PDButtons> {
		use core::ptr::null_mut;

		let mut pushed = PDButtons(0);
		sysfn!(getButtonState, null_mut(), &mut pushed, null_mut());
		pushed.into()
	}

	/// Uses [`getButtonState`][crate::sys::ffi::playdate_sys::getButtonState].
	///
	/// Requests & returns only `current` part of state, see [Self::released]
	pub fn get_released() -> Option<PDButtons> {
		use core::ptr::null_mut;

		let mut released = PDButtons(0);
		sysfn!(getButtonState, null_mut(), null_mut(), &mut released);
		released.into()
	}
}

impl core::fmt::Debug for Buttons {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		use crate::buttons::PDButtonsFmt;

		f.debug_struct("Buttons")
		 .field("current", &self.current.display())
		 .field("pushed", &self.pushed.display())
		 .field("released", &self.released.display())
		 .finish()
	}
}


/// Wrapped Crank API.
pub struct Crank;
impl Crank {
	/// Wrapped [`isCrankDocked`][crate::sys::ffi::playdate_sys::isCrankDocked].
	pub fn docked() -> Option<bool> { Some(sysfn!(isCrankDocked) == 1) }
	/// Wrapped [`getCrankAngle`][crate::sys::ffi::playdate_sys::getCrankAngle].
	pub fn angle() -> Option<c_float> { Some(sysfn!(getCrankAngle)).into() }
	/// Wrapped [`getCrankChange`][crate::sys::ffi::playdate_sys::getCrankChange].
	pub fn change() -> Option<c_float> { Some(sysfn!(getCrankChange)).into() }

	/// Wrapped [`setCrankSoundsDisabled`][crate::sys::ffi::playdate_sys::setCrankSoundsDisabled].
	/// * Returns the previous value for this setting.
	pub fn disable_sounds(disable: bool) -> Option<bool> {
		Some(sysfn!(setCrankSoundsDisabled, disable as _) == 1)
	}
}