1use core::time::Duration;
2
3use crate::*;
4
5#[derive(Clone, Debug)]
7pub struct Settings {
8 pub theme: Theme,
10
11 pub language: Language,
13
14 pub rotate_screen: bool,
19
20 pub reduce_flashing: bool,
22
23 pub contrast: bool,
30
31 pub easter_eggs: bool,
33}
34
35#[derive(PartialEq, Eq, Copy, Clone, Debug, Default)]
36pub enum Language {
37 #[default]
39 English,
40 Dutch,
42 French,
44 German,
46 Italian,
48 Polish,
50 Romanian,
52 Russian,
54 Spanish,
56 Swedish,
58 Turkish,
60 Ukrainian,
62 TokiPona,
64}
65
66impl Language {
67 #[must_use]
68 pub fn from_code(b: [u8; 2]) -> Option<Self> {
69 let code = match b {
70 [b'd', b'e'] => Self::German,
71 [b'e', b'n'] => Self::English,
72 [b'e', b's'] => Self::Spanish,
73 [b'f', b'r'] => Self::French,
74 [b'i', b't'] => Self::Italian,
75 [b'n', b'l'] => Self::Dutch,
76 [b'p', b'o'] => Self::Polish,
77 [b'r', b'o'] => Self::Romanian,
78 [b'r', b'u'] => Self::Russian,
79 [b's', b'v'] => Self::Swedish,
80 [b't', b'p'] => Self::TokiPona,
81 [b't', b'r'] => Self::Turkish,
82 [b'u', b'k'] => Self::Ukrainian,
83 _ => return None,
84 };
85 Some(code)
86 }
87
88 #[must_use]
89 pub fn code_array(self) -> [u8; 2] {
90 match self {
91 Self::English => [b'e', b'n'],
92 Self::Dutch => [b'n', b'l'],
93 Self::French => [b'f', b'r'],
94 Self::German => [b'd', b'e'],
95 Self::Italian => [b'i', b't'],
96 Self::Polish => [b'p', b'o'],
97 Self::Romanian => [b'r', b'o'],
98 Self::Russian => [b'r', b'u'],
99 Self::Spanish => [b'e', b's'],
100 Self::Swedish => [b's', b'v'],
101 Self::Turkish => [b't', b'r'],
102 Self::Ukrainian => [b'u', b'k'],
103 Self::TokiPona => [b't', b'p'],
104 }
105 }
106
107 #[must_use]
108 pub fn code_str(self) -> &'static str {
109 match self {
110 Self::English => "en",
111 Self::Dutch => "nl",
112 Self::French => "fr",
113 Self::German => "de",
114 Self::Italian => "it",
115 Self::Polish => "po",
116 Self::Romanian => "ro",
117 Self::Russian => "ru",
118 Self::Spanish => "es",
119 Self::Swedish => "sv",
120 Self::Turkish => "tr",
121 Self::Ukrainian => "uk",
122 Self::TokiPona => "tp",
123 }
124 }
125
126 #[must_use]
128 pub fn name_english(self) -> &'static str {
129 match self {
130 Self::English => "English",
131 Self::Dutch => "Dutch",
132 Self::French => "French",
133 Self::German => "German",
134 Self::Italian => "Italian",
135 Self::Polish => "Polish",
136 Self::Romanian => "Romanian",
137 Self::Russian => "Russian",
138 Self::Spanish => "Spanish",
139 Self::Swedish => "Swedish",
140 Self::TokiPona => "TokiPona",
141 Self::Turkish => "Turkish",
142 Self::Ukrainian => "Ukrainian",
143 }
144 }
145
146 #[must_use]
148 pub fn name_native(self) -> &'static str {
149 match self {
150 Self::English => "English",
151 Self::Dutch => "Nederlands",
152 Self::French => "Franรงais",
153 Self::German => "Deutsch",
154 Self::Italian => "Italiano",
155 Self::Polish => "Polski",
156 Self::Romanian => "Romรขnฤ",
157 Self::Russian => "ะ ัััะบะธะน",
158 Self::Spanish => "Espaรฑol",
159 Self::Swedish => "Svenska",
160 Self::TokiPona => "toki pona",
161 Self::Turkish => "Tรผrkรงe",
162 Self::Ukrainian => "ะฃะบัะฐัะฝััะบะฐ",
163 }
164 }
165
166 #[must_use]
170 pub fn encoding(self) -> &'static str {
171 match self {
172 Self::English | Self::Dutch | Self::TokiPona => "ascii",
176 Self::Italian | Self::Spanish | Self::Swedish => "iso_8859_1",
177 Self::German | Self::French => "iso_8859_2",
178 Self::Russian | Self::Ukrainian => "iso_8859_5",
179 Self::Turkish => "iso_8859_9",
180 Self::Polish => "iso_8859_13",
181 Self::Romanian => "iso_8859_16",
182 }
183 }
184}
185
186#[derive(Clone, Copy, Debug)]
195pub struct Theme {
196 pub id: u8,
197 pub primary: Color,
199 pub secondary: Color,
201 pub accent: Color,
203 pub bg: Color,
205}
206
207impl Default for Theme {
208 fn default() -> Self {
209 Self {
210 id: 0,
211 primary: Color::Black,
212 secondary: Color::LightGray,
213 accent: Color::Green,
214 bg: Color::White,
215 }
216 }
217}
218
219#[macro_export]
221macro_rules! dbg {
222 () => {
223 $crate::log_debug!(
224 "[{}:{}:{}]",
225 $crate::file!(),
226 $crate::line!(),
227 $crate::column!()
228 )
229 };
230 ($val:expr $(,)?) => {
231 match $val {
232 tmp => {
233 $crate::log_debug!(
234 "[{}:{}:{}] {} = {:#?}",
235 $crate::file!(),
236 $crate::line!(),
237 $crate::column!(),
238 $crate::stringify!($val),
239 &&tmp as &dyn $crate::fmt::Debug,
240 );
241 tmp
242 }
243 }
244 };
245}
246
247#[macro_export]
253macro_rules! log_debug {
254 () => {
255 $crate::log_debug(concat!(file!(), ":", line!()));
256 };
257 ($f: literal) => {
258 $crate::log_debug($f);
259 };
260 ($f: expr) => {
261 $crate::log_debug(&$f);
262 };
263 ($f: literal, $( $a:expr ),* ) => {
264 $crate::log_debug(&alloc::format!($f, $( $a ),* ));
265 };
266}
267
268pub fn log_debug(t: &str) {
270 let ptr = t.as_ptr() as u32;
271 let len = t.len() as u32;
272 unsafe {
273 bindings::log_debug(ptr, len);
274 }
275}
276
277#[macro_export]
281macro_rules! log_error {
282 () => {
283 $crate::log_error(concat!(file!(), ":", line!()));
284 };
285 ($f: literal) => {
286 $crate::log_error($f);
287 };
288 ($f: expr) => {
289 $crate::log_error(&$f);
290 };
291 ($f: literal, $( $a:expr ),* ) => {
292 $crate::log_error(&alloc::format!($f, $( $a ),* ));
293 };
294}
295
296pub fn log_error(t: &str) {
298 let ptr = t.as_ptr() as u32;
299 let len = t.len() as u32;
300 unsafe {
301 bindings::log_error(ptr, len);
302 }
303}
304
305pub fn set_seed(seed: u32) {
307 unsafe {
308 bindings::set_seed(seed);
309 }
310}
311
312#[must_use]
314pub fn get_random() -> u32 {
315 unsafe { bindings::get_random() }
316}
317
318#[must_use]
320pub fn get_time() -> Duration {
321 let micros = unsafe { bindings::get_time() };
322 Duration::from_micros(micros)
323}
324
325#[cfg(feature = "alloc")]
330#[must_use]
331pub fn get_name_buf(p: Peer) -> alloc::string::String {
332 let mut buf = [0u8; 16];
333 let name = get_name(p, &mut buf);
334 alloc::string::String::from(name)
335}
336
337#[must_use]
342pub fn get_name(p: Peer, buf: &mut [u8; 16]) -> &str {
343 let ptr = buf.as_ptr() as u32;
344 let len = unsafe { bindings::get_name(u32::from(p.0), ptr) };
345 let buf = &buf[..len as usize];
346 unsafe { core::str::from_utf8_unchecked(buf) }
347}
348
349#[must_use]
355pub fn get_settings<P: AnyPeer>(p: P) -> Settings {
356 let raw = unsafe { bindings::get_settings(u32::from(p.into_u8())) };
357 let code = [(raw >> 8) as u8, raw as u8];
358 let language = Language::from_code(code).unwrap_or_default();
359 let flags = raw >> 16;
360 let theme = raw >> 32;
361 let theme = Theme {
362 id: theme as u8,
363 primary: parse_color(theme >> 20),
364 secondary: parse_color(theme >> 16),
365 accent: parse_color(theme >> 12),
366 bg: parse_color(theme >> 8),
367 };
368 Settings {
369 theme,
370 language,
371 rotate_screen: (flags & 0b0001) != 0,
372 reduce_flashing: (flags & 0b0010) != 0,
373 contrast: (flags & 0b0100) != 0,
374 easter_eggs: (flags & 0b1000) != 0,
375 }
376}
377
378fn parse_color(c: u64) -> Color {
379 Color::from((c as u8 & 0xf) + 1)
380}
381
382pub fn quit() {
384 unsafe { bindings::quit() }
385}
386
387pub fn restart() {
389 unsafe { bindings::restart() }
390}
391
392mod bindings {
393 #[link(wasm_import_module = "misc")]
394 unsafe extern "C" {
395 pub(crate) unsafe fn log_debug(ptr: u32, len: u32);
396 pub(crate) unsafe fn log_error(ptr: u32, len: u32);
397 pub(crate) unsafe fn set_seed(seed: u32);
398 pub(crate) unsafe fn get_random() -> u32;
399 pub(crate) unsafe fn get_time() -> u64;
400 pub(crate) unsafe fn get_name(idx: u32, ptr: u32) -> u32;
401 pub(crate) unsafe fn get_settings(idx: u32) -> u64;
402 pub(crate) unsafe fn restart();
403 pub(crate) unsafe fn quit();
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410
411 #[test]
412 fn test_language_code_roundtrip() {
413 let mut valid_codes = 0;
414 let letters = "abcdefghijklmnopqrstuvwxyz";
415 for fst in letters.as_bytes() {
416 for snd in letters.as_bytes() {
417 let given = [*fst, *snd];
418 let Some(lang) = Language::from_code(given) else {
419 continue;
420 };
421 valid_codes += 1;
422 let actual = lang.code_array();
423 assert_eq!(actual, given);
424 }
425 }
426 assert!(valid_codes > 8);
427 }
428}