use anyhow::{Context, Result};
use evdev::{Device, EventSummary, EventType, InputEvent, KeyCode};
use flume::Sender;
use serde::Deserialize;
use std::collections::HashMap;
use std::thread;
use crate::devices;
use crate::events::Event;
pub(crate) fn handle_button_presses(
device_name: String,
buttons_to_key_code_names: HashMap<Button, String>,
sender: Sender<Event>,
) -> Result<()> {
let key_codes_to_buttons = map_key_codes_to_buttons(buttons_to_key_code_names)?;
let device = open_device(device_name)?;
let button_handler = ButtonHandler::new(key_codes_to_buttons, sender);
thread::spawn(move || button_handler.run(device));
Ok(())
}
fn map_key_codes_to_buttons(
buttons_to_key_code_names: HashMap<Button, String>,
) -> Result<HashMap<KeyCode, Button>> {
let mut key_codes_to_buttons: HashMap<KeyCode, Button> = HashMap::new();
for (button, key_code_name) in buttons_to_key_code_names {
let key_code = find_key_code_by_name(&key_code_name)
.with_context(|| format!("Unknown button key code name '{}'", key_code_name))?;
key_codes_to_buttons.insert(key_code, button);
}
Ok(key_codes_to_buttons)
}
fn open_device(device_name: String) -> Result<Device> {
let device_label = "button input device".to_string();
devices::open_input_device(device_name, device_label)
}
struct ButtonHandler {
key_codes_to_buttons: HashMap<KeyCode, Button>,
sender: Sender<Event>,
}
impl ButtonHandler {
fn new(key_codes_to_buttons: HashMap<KeyCode, Button>, sender: Sender<Event>) -> Self {
Self {
key_codes_to_buttons,
sender,
}
}
fn run(&self, mut device: Device) -> Result<()> {
loop {
for event in device.fetch_events()? {
if let Some(button) = self.handle_button_press(event) {
let event = Event::ButtonPressed { button };
self.sender.send(event)?;
}
}
}
}
fn handle_button_press(&self, event: InputEvent) -> Option<Button> {
if !self.is_key_released(event) {
return None;
}
let key_code = match event.destructure() {
EventSummary::Key(_, key_code, _) => Some(key_code),
_ => None,
};
key_code.and_then(|kc| self.find_button_for_key_code(kc))
}
fn is_key_released(&self, event: InputEvent) -> bool {
event.event_type() == EventType::KEY && event.value() == 0
}
fn find_button_for_key_code(&self, key_code: KeyCode) -> Option<Button> {
self.key_codes_to_buttons.get(&key_code).cloned()
}
}
fn find_key_code_by_name(name: &str) -> Option<KeyCode> {
match name {
"trigger" => Some(KeyCode::BTN_TRIGGER),
"thumb" => Some(KeyCode::BTN_THUMB),
"thumb2" => Some(KeyCode::BTN_THUMB2),
"top" => Some(KeyCode::BTN_TOP),
"top2" => Some(KeyCode::BTN_TOP2),
"pinkie" => Some(KeyCode::BTN_PINKIE),
"base" => Some(KeyCode::BTN_BASE),
"base2" => Some(KeyCode::BTN_BASE2),
"base3" => Some(KeyCode::BTN_BASE3),
"base4" => Some(KeyCode::BTN_BASE4),
"base5" => Some(KeyCode::BTN_BASE5),
"base6" => Some(KeyCode::BTN_BASE6),
"dead" => Some(KeyCode::BTN_DEAD),
"south" => Some(KeyCode::BTN_SOUTH),
"a" => Some(KeyCode::BTN_SOUTH),
"east" => Some(KeyCode::BTN_EAST),
"b" => Some(KeyCode::BTN_EAST),
"c" => Some(KeyCode::BTN_C),
"north" => Some(KeyCode::BTN_NORTH),
"x" => Some(KeyCode::BTN_NORTH),
"west" => Some(KeyCode::BTN_WEST),
"y" => Some(KeyCode::BTN_WEST),
"z" => Some(KeyCode::BTN_Z),
"tl" => Some(KeyCode::BTN_TL),
"tr" => Some(KeyCode::BTN_TR),
"tl2" => Some(KeyCode::BTN_TL2),
"tr2" => Some(KeyCode::BTN_TR2),
"select" => Some(KeyCode::BTN_SELECT),
"start" => Some(KeyCode::BTN_START),
"mode" => Some(KeyCode::BTN_MODE),
"thumbl" => Some(KeyCode::BTN_THUMBL),
"thumbr" => Some(KeyCode::BTN_THUMBR),
"dpad_up" => Some(KeyCode::BTN_DPAD_UP),
"dpad_down" => Some(KeyCode::BTN_DPAD_DOWN),
"dpad_left" => Some(KeyCode::BTN_DPAD_LEFT),
"dpad_right" => Some(KeyCode::BTN_DPAD_RIGHT),
"trigger_happy1" => Some(KeyCode::BTN_TRIGGER_HAPPY1),
"trigger_happy2" => Some(KeyCode::BTN_TRIGGER_HAPPY2),
"trigger_happy3" => Some(KeyCode::BTN_TRIGGER_HAPPY3),
"trigger_happy4" => Some(KeyCode::BTN_TRIGGER_HAPPY4),
"trigger_happy5" => Some(KeyCode::BTN_TRIGGER_HAPPY5),
"trigger_happy6" => Some(KeyCode::BTN_TRIGGER_HAPPY6),
"trigger_happy7" => Some(KeyCode::BTN_TRIGGER_HAPPY7),
"trigger_happy8" => Some(KeyCode::BTN_TRIGGER_HAPPY8),
_ => None,
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum Button {
Button1,
Button2,
Button3,
Button4,
Button5,
Button6,
Button7,
Button8,
}