pebble_sys/
lib.rs

1//! Documentation for this crate is work in progress.
2//!
3//! For now, please see the C API documentation at <https://developer.rebble.io/developer.pebble.com/docs/c/index.html> for more information.
4
5#![no_std]
6#![doc(html_root_url = "https://docs.rs/pebble-sys/0.0.1")]
7#![feature(extern_types)]
8#![warn(clippy::pedantic)]
9#![allow(clippy::match_bool)]
10// Matching the SDK documentation.
11#![allow(clippy::module_name_repetitions)]
12
13use core::panic::PanicInfo;
14use foundation::logging::app_log;
15use standard_c::memory::c_str;
16
17pub mod prelude {
18	pub use super::standard_c::prelude::*;
19}
20
21#[panic_handler]
22fn panic(_info: &PanicInfo) -> ! {
23	unsafe {
24		let panic = &*("### PANIC ###\0" as *const str as *const _ as *const c_str);
25		let todo = &*("TODO: Output trace somehow.\0" as *const str as *const _ as *const c_str);
26		app_log(1, panic, -1, todo);
27	}
28	loop {}
29}
30
31extern "C" {
32	/// Marker type for unsized newtypes.
33	type ExternData;
34}
35
36pub mod foundation {
37	pub mod app {
38		extern "C" {
39			pub fn app_event_loop();
40		}
41	}
42
43	pub mod logging {
44		use crate::standard_c::memory::{c_str, int};
45
46		//TODO: Enum AppLogLevel
47
48		extern "C" {
49			pub fn app_log(
50				log_level: u8,
51				src_filename: &c_str,
52				src_line_number: int,
53				fmt: &c_str,
54				...
55			);
56		}
57	}
58
59	pub mod resources {
60		use crate::standard_c::memory::void;
61
62		#[derive(Copy, Clone)]
63		#[repr(transparent)]
64		pub struct ResHandle(*const void);
65
66		extern "C" {
67			pub fn resource_get_handle(resource_id: u32) -> ResHandle;
68			pub fn resource_size(h: ResHandle) -> usize;
69			pub fn resource_load(h: ResHandle, buffer: *mut u8, max_length: usize) -> usize;
70			pub fn resource_load_byte_range(
71				h: ResHandle,
72				start_offset: u32,
73				buffer: *mut u8,
74				num_bytes: usize,
75			) -> usize;
76		}
77	}
78}
79
80pub mod graphics {
81	pub mod graphics_types {
82		#[repr(C)]
83		pub struct GPoint {
84			pub x: i16,
85			pub y: i16,
86		}
87
88		#[repr(C)]
89		pub struct GRect {
90			pub origin: GPoint,
91			pub size: GSize,
92		}
93
94		#[repr(C)]
95		pub struct GSize {
96			pub w: i16,
97			pub h: i16,
98		}
99
100		#[repr(C)]
101		pub union GColor8 {
102			pub argb: u8,
103		}
104
105		pub type GColor = GColor8;
106
107		extern "C" {
108			pub type GBitmap;
109			pub type GBitmapSequence;
110			pub type GContext;
111		}
112
113		pub mod color_definitions {
114			use super::GColor8;
115
116			macro_rules! colors {
117				($name:ident = $value:literal $(, $further_name:ident = $further_value:literal)*$(,)?) => {
118					pub const $name: GColor8 = GColor8 {
119						argb: $value,
120					};
121					$(colors!($further_name = $further_value);)*
122				};
123			}
124
125			colors! {
126				BLUE_MOON = 0b_11_00_01_11,
127				MELON = 0b_11_11_10_10,
128				YELLOW = 0b_11_11_11_00,
129			}
130		}
131	}
132}
133
134pub mod user_interface {
135	pub mod clicks {
136		use crate::standard_c::memory::void;
137
138		#[repr(C)] //TODO
139		pub enum ButtonId {
140			_A, //TODO
141		}
142
143		#[repr(transparent)]
144		pub struct ClickRecognizerRef(*mut void);
145		pub type ClickHandler = extern "C" fn(recognizer: ClickRecognizerRef, context: *mut void);
146		pub type ClickConfigProvider = extern "C" fn(context: *mut void);
147	}
148
149	pub mod layers {
150		use super::window::Window;
151		use crate::{
152			graphics::graphics_types::{GContext, GPoint, GRect},
153			standard_c::memory::void,
154		};
155		use core::ptr::NonNull;
156
157		pub type LayerUpdateProc = extern "C" fn(layer: NonNull<Layer>, NonNull<GContext>);
158
159		extern "C" {
160			pub type Layer;
161
162			pub fn layer_create(frame: GRect) -> *mut Layer;
163			pub fn layer_create_with_data(frame: GRect, data_size: usize) -> *mut Layer;
164			pub fn layer_destroy(layer: &'static mut Layer);
165			pub fn layer_mark_dirty(layer: NonNull<Layer>);
166			pub fn layer_set_update_proc(
167				layer: NonNull<Layer>,
168				update_proc: Option<LayerUpdateProc>, //TODO: Check if this is legal!
169			);
170			pub fn layer_set_frame(layer: NonNull<Layer>, frame: GRect);
171			pub fn layer_get_frame(layer: NonNull<Layer>) -> GRect;
172			pub fn layer_set_bounds(layer: NonNull<Layer>, bounds: GRect);
173			pub fn layer_get_bounds(layer: NonNull<Layer>) -> GRect;
174			pub fn layer_convert_point_to_screen(layer: NonNull<Layer>, point: GPoint) -> GPoint;
175			pub fn layer_convert_rect_to_screen(layer: NonNull<Layer>, rect: GRect) -> GRect;
176			pub fn layer_get_window(layer: NonNull<Layer>) -> *mut Window;
177			pub fn layer_remove_from_parent(child: NonNull<Layer>);
178			pub fn layer_remove_child_layers(parent: NonNull<Layer>);
179			pub fn layer_add_child(parent: NonNull<Layer>, child: NonNull<Layer>);
180			pub fn layer_insert_below_sibling(
181				layer_to_insert: NonNull<Layer>,
182				below_sibling_layer: NonNull<Layer>,
183			);
184			pub fn layer_insert_above_sibling(
185				layer_to_insert: NonNull<Layer>,
186				above_sibling_layer: NonNull<Layer>,
187			);
188			pub fn layer_set_hidden(layer: NonNull<Layer>, hidden: bool);
189			pub fn layer_get_hidden(layer: NonNull<Layer>) -> bool;
190			pub fn layer_set_clips(layer: NonNull<Layer>, clips: bool);
191			pub fn layer_get_clips(layer: NonNull<Layer>) -> bool;
192			pub fn layer_get_data(layer: NonNull<Layer>) -> NonNull<void>;
193
194		//TODO: #define GRect layer_get_unobstructed_bounds(const Layer* layer);
195		}
196	}
197
198	pub mod vibes {
199		use core::marker::PhantomData;
200
201		#[repr(C)]
202		pub struct VibePattern<'a> {
203			/// Pointer to an array of segment durations in on(off on)*off? order, up to 10_000ms each.
204			/// There must be at least one duration!
205			pub durations: *const u32,
206			/// Length of the array.
207			pub num_segments: u32,
208			pub phantom: PhantomData<&'a u32>,
209		}
210
211		extern "C" {
212			pub fn vibes_cancel();
213			pub fn vibes_short_pulse();
214			pub fn vibes_long_pulse();
215			pub fn vibes_double_pulse();
216			pub fn vibes_enqueue_custom_pattern(pattern: VibePattern);
217		}
218	}
219
220	pub mod window {
221		use super::{
222			clicks::{ButtonId, ClickConfigProvider, ClickHandler},
223			layers::Layer,
224		};
225		use crate::{graphics::graphics_types::GColor8, standard_c::memory::void};
226		use core::ptr::NonNull;
227
228		#[repr(C)]
229		pub struct WindowHandlers {
230			pub load: Option<WindowHandler>,
231			pub appear: Option<WindowHandler>,
232			pub disappear: Option<WindowHandler>,
233			pub unload: Option<WindowHandler>,
234		}
235
236		pub type WindowHandler = extern "C" fn(window: &mut Window);
237
238		extern "C" {
239			pub type Window;
240
241			pub fn window_create() -> Option<&'static mut Window>;
242			pub fn window_destroy(window: &'static mut Window);
243			pub fn window_set_click_config_provider(
244				window: &mut Window,
245				click_config_provider: Option<ClickConfigProvider>,
246			);
247			pub fn window_set_click_config_provider_with_context(
248				window: &mut Window,
249				click_config_provider: Option<ClickConfigProvider>,
250				context: *mut void,
251			);
252			pub fn window_get_click_config_provider(window: &Window)
253				-> Option<ClickConfigProvider>;
254			pub fn window_get_click_config_context(window: &Window) -> *mut void;
255			pub fn window_set_window_handlers(window: &mut Window, handlers: WindowHandlers);
256
257			// The watch is single-threaded and everything's on the heap, so this *should* be fine.
258			pub fn window_get_root_layer(window: &Window) -> &mut Layer;
259
260			pub fn window_set_background_color(window: &mut Window, background_color: GColor8);
261			pub fn window_is_loaded(window: &mut Window) -> bool;
262			pub fn window_set_user_data(window: &mut Window, data: *mut void);
263			pub fn window_get_user_data(window: &Window) -> *mut void;
264			pub fn window_single_click_subscribe(button_id: ButtonId, handler: ClickHandler);
265			pub fn window_single_repeating_click_subscribe(
266				button_id: ButtonId,
267				repeat_interval_ms: u16,
268				handler: ClickHandler,
269			);
270			pub fn window_multi_click_subscribe(
271				button_id: ButtonId,
272				min_clicks: u8,
273				max_clicks: u8,
274				timeout: u16,
275				last_click_only: bool,
276				handler: ClickHandler,
277			);
278			pub fn window_long_click_subscribe(
279				button_id: ButtonId,
280				delay_ms: u16,
281				down_handler: ClickHandler,
282				up_handler: ClickHandler,
283			);
284			pub fn window_raw_click_subscribe(
285				button_id: ButtonId,
286				down_handler: ClickHandler,
287				up_handler: ClickHandler,
288				context: Option<NonNull<void>>,
289			);
290			pub fn window_set_click_context(button_id: ButtonId, context: *mut void);
291		}
292
293		pub mod number_window {
294			//! A ready-made window prompting the user to pick a number.
295			//!
296			//! TODO: Images
297
298			use super::Window;
299			use crate::{
300				standard_c::memory::{c_str, void},
301				ExternData,
302			};
303			use core::{
304				marker::PhantomData,
305				ops::{Deref, DerefMut},
306			};
307
308			/// [`NumberWindow`] callbacks.
309			#[repr(C)]
310			pub struct NumberWindowCallbacks {
311				/// Called as the value is incremented.
312				pub incremented: Option<NumberWindowCallback>,
313				/// Called as the value is decremented.
314				pub decremented: Option<NumberWindowCallback>,
315				/// Called as the value is confirmed, i.e. as the SELECT button is clicked.
316				pub selected: Option<NumberWindowCallback>,
317			}
318
319			/// A [`NumberWindow`] callback.
320			pub type NumberWindowCallback =
321				for<'a> extern "C" fn(number_window: &'a mut NumberWindow<'a>, context: &mut void);
322
323			/// Limited-lifetime foreign type. See [module](./index.html) documentation.  
324			/// [`Deref`] and [`DerefMut`] towards [`Window`] (but destroying it as such might leak memory).
325			///
326			/// [`Deref`]: https://doc.rust-lang.org/stable/core/ops/trait.Deref.html
327			/// [`DerefMut`]: https://doc.rust-lang.org/stable/core/ops/trait.DerefMut.html
328			/// [`Window`]: ../foreigntype.Window.html
329			#[repr(transparent)]
330			pub struct NumberWindow<'a>(PhantomData<&'a ()>, ExternData);
331
332			extern "C" {
333				pub fn number_window_create<'a>(
334					label: &'a c_str,
335					callbacks: NumberWindowCallbacks,
336					callback_context: &'static mut void,
337				) -> Option<&'a mut NumberWindow<'a>>;
338
339				pub fn number_window_destroy(number_window: &'static mut NumberWindow);
340				pub fn number_window_set_label<'a>(
341					number_window: &mut NumberWindow<'a>,
342					label: &'a c_str,
343				);
344				pub fn number_window_set_max(number_window: &mut NumberWindow, max: i32);
345				pub fn number_window_set_min(number_window: &mut NumberWindow, min: i32);
346				pub fn number_window_set_value(number_window: &mut NumberWindow, value: i32);
347				pub fn number_window_set_step_size(number_window: &mut NumberWindow, step: i32);
348				pub fn number_window_get_value(number_window: &NumberWindow) -> i32;
349				pub fn number_window_get_window<'a>(
350					number_window: &'a NumberWindow<'a>,
351				) -> &'a Window;
352				#[allow(clashing_extern_declarations)]
353				#[link_name = "number_window_get_window"]
354				pub fn number_window_get_window_mut<'a>(
355					number_window: &'a mut NumberWindow<'a>,
356				) -> &'a mut Window;
357			}
358
359			impl<'a> Deref for NumberWindow<'a> {
360				type Target = Window;
361
362				fn deref(&self) -> &Self::Target {
363					unsafe { &*(self as *const _ as *const Self::Target) }
364				}
365			}
366
367			impl<'a> DerefMut for NumberWindow<'a> {
368				fn deref_mut(&mut self) -> &mut Self::Target {
369					unsafe { &mut *(self as *mut _ as *mut Self::Target) }
370				}
371			}
372		}
373	}
374
375	pub mod window_stack {
376		use super::window::Window;
377		use core::ptr::NonNull;
378
379		extern "C" {
380			pub fn window_stack_push(window: &'static mut Window, animated: bool);
381			pub fn window_stack_pop(animated: bool) -> Option<NonNull<Window>>;
382			pub fn window_stack_pop_all(animated: bool);
383			pub fn window_stack_remove(window: &mut Window, animated: bool) -> bool;
384			pub fn window_stack_get_top_window() -> Option<NonNull<Window>>;
385			pub fn window_stack_contains_window(window: &mut Window) -> bool;
386		}
387	}
388}
389
390pub mod standard_c {
391	pub mod prelude {
392		pub use super::memory::prelude::*;
393	}
394
395	pub mod memory {
396		#![allow(non_camel_case_types)]
397
398		use core::convert::TryFrom;
399
400		pub mod prelude {
401			pub use super::{
402				CastUncheckedExt, CastUncheckedMutExt, OptionCastUncheckedMutExt, UpcastExt,
403				UpcastMutExt,
404			};
405		}
406
407		pub type int = i32;
408
409		extern "C" {
410			pub type c_str;
411
412			/// `void` can be safely passed back across the FFI as `&void` while [`core::ffi::c_void`] cannot.
413			/// ([`c_void`] is NOT [unsized]!)
414			///
415			/// [`core::ffi::c_void`]: https://doc.rust-lang.org/stable/core/ffi/enum.c_void.html
416			/// [`c_void`]: https://doc.rust-lang.org/stable/core/ffi/enum.c_void.html
417			/// [unsized]: https://doc.rust-lang.org/stable/core/marker/trait.Sized.html
418			pub type void;
419
420			pub fn malloc(size: usize) -> Option<&'static mut void>;
421			pub fn calloc(count: usize, size: usize) -> Option<&'static mut void>;
422			pub fn realloc(ptr: *mut void, size: usize) -> Option<&'static mut void>;
423			pub fn free(ptr: &'static mut void);
424			pub fn memcmp(ptr1: &void, ptr2: &void, n: usize) -> int;
425			pub fn memcpy(dest: &mut void, src: &void, n: usize) -> *mut void;
426			pub fn memmove(dest: *mut void, src: *const void, n: usize) -> *mut void;
427			pub fn memset(dest: &mut void, c: int, n: usize) -> *mut void;
428		}
429
430		impl<'a, T> From<&'a mut T> for &'a mut void {
431			fn from(src: &'a mut T) -> Self {
432				unsafe { &mut *(src as *mut _ as *mut void) }
433			}
434		}
435
436		impl<'a, T> From<&'a T> for &'a void {
437			fn from(src: &'a T) -> Self {
438				unsafe { &*(src as *const _ as *const void) }
439			}
440		}
441
442		pub trait CastUncheckedExt<'a> {
443			/// Casts a mutable untyped heap reference ([`&void]) into a typed one.
444			///
445			/// # Safety
446			///
447			/// Horribly unsafe if T doesn't point to an **initialised** instance of T.
448			unsafe fn cast_unchecked<T>(self) -> &'a T;
449		}
450
451		pub trait CastUncheckedMutExt<'a> {
452			/// Casts a mutable untyped heap reference ([`&mut void]) into a typed one.
453			///
454			/// # Safety
455			///
456			/// Horribly unsafe if T doesn't point to an **initialised** instance of T.
457			unsafe fn cast_unchecked_mut<T>(self) -> &'a mut T;
458		}
459
460		pub trait OptionCastUncheckedMutExt<'a> {
461			/// Casts a mutable untyped heap reference ([`&mut void]) into a typed one.
462			///
463			/// # Safety
464			///
465			/// Horribly unsafe if T doesn't point to an **initialised** instance of T.
466			unsafe fn cast_unchecked_mut<T>(self) -> Option<&'a mut T>;
467		}
468
469		pub trait UpcastExt<'a> {
470			type Output;
471
472			fn upcast(self) -> Self::Output;
473		}
474
475		pub trait UpcastMutExt<'a> {
476			type Output;
477
478			fn upcast_mut(self) -> Self::Output;
479		}
480
481		impl<'a> CastUncheckedExt<'a> for &'a void {
482			unsafe fn cast_unchecked<T>(self) -> &'a T {
483				&*(self as *const _ as *const T)
484			}
485		}
486
487		impl<'a> CastUncheckedMutExt<'a> for &'a mut void {
488			unsafe fn cast_unchecked_mut<T>(self) -> &'a mut T {
489				&mut *(self as *mut _ as *mut T)
490			}
491		}
492
493		impl<'a> OptionCastUncheckedMutExt<'a> for Option<&'a mut void> {
494			unsafe fn cast_unchecked_mut<T>(self) -> Option<&'a mut T> {
495				self.map(|void_ref| &mut *(void_ref as *mut _ as *mut T))
496			}
497		}
498
499		impl<'a, T> UpcastMutExt<'a> for Option<&'a mut T> {
500			type Output = Option<&'a mut void>;
501
502			fn upcast_mut(self) -> Self::Output {
503				self.map(|t_ref| t_ref.into())
504			}
505		}
506
507		impl<'a, T> UpcastExt<'a> for &'a T {
508			type Output = &'a void;
509
510			fn upcast(self) -> Self::Output {
511				self.into()
512			}
513		}
514
515		impl<'a, T> UpcastMutExt<'a> for &'a mut T {
516			type Output = &'a mut void;
517
518			fn upcast_mut(self) -> Self::Output {
519				self.into()
520			}
521		}
522
523		impl c_str {
524			/// Interprets a zero-terminated Rust [`str`] as [`c_str`].
525			///
526			/// # Errors
527			///
528			/// Iff `text` does not end with `'\0'`.
529			pub fn ref_from_str(text: &str) -> Result<&Self, ()> {
530				match text.ends_with('\0') {
531					true => Ok(unsafe { &*(text as *const _ as *const c_str) }),
532					false => Err(()),
533				}
534			}
535		}
536
537		impl<'a> TryFrom<&'a str> for &'a c_str {
538			type Error = ();
539
540			fn try_from(value: &'a str) -> Result<Self, Self::Error> {
541				match value.ends_with('\0') {
542					true => Ok(unsafe { &*(value as *const _ as *const c_str) }),
543					false => Err(()),
544				}
545			}
546		}
547	}
548}