hello_world/
hello-world.rs

1//! Unsafe low-level Hello-World example.
2
3#![no_std]
4extern crate alloc;
5use core::ffi::*;
6use core::ptr::null_mut;
7use alloc::boxed::Box;
8
9#[macro_use]
10extern crate playdate_sys as pd;
11use pd::ffi::*;
12
13
14const INITIAL_X: u32 = LCD_COLUMNS / 2;
15const INITIAL_Y: u32 = (pd::ffi::LCD_ROWS - TEXT_HEIGHT) / 2;
16const TEXT_HEIGHT: u32 = 16;
17const TEXT: &str = "Hello, Rust World";
18
19
20/// 2D point
21struct Point<T> {
22	x: T,
23	y: T,
24}
25
26impl<T> Point<T> {
27	const fn new(x: T, y: T) -> Point<T> { Point { x, y } }
28}
29
30
31/// App state
32struct State {
33	location: Point<i32>,
34	velocity: Point<i32>,
35}
36
37impl State {
38	const fn new() -> Self {
39		Self { location: Point::new(INITIAL_X as _, INITIAL_Y as _),
40		       velocity: Point::new(1, 2) }
41	}
42
43
44	/// Updates the state
45	fn update(&mut self) -> Option<()> {
46		unsafe {
47			let graphics = (*pd::API).graphics;
48			(*graphics).clear?(LCDSolidColor::kColorWhite as LCDColor);
49
50			let c_text = CString::new(TEXT).ok()?;
51			let text_width = (*graphics).getTextWidth?(
52			                                           null_mut(),
53			                                           c_text.as_ptr() as *const _,
54			                                           TEXT.len(),
55			                                           PDStringEncoding::kUTF8Encoding,
56			                                           0,
57			);
58			(*graphics).drawText?(
59			                      c_text.as_ptr() as *const _,
60			                      TEXT.len(),
61			                      PDStringEncoding::kUTF8Encoding,
62			                      self.location.x,
63			                      self.location.y,
64			);
65
66			self.location.x += self.velocity.x;
67			self.location.y += self.velocity.y;
68
69			if self.location.x < 0 || self.location.x > LCD_COLUMNS as i32 - text_width {
70				self.velocity.x = -self.velocity.x;
71			}
72
73			if self.location.y < 0 || self.location.y > LCD_ROWS as i32 - TEXT_HEIGHT as i32 {
74				self.velocity.y = -self.velocity.y;
75			}
76
77			(*(*pd::API).system).drawFPS?(0, 0);
78			Some(())
79		}
80	}
81
82
83	/// Event handler
84	fn event(&mut self, event: PDSystemEvent) -> Option<()> {
85		match event {
86			// initial setup
87			PDSystemEvent::kEventInit => unsafe {
88				(*(*pd::API).display).setRefreshRate?(20.0);
89			},
90			_ => {},
91		}
92		Some(())
93	}
94}
95
96
97#[no_mangle]
98/// Proxy event handler, calls `State::event`
99pub extern "C" fn eventHandlerShim(api: *const PlaydateAPI, event: PDSystemEvent, _arg: u32) -> c_int {
100	static mut STATE: Option<Box<State>> = None;
101
102	match event {
103		PDSystemEvent::kEventInit => unsafe {
104			// register the API entry point
105			pd::API = api;
106
107			// create game state
108			if STATE.is_none() {
109				STATE = Some(Box::new(State::new()));
110			}
111			let state = STATE.as_mut().unwrap().as_mut() as *mut State;
112
113			// get `setUpdateCallback` fn
114			let f = (*(*api).system).setUpdateCallback.expect("setUpdateCallback");
115			// register update callback with user-data = our state
116			f(Some(on_update), state.cast());
117		},
118		_ => {},
119	}
120
121	if let Some(state) = unsafe { STATE.as_mut() } {
122		state.event(event).and(Some(0)).unwrap_or(1)
123	} else {
124		1
125	}
126}
127
128
129/// Proxy update callback, calls `State::update`
130unsafe extern "C" fn on_update(state: *mut c_void) -> i32 {
131	let ptr: *mut State = state.cast();
132	let state = ptr.as_mut().expect("missed state");
133	state.update().and(Some(1)).unwrap_or_default()
134}
135
136
137// Needed for debug build
138ll_symbols!();