#[cfg(target_os = "linux")]
pub use linux::RawKeys;
#[cfg(target_os = "linux")]
mod linux {
use crate::tui::BUTTONS;
use evdev::{Device, EventSummary, KeyCode};
pub struct RawKeys {
devices: Vec<Device>,
down: [bool; BUTTONS],
}
impl RawKeys {
pub fn start() -> Option<RawKeys> {
let mut devices: Vec<Device> = evdev::enumerate()
.map(|(_, d)| d)
.filter(is_keyboard)
.collect();
for d in &mut devices {
let _ = d.set_nonblocking(true);
}
(!devices.is_empty()).then_some(RawKeys {
devices,
down: [false; BUTTONS],
})
}
pub fn poll(&mut self) -> [bool; BUTTONS] {
let mut events = Vec::new();
self.devices.retain_mut(|dev| match dev.fetch_events() {
Ok(iter) => {
events.extend(iter);
true
}
Err(e) => e.kind() == std::io::ErrorKind::WouldBlock,
});
for ev in events {
if let EventSummary::Key(_, code, val) = ev.destructure() {
if val != 2 {
if let Some(b) = button_for(code) {
self.down[b] = val == 1;
}
}
}
}
self.down
}
}
fn is_keyboard(dev: &Device) -> bool {
dev.supported_keys()
.is_some_and(|keys| keys.contains(KeyCode::KEY_LEFT) && keys.contains(KeyCode::KEY_Z))
}
fn button_for(key: KeyCode) -> Option<usize> {
Some(match key {
KeyCode::KEY_LEFT => 0,
KeyCode::KEY_RIGHT => 1,
KeyCode::KEY_UP => 2,
KeyCode::KEY_DOWN => 3,
KeyCode::KEY_Z | KeyCode::KEY_C | KeyCode::KEY_N => 4,
KeyCode::KEY_X | KeyCode::KEY_V | KeyCode::KEY_M => 5,
_ => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn buttons_match_the_windowed_console() {
assert_eq!(button_for(KeyCode::KEY_LEFT), Some(0));
assert_eq!(button_for(KeyCode::KEY_DOWN), Some(3));
for k in [KeyCode::KEY_Z, KeyCode::KEY_C, KeyCode::KEY_N] {
assert_eq!(button_for(k), Some(4));
}
for k in [KeyCode::KEY_X, KeyCode::KEY_V, KeyCode::KEY_M] {
assert_eq!(button_for(k), Some(5));
}
assert_eq!(button_for(KeyCode::KEY_A), None);
assert_eq!(button_for(KeyCode::KEY_ENTER), None);
}
}
}
#[cfg(not(target_os = "linux"))]
pub use fallback::RawKeys;
#[cfg(not(target_os = "linux"))]
mod fallback {
use crate::tui::BUTTONS;
pub struct RawKeys;
impl RawKeys {
pub fn start() -> Option<RawKeys> {
None
}
pub fn poll(&mut self) -> [bool; BUTTONS] {
[false; BUTTONS]
}
}
}