pebble_skip/user_interface/window/
number_window.rs

1use crate::{
2	standard_c::{CStr, NotStack},
3	Box, Handle, SpecialDrop,
4};
5use core::{
6	marker::PhantomData,
7	mem::ManuallyDrop,
8	ops::{Deref, DerefMut},
9};
10#[allow(clippy::wildcard_imports)]
11use pebble_sys::{
12	prelude::*,
13	standard_c::memory::void,
14	user_interface::window::number_window::{NumberWindow as sysNumberWindow, *},
15};
16
17use super::{WindowRef, WindowRefMut};
18
19pub struct NumberWindow<'a, T: ?Sized>(
20	pub(crate) Handle<'a, sysNumberWindow<'a>>,
21	PhantomData<T>,
22	*mut NumberWindowDataWrapper<'a>,
23);
24
25pub struct NumberWindowData<
26	I: FnMut(&NumberWindow<void>, &mut T),
27	D: FnMut(&NumberWindow<void>, &mut T),
28	S: FnMut(&NumberWindow<void>, &mut T),
29	T,
30> {
31	pub incremented: I,
32	pub decremented: D,
33	pub selected: S,
34	pub context: T,
35}
36
37trait NumberWindowDataTrait {
38	fn incremented(&mut self, number_window: &NumberWindow<void>);
39	fn decremented(&mut self, number_window: &NumberWindow<void>);
40	fn selected(&mut self, number_window: &NumberWindow<void>);
41}
42
43impl<
44		I: FnMut(&NumberWindow<void>, &mut T),
45		D: FnMut(&NumberWindow<void>, &mut T),
46		S: FnMut(&NumberWindow<void>, &mut T),
47		T,
48	> NumberWindowDataTrait for NumberWindowData<I, D, S, T>
49{
50	fn incremented(&mut self, number_window: &NumberWindow<void>) {
51		(self.incremented)(number_window, &mut self.context)
52	}
53
54	fn decremented(&mut self, number_window: &NumberWindow<void>) {
55		(self.decremented)(number_window, &mut self.context)
56	}
57
58	fn selected(&mut self, number_window: &NumberWindow<void>) {
59		(self.selected)(number_window, &mut self.context)
60	}
61}
62
63pub struct NumberWindowDataWrapper<'a>(Box<'a, dyn 'a + NumberWindowDataTrait>);
64
65impl<'a, T> NumberWindow<'a, T> {
66	// TODO: This probably should take and set a set of window handlers, which can then also act as lifecycle hooks for the context.
67	/// # Errors
68	///
69	/// TODO
70	///
71	pub fn new<
72		I: 'a + FnMut(&NumberWindow<void>, &mut T),
73		D: 'a + FnMut(&NumberWindow<void>, &mut T),
74		S: 'a + FnMut(&NumberWindow<void>, &mut T),
75	>(
76		label: &'a CStr<impl NotStack>,
77		number_window_data: NumberWindowData<I, D, S, T>,
78	) -> Result<Self, NumberWindowData<I, D, S, T>>
79	where
80		T: 'a,
81	{
82		#![allow(clippy::items_after_statements)]
83
84		let window_data_wrapper = Box::leak(
85			Box::new(NumberWindowDataWrapper(Box::new(number_window_data)?)).map_err(
86				|wrapper| Box::into_inner(unsafe { Box::downcast_unchecked(wrapper.0) }),
87			)?,
88		) as *mut NumberWindowDataWrapper;
89
90		extern "C" fn raw_incremented<'a>(
91			raw_window: &'a mut sysNumberWindow<'a>,
92			context: &mut void,
93		) {
94			let context = context as *mut void; // This will be aliased.
95			let fake_window = unsafe {
96				//SAFETY: It's actually *kind of* safe to alias NumberWindow instances... But only because they store a Handle internally, which stores a pointer.
97				// Actually accessing associated data would NOT be safe, so the user-provided handlers only see a NumberWindow<void> where such access is impossible.
98				#[allow(clippy::cast_ptr_alignment)]
99				NumberWindow::<void>::from_raw_unsized(
100					raw_window,
101					context as *mut _ as *mut NumberWindowDataWrapper,
102				)
103			};
104			unsafe {
105				//SAFETY: And here's the third concurrent use of this pointer.
106				// The reference goes out of scope before the others are used, so this is safe.
107				let context = &mut *context;
108				context
109					.cast_unchecked_mut::<NumberWindowDataWrapper>()
110					.0
111					.incremented(&fake_window)
112			}
113			fake_window.abandon();
114		}
115		extern "C" fn raw_decremented<'a>(
116			raw_window: &'a mut sysNumberWindow<'a>,
117			context: &mut void,
118		) {
119			let context = context as *mut void; // This will be aliased.
120			let fake_window = unsafe {
121				//SAFETY: It's actually *kind of* safe to alias NumberWindow instances... But only because they store a Handle internally, which stores a pointer.
122				// Actually accessing associated data would NOT be safe, so the user-provided handlers only see a NumberWindow<void> where such access is impossible.
123				#[allow(clippy::cast_ptr_alignment)]
124				NumberWindow::<void>::from_raw_unsized(
125					raw_window,
126					context as *mut _ as *mut NumberWindowDataWrapper,
127				)
128			};
129			unsafe {
130				//SAFETY: And here's the third concurrent use of this pointer.
131				// The reference goes out of scope before the others are used, so this is safe.
132				let context = &mut *context;
133				context
134					.cast_unchecked_mut::<NumberWindowDataWrapper>()
135					.0
136					.decremented(&fake_window)
137			}
138			fake_window.abandon();
139		}
140		extern "C" fn raw_selected<'a>(
141			raw_window: &'a mut sysNumberWindow<'a>,
142			context: &mut void,
143		) {
144			let context = context as *mut void; // This will be aliased.
145			let fake_window = unsafe {
146				//SAFETY: It's actually *kind of* safe to alias NumberWindow instances... But only because they store a Handle internally, which stores a pointer.
147				// Actually accessing associated data would NOT be safe, so the user-provided handlers only see a NumberWindow<void> where such access is impossible.
148				#[allow(clippy::cast_ptr_alignment)]
149				NumberWindow::<void>::from_raw_unsized(
150					raw_window,
151					context as *mut _ as *mut NumberWindowDataWrapper,
152				)
153			};
154			unsafe {
155				//SAFETY: And here's the third concurrent use of this pointer.
156				// The reference goes out of scope before the others are used, so this is safe.
157				let context = &mut *context;
158				context
159					.cast_unchecked_mut::<NumberWindowDataWrapper>()
160					.0
161					.selected(&fake_window)
162			}
163			fake_window.abandon();
164		}
165
166		match unsafe {
167			number_window_create(
168				label.as_c_str(),
169				NumberWindowCallbacks {
170					incremented: Some(raw_incremented),
171					decremented: Some(raw_decremented),
172					selected: Some(raw_selected),
173				},
174				&mut *(window_data_wrapper as *mut _ as *mut void),
175			)
176		} {
177			Some(raw_window) => Ok(Self(
178				Handle::new(raw_window),
179				PhantomData,
180				window_data_wrapper,
181			)),
182			None => Err(Box::into_inner(unsafe {
183				Box::downcast_unchecked(
184					Box::into_inner(Box::<NumberWindowDataWrapper>::from_raw(
185						&mut *window_data_wrapper,
186					))
187					.0,
188				)
189			})),
190		}
191	}
192
193	/// Assembles a new instance of [`NumberWindow`] from the given raw window handle.
194	///
195	/// # Safety
196	///
197	/// This function is only safe if `raw_window` is a raw window handle that was previously [`.leak()`]ed from the same [`NumberWindow<T>`] variant and no other [`NumberWindow<T>`] instance has been created from it since.
198	///
199	/// [`.leak()`]: #method.leak
200	pub unsafe fn from_raw(
201		raw_window: &'a mut sysNumberWindow<'a>,
202		number_window_data_wrapper: *mut NumberWindowDataWrapper<'a>,
203	) -> Self {
204		Self(
205			Handle::new(raw_window),
206			PhantomData,
207			number_window_data_wrapper,
208		)
209	}
210
211	/// Leaks the current [`NumberWindow`] instance into a raw Pebble number window handle.
212	///
213	/// Note that [`NumberWindow`] has associated heap instances beyond the raw window, so only destroying that would still leak memory.
214	#[must_use = "Not reassembling the `NumberWindow` later causes a memory leak."]
215	pub fn leak(
216		self,
217	) -> (
218		&'a mut sysNumberWindow<'a>,
219		*mut NumberWindowDataWrapper<'a>,
220	)
221	where
222		T: 'a,
223	{
224		let undropped = ManuallyDrop::new(self);
225		unsafe { (undropped.0.duplicate().unwrap(), undropped.2) }
226	}
227}
228
229impl<'a, T: ?Sized> NumberWindow<'a, T> {
230	#[must_use]
231	pub fn window(&self) -> WindowRef<'_> {
232		WindowRef(Handle::new(unsafe {
233			number_window_get_window_mut(&mut *(self.0.as_mut_unchecked() as *mut _))
234		}))
235	}
236
237	#[must_use]
238	pub fn window_mut<'b: 'a>(&'b mut self) -> WindowRefMut<'b> {
239		WindowRefMut(Handle::new(unsafe {
240			number_window_get_window_mut(self.0.as_mut_unchecked())
241		}))
242	}
243
244	pub fn set_label(&self, label: &'a CStr<impl NotStack>) {
245		unsafe { number_window_set_label(self.0.as_mut_unchecked(), label.as_c_str()) }
246	}
247
248	pub fn set_max(&self, max: i32) {
249		unsafe { number_window_set_max(self.0.as_mut_unchecked(), max) }
250	}
251
252	pub fn set_min(&self, min: i32) {
253		unsafe { number_window_set_min(self.0.as_mut_unchecked(), min) }
254	}
255
256	pub fn set_value(&self, value: i32) {
257		unsafe { number_window_set_value(self.0.as_mut_unchecked(), value) }
258	}
259
260	pub fn set_step_size(&self, step_size: i32) {
261		unsafe { number_window_set_step_size(self.0.as_mut_unchecked(), step_size) }
262	}
263
264	#[must_use]
265	pub fn get_value(&self) -> i32 {
266		unsafe { number_window_get_value(&*self.0) }
267	}
268
269	/// # Safety
270	///
271	/// It's actually safe to assemble [`NumberWindow`] instances with mismatched type parameters iff the type parameter assembled against is unsized,
272	/// because this data can never be directly accessed outside the destructor.
273	///
274	/// However, dropping such a value will always panic, so adding this to the public API would be a *really* bad idea.
275	unsafe fn from_raw_unsized(
276		raw_window: &'a mut sysNumberWindow<'a>,
277		number_window_data_wrapper: *mut NumberWindowDataWrapper<'a>,
278	) -> Self {
279		Self(
280			Handle::new(raw_window),
281			PhantomData,
282			number_window_data_wrapper,
283		)
284	}
285
286	/// Discards this instance while skipping the destructor. Helper for aliased temporaries.
287	fn abandon(self) {
288		let _ = ManuallyDrop::new(self);
289	}
290}
291
292impl<'a, T> Deref for NumberWindow<'a, T> {
293	type Target = NumberWindow<'a, void>;
294
295	fn deref(&self) -> &Self::Target {
296		unsafe {
297			//SAFETY: Same memory layout, no access to data.
298			&*(self as *const _ as *const Self::Target)
299		}
300	}
301}
302
303impl<'a, T> DerefMut for NumberWindow<'a, T> {
304	fn deref_mut(&mut self) -> &mut Self::Target {
305		unsafe {
306			//SAFETY: Same memory layout, no access to data.
307			&mut *(self as *mut _ as *mut Self::Target)
308		}
309	}
310}
311
312impl<'a, T: ?Sized> Drop for NumberWindow<'a, T> {
313	fn drop(&mut self) {
314		self.special_drop()
315	}
316}
317
318impl<'a, T: ?Sized> SpecialDrop for NumberWindow<'a, T> {
319	default fn special_drop(&mut self) {
320		panic!("Dropping unsized `NumberWindow<T>`s is illegal")
321	}
322}
323
324impl<'a, T: Sized> SpecialDrop for NumberWindow<'a, T> {
325	fn special_drop(&mut self) {
326		unsafe {
327			//SAFETY: window_data is created and leaked in the only accessible constructor.
328			//SAFETY: self.0 isn't accessed after this.
329			let data_wrapper = self.2;
330			// Detaching the lifetime here takes a bit of work.
331			let sys_number_window = self.0.duplicate().unwrap() as *mut _ as *mut void as *mut _;
332
333			// Destroy the window, THEN drop its data.
334			number_window_destroy(&mut *sys_number_window);
335			Box::<NumberWindowDataWrapper>::from_raw(&mut *data_wrapper);
336		}
337	}
338}