1use crate::*;
2
3#[derive(Clone, Debug)]
5pub struct Settings {
6 pub theme: Theme,
8
9 pub language: Language,
11
12 pub rotate_screen: bool,
17
18 pub reduce_flashing: bool,
20
21 pub contrast: bool,
28
29 pub easter_eggs: bool,
31}
32
33#[derive(PartialEq, Eq, Copy, Clone, Debug, Default)]
34pub enum Language {
35 #[default]
37 English,
38 Dutch,
40 French,
42 German,
44 Italian,
46 Polish,
48 Romanian,
50 Russian,
52 Spanish,
54 Swedish,
56 Turkish,
58 Ukrainian,
60 TokiPona,
62}
63
64impl Language {
65 #[must_use]
66 pub fn from_code(b: [u8; 2]) -> Option<Self> {
67 let code = match b {
68 [b'd', b'e'] => Self::German,
69 [b'e', b'n'] => Self::English,
70 [b'e', b's'] => Self::Spanish,
71 [b'f', b'r'] => Self::French,
72 [b'i', b't'] => Self::Italian,
73 [b'n', b'l'] => Self::Dutch,
74 [b'p', b'o'] => Self::Polish,
75 [b'r', b'o'] => Self::Romanian,
76 [b'r', b'u'] => Self::Russian,
77 [b's', b'v'] => Self::Swedish,
78 [b't', b'p'] => Self::TokiPona,
79 [b't', b'r'] => Self::Turkish,
80 [b'u', b'k'] => Self::Ukrainian,
81 _ => return None,
82 };
83 Some(code)
84 }
85
86 #[must_use]
87 pub fn code_array(self) -> [u8; 2] {
88 match self {
89 Self::English => [b'e', b'n'],
90 Self::Dutch => [b'n', b'l'],
91 Self::French => [b'f', b'r'],
92 Self::German => [b'd', b'e'],
93 Self::Italian => [b'i', b't'],
94 Self::Polish => [b'p', b'o'],
95 Self::Romanian => [b'r', b'o'],
96 Self::Russian => [b'r', b'u'],
97 Self::Spanish => [b'e', b's'],
98 Self::Swedish => [b's', b'v'],
99 Self::Turkish => [b't', b'r'],
100 Self::Ukrainian => [b'u', b'k'],
101 Self::TokiPona => [b't', b'p'],
102 }
103 }
104
105 #[must_use]
106 pub fn code_str(self) -> &'static str {
107 match self {
108 Self::English => "en",
109 Self::Dutch => "nl",
110 Self::French => "fr",
111 Self::German => "de",
112 Self::Italian => "it",
113 Self::Polish => "po",
114 Self::Romanian => "ro",
115 Self::Russian => "ru",
116 Self::Spanish => "es",
117 Self::Swedish => "sv",
118 Self::Turkish => "tr",
119 Self::Ukrainian => "uk",
120 Self::TokiPona => "tp",
121 }
122 }
123
124 #[must_use]
126 pub fn name_english(self) -> &'static str {
127 match self {
128 Self::English => "English",
129 Self::Dutch => "Dutch",
130 Self::French => "French",
131 Self::German => "German",
132 Self::Italian => "Italian",
133 Self::Polish => "Polish",
134 Self::Romanian => "Romanian",
135 Self::Russian => "Russian",
136 Self::Spanish => "Spanish",
137 Self::Swedish => "Swedish",
138 Self::TokiPona => "TokiPona",
139 Self::Turkish => "Turkish",
140 Self::Ukrainian => "Ukrainian",
141 }
142 }
143
144 #[must_use]
146 pub fn name_native(self) -> &'static str {
147 match self {
148 Self::English => "English",
149 Self::Dutch => "Nederlands",
150 Self::French => "Franรงais",
151 Self::German => "Deutsch",
152 Self::Italian => "Italiano",
153 Self::Polish => "Polski",
154 Self::Romanian => "Romรขnฤ",
155 Self::Russian => "ะ ัััะบะธะน",
156 Self::Spanish => "Espaรฑol",
157 Self::Swedish => "Svenska",
158 Self::TokiPona => "toki pona",
159 Self::Turkish => "Tรผrkรงe",
160 Self::Ukrainian => "ะฃะบัะฐัะฝััะบะฐ",
161 }
162 }
163
164 #[must_use]
168 pub fn encoding(self) -> &'static str {
169 match self {
170 Self::English | Self::Dutch | Self::TokiPona => "ascii",
174 Self::Italian | Self::Spanish | Self::Swedish => "iso_8859_1",
175 Self::German | Self::French => "iso_8859_2",
176 Self::Russian | Self::Ukrainian => "iso_8859_5",
177 Self::Turkish => "iso_8859_9",
178 Self::Polish => "iso_8859_13",
179 Self::Romanian => "iso_8859_16",
180 }
181 }
182}
183
184#[derive(Clone, Copy, Debug)]
193pub struct Theme {
194 pub id: u8,
195 pub primary: Color,
197 pub secondary: Color,
199 pub accent: Color,
201 pub bg: Color,
203}
204
205impl Default for Theme {
206 fn default() -> Self {
207 Self {
208 id: 0,
209 primary: Color::Black,
210 secondary: Color::LightGray,
211 accent: Color::Green,
212 bg: Color::White,
213 }
214 }
215}
216
217pub fn log_debug(t: &str) {
219 let ptr = t.as_ptr() as u32;
220 let len = t.len() as u32;
221 unsafe {
222 bindings::log_debug(ptr, len);
223 }
224}
225
226pub fn log_error(t: &str) {
228 let ptr = t.as_ptr() as u32;
229 let len = t.len() as u32;
230 unsafe {
231 bindings::log_error(ptr, len);
232 }
233}
234
235pub fn set_seed(seed: u32) {
237 unsafe {
238 bindings::set_seed(seed);
239 }
240}
241
242#[must_use]
244pub fn get_random() -> u32 {
245 unsafe { bindings::get_random() }
246}
247
248#[cfg(feature = "alloc")]
253#[must_use]
254pub fn get_name_buf(p: Peer) -> alloc::string::String {
255 let mut buf = [0u8; 16];
256 let name = get_name(p, &mut buf);
257 alloc::string::String::from(name)
258}
259
260#[must_use]
265pub fn get_name(p: Peer, buf: &mut [u8; 16]) -> &str {
266 let ptr = buf.as_ptr() as u32;
267 let len = unsafe { bindings::get_name(u32::from(p.0), ptr) };
268 let buf = &buf[..len as usize];
269 unsafe { core::str::from_utf8_unchecked(buf) }
270}
271
272#[must_use]
278pub fn get_settings<P: AnyPeer>(p: P) -> Settings {
279 let raw = unsafe { bindings::get_settings(u32::from(p.into_u8())) };
280 let code = [(raw >> 8) as u8, raw as u8];
281 let language = Language::from_code(code).unwrap_or_default();
282 let flags = raw >> 16;
283 let theme = raw >> 32;
284 let theme = Theme {
285 id: theme as u8,
286 primary: parse_color(theme >> 20),
287 secondary: parse_color(theme >> 16),
288 accent: parse_color(theme >> 12),
289 bg: parse_color(theme >> 8),
290 };
291 Settings {
292 theme,
293 language,
294 rotate_screen: (flags & 0b0001) != 0,
295 reduce_flashing: (flags & 0b0010) != 0,
296 contrast: (flags & 0b0100) != 0,
297 easter_eggs: (flags & 0b1000) != 0,
298 }
299}
300
301fn parse_color(c: u64) -> Color {
302 Color::from((c as u8 & 0xf) + 1)
303}
304
305pub fn quit() {
307 unsafe { bindings::quit() }
308}
309
310mod bindings {
311 #[link(wasm_import_module = "misc")]
312 unsafe extern "C" {
313 pub(crate) unsafe fn log_debug(ptr: u32, len: u32);
314 pub(crate) unsafe fn log_error(ptr: u32, len: u32);
315 pub(crate) unsafe fn set_seed(seed: u32);
316 pub(crate) unsafe fn get_random() -> u32;
317 pub(crate) unsafe fn get_name(idx: u32, ptr: u32) -> u32;
318 pub(crate) unsafe fn get_settings(idx: u32) -> u64;
319 pub(crate) unsafe fn quit();
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_language_code_roundtrip() {
329 let mut valid_codes = 0;
330 let letters = "abcdefghijklmnopqrstuvwxyz";
331 for fst in letters.as_bytes() {
332 for snd in letters.as_bytes() {
333 let given = [*fst, *snd];
334 let Some(lang) = Language::from_code(given) else {
335 continue;
336 };
337 valid_codes += 1;
338 let actual = lang.code_array();
339 assert_eq!(actual, given);
340 }
341 }
342 assert!(valid_codes > 8);
343 }
344}