pros_simulator/host/
lcd.rs1use std::mem::replace;
2
3use pros_simulator_interface::{LcdLines, SimulatorEvent, LCD_HEIGHT, LCD_WIDTH};
4use pros_sys::error as errno;
5use tokio::sync::Mutex;
6use wasmtime::{AsContextMut, Table};
7
8use crate::interface::SimulatorInterface;
9
10#[derive(Debug)]
11pub struct AlreadyInitializedError;
12
13pub struct LcdColors {
14 pub background: u32,
15 pub foreground: u32,
16}
17
18pub struct Lcd {
19 lines: LcdLines,
20 interface: SimulatorInterface,
21 initialized: bool,
22 button_presses: [bool; 3],
23 button_callbacks: [Option<u32>; 3],
24}
25
26impl Lcd {
27 pub fn new(interface: SimulatorInterface) -> Self {
28 Self {
29 lines: Default::default(),
30 interface,
31 initialized: false,
32 button_presses: [false; 3],
33 button_callbacks: [None; 3],
34 }
35 }
36
37 fn assert_initialized(&self) -> Result<(), i32> {
38 if !self.initialized {
39 tracing::error!("Not initialized");
40 return Err(errno::ENXIO);
41 }
42 Ok(())
43 }
44
45 fn assert_line_in_bounds(&self, line: i32) -> Result<(), i32> {
46 if line < 0 || line >= LCD_HEIGHT as i32 {
47 tracing::error!("Line {line} not in bounds");
48 return Err(errno::EINVAL);
49 }
50 Ok(())
51 }
52
53 fn assert_text_length_in_bounds(&self, text: &str) -> Result<(), i32> {
54 if text.len() > LCD_WIDTH as usize {
55 tracing::error!("Text too long for LCD");
56 return Err(errno::EINVAL);
57 }
58 Ok(())
59 }
60
61 pub fn initialize(&mut self) -> Result<(), AlreadyInitializedError> {
62 if self.initialized {
63 return Err(AlreadyInitializedError);
64 }
65 self.initialized = true;
66 self.button_presses = Default::default();
67 self.button_callbacks = Default::default();
68 self.interface.send(SimulatorEvent::LcdInitialized);
69 Ok(())
70 }
71
72 pub fn set_line(&mut self, line: i32, text: &str) -> Result<(), i32> {
73 self.assert_initialized()?;
74 self.assert_line_in_bounds(line)?;
75 self.assert_text_length_in_bounds(text)?;
76
77 self.lines[line as usize] = text.to_string();
78 self.interface
79 .send(SimulatorEvent::LcdUpdated(self.lines.clone()));
80 Ok(())
81 }
82
83 pub fn clear(&mut self) -> Result<(), i32> {
84 self.assert_initialized()?;
85 for line in &mut self.lines {
86 line.clear();
87 }
88 self.interface
89 .send(SimulatorEvent::LcdUpdated(self.lines.clone()));
90 Ok(())
91 }
92
93 pub fn clear_line(&mut self, line: i32) -> Result<(), i32> {
94 self.assert_initialized()?;
95 self.assert_line_in_bounds(line)?;
96
97 self.lines[line as usize] = String::new();
98 self.interface
99 .send(SimulatorEvent::LcdUpdated(self.lines.clone()));
100 Ok(())
101 }
102
103 pub fn set_btn_press_callback(&mut self, button: usize, callback: u32) -> Result<(), i32> {
104 self.assert_initialized()?;
105
106 self.button_callbacks[button] = Some(callback);
107 Ok(())
108 }
109
110 pub async fn press(
113 lcd: &Mutex<Self>,
114 mut store: impl AsContextMut<Data = impl Send>,
115 callback_table: Table,
116 buttons: [bool; 3],
117 ) -> anyhow::Result<()> {
118 let mut lcd = lcd.lock().await;
119 let previous_presses = replace(&mut lcd.button_presses, buttons);
120 let callbacks = lcd.button_callbacks;
121 drop(lcd);
122
123 for (index, button_pressed) in buttons.iter().enumerate() {
124 if *button_pressed && !previous_presses[index] {
125 if let Some(cb_index) = &callbacks[index] {
126 let callback = callback_table.get(&mut store, *cb_index).unwrap();
127 let callback = callback.funcref().unwrap().unwrap();
128 let callback = callback.typed::<(), ()>(&mut store).unwrap();
129 callback.call_async(&mut store, ()).await?;
130 }
131 }
132 }
133
134 Ok(())
135 }
136}