1use std::fs;
4
5use std::cell::RefCell;
6use std::collections::HashMap;
7use std::collections::HashSet;
8use std::fmt;
9use std::fs::File;
10use std::os::unix::io::AsRawFd;
11use std::path::Path;
12use std::rc::Rc;
13
14use paste::paste;
15
16use crate::driver::DRIVER_PATH;
17use crate::utils::OrErr;
18use crate::{Attribute, Ev3Result};
19
20pub type Color = (u8, u8);
22
23#[derive(Debug, Clone)]
25pub struct Led {
26 left_red: Attribute,
27 left_green: Attribute,
28 right_red: Attribute,
29 right_green: Attribute,
30}
31
32impl Led {
33 pub const COLOR_OFF: Color = (0, 0);
35
36 pub const COLOR_RED: Color = (255, 0);
38
39 pub const COLOR_GREEN: Color = (0, 255);
41
42 pub const COLOR_AMBER: Color = (255, 255);
44
45 pub const COLOR_ORANGE: Color = (255, 128);
47
48 pub const COLOR_YELLOW: Color = (25, 255);
50
51 pub fn new() -> Ev3Result<Led> {
53 let mut left_red_name = String::new();
54 let mut left_green_name = String::new();
55 let mut right_red_name = String::new();
56 let mut right_green_name = String::new();
57
58 let paths = fs::read_dir(Path::new(DRIVER_PATH).join("leds"))?;
59
60 for path in paths {
61 let file_name = path?.file_name();
62 let name = file_name.to_str().or_err()?.to_owned();
63
64 if name.contains(":brick-status") || name.contains(":ev3dev") {
65 if name.contains("led0:") || name.contains("left:") {
66 if name.contains("red:") {
67 left_red_name = name;
68 } else if name.contains("green:") {
69 left_green_name = name
70 }
71 } else if name.contains("led1:") || name.contains("right:") {
72 if name.contains("red:") {
73 right_red_name = name
74 } else if name.contains("green:") {
75 right_green_name = name
76 }
77 }
78 }
79 }
80
81 let left_red = Attribute::from_sys_class("leds", left_red_name.as_str(), "brightness")?;
82 let left_green = Attribute::from_sys_class("leds", left_green_name.as_str(), "brightness")?;
83 let right_red = Attribute::from_sys_class("leds", right_red_name.as_str(), "brightness")?;
84 let right_green =
85 Attribute::from_sys_class("leds", right_green_name.as_str(), "brightness")?;
86
87 Ok(Led {
88 left_red,
89 left_green,
90 right_red,
91 right_green,
92 })
93 }
94
95 fn get_left_red(&self) -> Ev3Result<u8> {
97 self.left_red.get()
98 }
99
100 fn set_left_red(&self, brightness: u8) -> Ev3Result<()> {
102 self.left_red.set(brightness)
103 }
104
105 fn get_left_green(&self) -> Ev3Result<u8> {
107 self.left_green.get()
108 }
109
110 fn set_left_green(&self, brightness: u8) -> Ev3Result<()> {
112 self.left_green.set(brightness)
113 }
114
115 fn get_right_red(&self) -> Ev3Result<u8> {
117 self.right_red.get()
118 }
119
120 fn set_right_red(&self, brightness: u8) -> Ev3Result<()> {
122 self.right_red.set(brightness)
123 }
124
125 fn get_right_green(&self) -> Ev3Result<u8> {
127 self.right_green.get()
128 }
129
130 fn set_right_green(&self, brightness: u8) -> Ev3Result<()> {
132 self.right_green.set(brightness)
133 }
134
135 pub fn get_left_color(&self) -> Ev3Result<Color> {
137 let red = self.get_left_red()?;
138 let green = self.get_left_green()?;
139
140 Ok((red, green))
141 }
142
143 pub fn set_left_color(&self, color: Color) -> Ev3Result<()> {
145 self.set_left_red(color.0)?;
146 self.set_left_green(color.1)
147 }
148
149 pub fn get_right_color(&self) -> Ev3Result<Color> {
151 let red = self.get_right_red()?;
152 let green = self.get_right_green()?;
153
154 Ok((red, green))
155 }
156
157 pub fn set_right_color(&self, color: Color) -> Ev3Result<()> {
159 self.set_right_red(color.0)?;
160 self.set_right_green(color.1)
161 }
162
163 pub fn get_color(&self) -> Ev3Result<Option<Color>> {
165 let left = self.get_left_color()?;
166 let right = self.get_right_color()?;
167
168 if left.0 == right.0 && left.1 == right.1 {
169 Ok(Some(left))
170 } else {
171 Ok(None)
172 }
173 }
174
175 pub fn set_color(&self, color: Color) -> Ev3Result<()> {
177 self.set_left_color(color)?;
178 self.set_right_color(color)
179 }
180}
181
182const KEY_BUF_LEN: usize = 96;
183const EVIOCGKEY: u32 = 2_153_792_792;
184
185struct FileMapEntry {
187 pub file: File,
188 pub buffer_cache: [u8; KEY_BUF_LEN],
189}
190impl fmt::Debug for FileMapEntry {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 f.debug_struct("FileMapEntry")
194 .field("file", &self.file)
195 .finish()
196 }
197}
198
199#[derive(Debug)]
201struct ButtonMapEntry {
202 pub file_name: String,
203 pub key_code: u32,
204}
205
206type ButtonChangeHandler = Box<dyn Fn(HashSet<String>)>;
207type ButtonHandler = Box<dyn Fn(bool)>;
208
209struct ButtonFileHandler {
213 file_map: HashMap<String, FileMapEntry>,
214 button_map: HashMap<String, ButtonMapEntry>,
215 button_change_handler: Option<ButtonChangeHandler>,
216 button_handlers: HashMap<String, ButtonHandler>,
217 pressed_buttons: HashSet<String>,
218}
219
220impl std::fmt::Debug for ButtonFileHandler {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 f.debug_struct("ButtonFileHandler")
223 .field("file_map", &self.file_map)
224 .field("button_map", &self.button_map)
225 .field(
226 "button_change_handler",
227 &self.button_change_handler.is_some(),
228 )
229 .field("button_handlers", &self.button_map.keys())
230 .field("pressed_buttons", &self.pressed_buttons)
231 .finish()
232 }
233}
234
235impl ButtonFileHandler {
236 fn new() -> Self {
238 ButtonFileHandler {
239 file_map: HashMap::new(),
240 button_map: HashMap::new(),
241 button_change_handler: None,
242 button_handlers: HashMap::new(),
243 pressed_buttons: HashSet::new(),
244 }
245 }
246
247 fn add_button(&mut self, name: &str, file_name: &str, key_code: u32) -> Ev3Result<()> {
249 if !self.file_map.contains_key(file_name) {
250 let file = File::open(file_name)?;
251 let buffer_cache = [0u8; KEY_BUF_LEN];
252
253 self.file_map
254 .insert(file_name.to_owned(), FileMapEntry { file, buffer_cache });
255 }
256
257 self.button_map.insert(
258 name.to_owned(),
259 ButtonMapEntry {
260 file_name: file_name.to_owned(),
261 key_code,
262 },
263 );
264
265 Ok(())
266 }
267
268 fn set_button_handler(&mut self, name: &str, handler: Option<ButtonHandler>) {
270 if let Some(listener) = handler {
271 self.button_handlers.insert(name.to_owned(), listener);
272 } else {
273 self.button_handlers.remove(name);
274 }
275 }
276
277 fn set_button_change_handler(&mut self, handler: Option<ButtonChangeHandler>) {
279 self.button_change_handler = handler;
280 }
281
282 fn get_pressed_buttons(&self) -> HashSet<String> {
284 self.pressed_buttons.clone()
285 }
286
287 fn get_button_state(&self, name: &str) -> bool {
289 self.pressed_buttons.contains(name)
290 }
291
292 fn process(&mut self) {
295 for entry in self.file_map.values_mut() {
296 unsafe {
297 libc::ioctl(
298 entry.file.as_raw_fd(),
299 (EVIOCGKEY as i32).try_into().unwrap(),
300 &mut entry.buffer_cache,
301 );
302 }
303 }
304
305 let old_pressed_buttons = self.pressed_buttons.clone();
306 self.pressed_buttons.clear();
307
308 for (
309 btn_name,
310 ButtonMapEntry {
311 file_name,
312 key_code,
313 },
314 ) in self.button_map.iter()
315 {
316 let buffer = &self.file_map[file_name].buffer_cache;
317
318 if (buffer[(key_code / 8) as usize] & 1 << (key_code % 8)) != 0 {
319 self.pressed_buttons.insert(btn_name.to_owned());
320 }
321 }
322
323 let difference = old_pressed_buttons.symmetric_difference(&self.pressed_buttons);
324
325 let mut difference_count = 0;
326 for button in difference {
327 difference_count += 1;
328 if self.button_handlers.contains_key(button) {
329 self.button_handlers[button](self.get_button_state(button));
330 }
331 }
332
333 if difference_count > 0 {
334 if let Some(ref handler) = self.button_change_handler {
335 handler(self.get_pressed_buttons());
336 }
337 }
338 }
339}
340
341#[derive(Debug, Clone)]
370pub struct Button {
371 button_handler: Rc<RefCell<ButtonFileHandler>>,
372}
373
374impl Button {
375 pub fn new() -> Ev3Result<Self> {
377 let mut handler = ButtonFileHandler::new();
378
379 handler.add_button("up", "/dev/input/by-path/platform-gpio_keys-event", 103)?;
380 handler.add_button("down", "/dev/input/by-path/platform-gpio_keys-event", 108)?;
381 handler.add_button("left", "/dev/input/by-path/platform-gpio_keys-event", 105)?;
382 handler.add_button("right", "/dev/input/by-path/platform-gpio_keys-event", 106)?;
383 handler.add_button("enter", "/dev/input/by-path/platform-gpio_keys-event", 28)?;
384 handler.add_button(
385 "backspace",
386 "/dev/input/by-path/platform-gpio_keys-event",
387 14,
388 )?;
389
390 Ok(Self {
391 button_handler: Rc::new(RefCell::new(handler)),
392 })
393 }
394
395 pub fn process(&self) {
420 self.button_handler.borrow_mut().process()
421 }
422
423 pub fn get_pressed_buttons(&self) -> HashSet<String> {
441 self.button_handler.borrow().get_pressed_buttons()
442 }
443
444 pub fn set_change_handler(&mut self, handler: impl Fn(HashSet<String>) + 'static) {
466 self.button_handler
467 .borrow_mut()
468 .set_button_change_handler(Some(Box::new(handler)))
469 }
470
471 pub fn remove_change_handler(&mut self) {
473 self.button_handler
474 .borrow_mut()
475 .set_button_change_handler(None)
476 }
477
478 ev3_button_functions!(up);
479 ev3_button_functions!(down);
480 ev3_button_functions!(left);
481 ev3_button_functions!(right);
482 ev3_button_functions!(enter);
483 ev3_button_functions!(backspace);
484}