use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use crate::Ev3Result;
const KEY_BUF_LEN: usize = 96;
const EVIOCGKEY: u32 = 2_153_792_792;
struct FileMapEntry {
pub file: File,
pub buffer_cache: [u8; KEY_BUF_LEN],
}
impl fmt::Debug for FileMapEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FileMapEntry")
.field("file", &self.file)
.finish()
}
}
#[derive(Debug)]
struct ButtonMapEntry {
pub file_name: String,
pub key_code: u32,
}
#[derive(Debug)]
struct ButtonFileHandler {
file_map: HashMap<String, FileMapEntry>,
button_map: HashMap<String, ButtonMapEntry>,
pressed_buttons: HashSet<String>,
}
impl ButtonFileHandler {
fn new() -> Self {
ButtonFileHandler {
file_map: HashMap::new(),
button_map: HashMap::new(),
pressed_buttons: HashSet::new(),
}
}
fn add_button(&mut self, name: &str, file_name: &str, key_code: u32) -> Ev3Result<()> {
if !self.file_map.contains_key(file_name) {
let file = File::open(file_name)?;
let buffer_cache = [0u8; KEY_BUF_LEN];
self.file_map
.insert(file_name.to_owned(), FileMapEntry { file, buffer_cache });
}
self.button_map.insert(
name.to_owned(),
ButtonMapEntry {
file_name: file_name.to_owned(),
key_code,
},
);
Ok(())
}
fn get_pressed_buttons(&self) -> HashSet<String> {
self.pressed_buttons.clone()
}
fn get_button_state(&self, name: &str) -> bool {
self.pressed_buttons.contains(name)
}
fn process(&mut self) {
for entry in self.file_map.values_mut() {
unsafe {
libc::ioctl(
entry.file.as_raw_fd(),
EVIOCGKEY.into(),
&mut entry.buffer_cache,
);
}
}
self.pressed_buttons.clear();
for (
btn_name,
ButtonMapEntry {
file_name,
key_code,
},
) in self.button_map.iter()
{
let buffer = &self.file_map[file_name].buffer_cache;
if (buffer[(key_code / 8) as usize] & 1 << (key_code % 8)) != 0 {
self.pressed_buttons.insert(btn_name.to_owned());
}
}
}
}
#[derive(Debug, Clone)]
pub struct Ev3Button {
button_handler: Rc<RefCell<ButtonFileHandler>>,
}
impl Ev3Button {
pub fn new() -> Ev3Result<Self> {
let mut handler = ButtonFileHandler::new();
handler.add_button("up", "/dev/input/by-path/platform-gpio_keys-event", 103)?;
handler.add_button("down", "/dev/input/by-path/platform-gpio_keys-event", 108)?;
handler.add_button("left", "/dev/input/by-path/platform-gpio_keys-event", 105)?;
handler.add_button("right", "/dev/input/by-path/platform-gpio_keys-event", 106)?;
handler.add_button("enter", "/dev/input/by-path/platform-gpio_keys-event", 28)?;
handler.add_button(
"backspace",
"/dev/input/by-path/platform-gpio_keys-event",
14,
)?;
Ok(Self {
button_handler: Rc::new(RefCell::new(handler)),
})
}
pub fn process(&self) {
self.button_handler.borrow_mut().process()
}
pub fn get_pressed_buttons(&self) -> HashSet<String> {
self.button_handler.borrow().get_pressed_buttons()
}
pub fn is_up(&self) -> bool {
self.button_handler.borrow().get_button_state("up")
}
pub fn is_down(&self) -> bool {
self.button_handler.borrow().get_button_state("down")
}
pub fn is_left(&self) -> bool {
self.button_handler.borrow().get_button_state("left")
}
pub fn is_right(&self) -> bool {
self.button_handler.borrow().get_button_state("right")
}
pub fn is_enter(&self) -> bool {
self.button_handler.borrow().get_button_state("enter")
}
pub fn is_backspace(&self) -> bool {
self.button_handler.borrow().get_button_state("backspace")
}
}