From 42ea013b77b9d22e0dfa2b188eb3333828731aa7 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Tue, 5 Mar 2024 15:54:39 +0000 Subject: [PATCH] Simplifiy evdev reading and it actually works --- lefthk-core/src/] | 104 ++++++++++++++++++++++ lefthk-core/src/errors.rs | 2 - lefthk-core/src/evdev.rs | 145 ++++++++----------------------- lefthk-core/src/keysym_lookup.rs | 45 ++++++---- lefthk-core/src/worker/mod.rs | 136 +++++++++++++++++------------ 5 files changed, 248 insertions(+), 184 deletions(-) create mode 100644 lefthk-core/src/] diff --git a/lefthk-core/src/] b/lefthk-core/src/] new file mode 100644 index 0000000..0be7bc3 --- /dev/null +++ b/lefthk-core/src/] @@ -0,0 +1,104 @@ +use evdev_rs::{Device, DeviceWrapper}; +use std::fs::File; +use std::future::Future; +use std::os::fd::AsRawFd; +use std::path::PathBuf; +use std::pin::Pin; +use std::sync::Arc; +use tokio::sync::{oneshot, Notify}; +use tokio::time::Duration; + +use crate::errors::{self, LeftError, Result}; + +pub struct EvDev { + pub devices: Vec, + pub task_notify: Arc, + _task_guards: Vec>, +} + +impl EvDev { + pub fn new() -> Self { + let task_notify = Arc::new(Notify::new()); + + let mut task_guards: Vec> = vec![]; + let mut devices = vec![]; + for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { + let entry = errors::exit_on_error!(entry); + + if !entry + .file_name() + .to_str() + .unwrap_or("") + .starts_with("event") + { + continue; + } + let path = entry.path(); + if path.is_dir() { + continue; + } + + match device_with_path(path) { + Ok(item) => devices.push(item), + Err(err) => tracing::error!("{:#}", err), + } + } + devices + .iter() + .filter(|device| { + device.has(evdev_rs::enums::EventType::EV_KEY) + && device.phys().unwrap().contains("input0") + }) + .for_each(|device| { + let (guard, task_guard) = oneshot::channel(); + let notify = task_notify.clone(); + const SERVER: mio::Token = mio::Token(0); + let fd = device.file().as_raw_fd(); + let mut poll = errors::exit_on_error!(mio::Poll::new()); + let mut events = mio::Events::with_capacity(1); + errors::exit_on_error!(poll.registry().register( + &mut mio::unix::SourceFd(&fd), + SERVER, + mio::Interest::READABLE, + )); + let timeout = Duration::from_millis(100); + tokio::task::spawn_blocking(move || loop { + if guard.is_closed() { + println!("Bye"); + return; + } + + if let Err(err) = poll.poll(&mut events, Some(timeout)) { + tracing::warn!("Xlib socket poll failed with {:?}", err); + continue; + } + + events + .iter() + .filter(|event| SERVER == event.token()) + .for_each(|_| notify.notify_one()); + }); + task_guards.push(task_guard); + }); + + println!("Setup keyboard watcher"); + + Self { + devices: files, + task_notify, + _task_guards: task_guards, + } + } + + pub fn wait_readable(&mut self) -> Pin>> { + let task_notify = self.task_notify.clone(); + Box::pin(async move { + task_notify.notified().await; + }) + } +} + +pub fn device_with_path(path: PathBuf) -> Result<(Device, File)> { + let f = std::fs::File::open(&path)?; + Ok((Device::new_from_path(path)?, f)) +} diff --git a/lefthk-core/src/errors.rs b/lefthk-core/src/errors.rs index ac53c98..b6ef807 100644 --- a/lefthk-core/src/errors.rs +++ b/lefthk-core/src/errors.rs @@ -33,8 +33,6 @@ pub enum LeftError { IoError(#[from] std::io::Error), #[error("Nix errno: {0}.")] NixErrno(#[from] nix::errno::Errno), - #[error("Xlib error: {0}.")] - XlibError(#[from] x11_dl::error::OpenError), #[error("XDG error: {0}.")] XdgBaseDirError(#[from] xdg::BaseDirectoriesError), diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index cabdc4f..250fafa 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,39 +1,21 @@ -use evdev_rs::{Device, DeviceWrapper}; -use std::cmp::Ordering; -use std::future::Future; +use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; use std::os::fd::AsRawFd; use std::path::PathBuf; -use std::pin::Pin; -use std::ptr; -use std::sync::Arc; -use std::task::{Context, Waker}; -use tokio::sync::{oneshot, Notify}; +use tokio::sync::{mpsc, oneshot}; use tokio::time::Duration; -use crate::errors::{self, Error, LeftError, Result}; +use crate::errors::{self, LeftError}; pub struct EvDev { - pub devices: Vec, - pub task_notify: Arc, + pub task_receiver: mpsc::Receiver, _task_guards: Vec>, } -// impl From<(PathBuf, Device)> for EvDev { -// fn from(value: (PathBuf, Device)) -> Self { -// Self { -// name: value.1.name().unwrap_or("").to_string(), -// phys: value.1.physical_path().unwrap_or("").to_string(), -// path: value.0, -// } -// } -// } - impl EvDev { pub fn new() -> Self { - let task_notify = Arc::new(Notify::new()); + let (tx, task_receiver) = mpsc::channel(100); let mut task_guards: Vec> = vec![]; - let mut devices = vec![]; for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { let entry = errors::exit_on_error!(entry); @@ -49,21 +31,9 @@ impl EvDev { if path.is_dir() { continue; } - - match device_with_path(path) { - Ok(item) => devices.push(item), - Err(err) => tracing::error!("{:#}", err), - } - } - devices - .iter() - .filter(|device| { - device.has(evdev_rs::enums::EventType::EV_KEY) - && device.phys().unwrap().contains("input0") - }) - .for_each(|device| { + if let Some(device) = device_with_path(path) { let (guard, task_guard) = oneshot::channel(); - let notify = task_notify.clone(); + let transmitter = tx.clone(); const SERVER: mio::Token = mio::Token(0); let fd = device.file().as_raw_fd(); let mut poll = errors::exit_on_error!(mio::Poll::new()); @@ -74,85 +44,46 @@ impl EvDev { mio::Interest::READABLE, )); let timeout = Duration::from_millis(100); - tokio::task::spawn_blocking(move || loop { - if guard.is_closed() { - println!("Bye"); - return; - } + tokio::task::spawn(async move { + loop { + if guard.is_closed() { + println!("Bye"); + return; + } - if let Err(err) = poll.poll(&mut events, Some(timeout)) { - tracing::warn!("Xlib socket poll failed with {:?}", err); - continue; - } + if let Err(err) = poll.poll(&mut events, Some(timeout)) { + tracing::warn!("Evdev device poll failed with {:?}", err); + continue; + } - events - .iter() - .filter(|event| SERVER == event.token()) - .for_each(|_| notify.notify_one()); + while device.has_event_pending() { + match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { + Ok((status, event)) if status == ReadStatus::Success => { + transmitter.send(event).await.unwrap(); + } + Err(_) => println!("Boo"), + _ => {} + } + } + } }); task_guards.push(task_guard); - }); + } + } Self { - devices, - task_notify, + task_receiver, _task_guards: task_guards, } } - - pub fn wait_readable(&mut self) -> Pin>> { - let task_notify = self.task_notify.clone(); - Box::pin(async move { - task_notify.notified().await; - }) - } - - // fn obtain_device_list() -> Result> { - // let mut devices: Vec = evdev::enumerate() - // .filter(|(_, device)| { - // device - // .supported_keys() - // .map_or(false, |keys| keys.contains(evdev::Key::KEY_ENTER)) - // }) - // .map(|device| Self::from(device)) - // .collect(); - // - // // Order by name, but when multiple devices have the same name, - // // order by the event device unit number - // devices.sort_by(|a, b| { - // match event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) { - // Ordering::Equal => { - // event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) - // } - // different => different, - // } - // }); - // Ok(devices) - // } } -pub fn device_with_path(path: PathBuf) -> Result { - let f = std::fs::File::open(&path)?; - Ok(Device::new_from_path(path)?) +pub fn device_with_path(path: PathBuf) -> Option { + let device = Device::new_from_path(path).ok()?; + if device.has(evdev_rs::enums::EventType::EV_KEY) + && device.phys().unwrap_or("").contains("input0") + { + return Some(device); + } + None } - -// fn event_number_from_path(path: &PathBuf) -> u32 { -// match path.to_str() { -// Some(s) => match s.rfind("event") { -// Some(idx) => s[idx + 5..].parse().unwrap_or(0), -// None => 0, -// }, -// None => 0, -// } -// } -// -// pub fn list_devices() -> Error { -// let devices = EvDev::obtain_device_list()?; -// for item in &devices { -// println!("Name: {}", item.name); -// println!("Path: {}", item.path.display()); -// println!("Phys: {}", item.phys); -// println!(); -// } -// Ok(()) -// } diff --git a/lefthk-core/src/keysym_lookup.rs b/lefthk-core/src/keysym_lookup.rs index ea56202..a02121b 100644 --- a/lefthk-core/src/keysym_lookup.rs +++ b/lefthk-core/src/keysym_lookup.rs @@ -1,5 +1,32 @@ use evdev_rs::enums::EV_KEY; +pub fn is_modifier(key: &EV_KEY) -> bool { + matches!( + key, + EV_KEY::KEY_LEFTSHIFT + | EV_KEY::KEY_RIGHTSHIFT + | EV_KEY::KEY_LEFTCTRL + | EV_KEY::KEY_RIGHTCTRL + | EV_KEY::KEY_LEFTALT + | EV_KEY::KEY_RIGHTALT + | EV_KEY::KEY_LEFTMETA + | EV_KEY::KEY_RIGHTMETA + | EV_KEY::KEY_CAPSLOCK + | EV_KEY::KEY_NUMLOCK + | EV_KEY::KEY_SCROLLLOCK + ) +} + +pub fn into_keys(keys: &[String]) -> Vec { + let mut result = Vec::new(); + for key in keys { + if let Some(key) = into_key(key) { + result.push(key); + } + } + result +} + // We allow this because this function is simply a mapping wrapper. #[allow(clippy::too_many_lines)] #[must_use] @@ -422,9 +449,6 @@ pub fn into_key(key: &str) -> Option { "Kbd_layout_next" => Some(EV_KEY::KEY_KBD_LAYOUT_NEXT), "Emoji_picker" => Some(EV_KEY::KEY_EMOJI_PICKER), "Dictate" => Some(EV_KEY::KEY_DICTATE), - "Camera_access_enable" => Some(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), - "Camera_access_disable" => Some(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), - "Camera_access_toggle" => Some(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), "Brightness_min" => Some(EV_KEY::KEY_BRIGHTNESS_MIN), "Brightness_max" => Some(EV_KEY::KEY_BRIGHTNESS_MAX), "Kbdinputassist_prev" => Some(EV_KEY::KEY_KBDINPUTASSIST_PREV), @@ -454,21 +478,6 @@ pub fn into_key(key: &str) -> Option { "Onscreen_keyboard" => Some(EV_KEY::KEY_ONSCREEN_KEYBOARD), "Privacy_screen_toggle" => Some(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), "Selective_screenshot" => Some(EV_KEY::KEY_SELECTIVE_SCREENSHOT), - "Next_element" => Some(EV_KEY::KEY_NEXT_ELEMENT), - "Previous_element" => Some(EV_KEY::KEY_PREVIOUS_ELEMENT), - "Autopilot_engage_toggle" => Some(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), - "Mark_waypoint" => Some(EV_KEY::KEY_MARK_WAYPOINT), - "Sos" => Some(EV_KEY::KEY_SOS), - "Nav_chart" => Some(EV_KEY::KEY_NAV_CHART), - "Fishing_chart" => Some(EV_KEY::KEY_FISHING_CHART), - "Single_range_radar" => Some(EV_KEY::KEY_SINGLE_RANGE_RADAR), - "Dual_range_radar" => Some(EV_KEY::KEY_DUAL_RANGE_RADAR), - "Radar_overlay" => Some(EV_KEY::KEY_RADAR_OVERLAY), - "Traditional_sonar" => Some(EV_KEY::KEY_TRADITIONAL_SONAR), - "Clearvu_sonar" => Some(EV_KEY::KEY_CLEARVU_SONAR), - "Sidevu_sonar" => Some(EV_KEY::KEY_SIDEVU_SONAR), - "Nav_info" => Some(EV_KEY::KEY_NAV_INFO), - "Brightness_menu" => Some(EV_KEY::KEY_BRIGHTNESS_MENU), "Macro1" => Some(EV_KEY::KEY_MACRO1), "Macro2" => Some(EV_KEY::KEY_MACRO2), "Macro3" => Some(EV_KEY::KEY_MACRO3), diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 49991c9..0a750b5 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -1,17 +1,34 @@ pub mod context; -use std::path::Path; - use crate::child::Children; use crate::config::{command, Keybind}; -use crate::errors::{self, Error, LeftError}; -use crate::evdev; +use crate::errors::{self, LeftError}; use crate::evdev::EvDev; use crate::ipc::Pipe; -use evdev_rs::enums::EV_KEY; -use evdev_rs::ReadFlag; +use crate::keysym_lookup; +use evdev_rs::enums::{EventCode, EV_KEY}; +use evdev_rs::InputEvent; use xdg::BaseDirectories; +#[derive(Clone, Copy, Debug)] +enum KeyEventType { + Release, + Press, + Repeat, + Unknown, +} + +impl From for KeyEventType { + fn from(value: i32) -> Self { + match value { + 0 => Self::Release, + 1 => Self::Press, + 2 => Self::Repeat, + _ => Self::Unknown, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Status { Reload, @@ -54,18 +71,11 @@ impl Worker { tokio::select! { () = self.children.wait_readable() => { + println!("Reaping children"); self.children.reap(); } - () = self.evdev.wait_readable() => { - for device in &self.evdev.devices { - while device.has_event_pending() { - match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { - Ok((_, event)) if event.value == 1 => println!("{:?}", event), - Err(_) => println!("Boo"), - _ => {}, - } - } - } + Some(event) = self.evdev.task_receiver.recv() => { + self.handle_event(&event); } Some(command) = pipe.get_next_command() => { errors::log_on_error!(command.execute(&mut self)); @@ -82,47 +92,59 @@ impl Worker { errors::exit_on_error!(Pipe::new(pipe_file).await) } - // fn handle_event(&mut self, xlib_event: &xlib::XEvent) { - // let error = match xlib_event.get_type() { - // xlib::KeyPress => self.handle_key_press(&xlib::XKeyEvent::from(xlib_event)), - // xlib::MappingNotify => { - // self.handle_mapping_notify(&mut xlib::XMappingEvent::from(xlib_event)) - // } - // _ => return, - // }; - // errors::log_on_error!(error); - // } - // - // fn handle_key_press(&mut self, event: &xlib::XKeyEvent) -> Error { - // let key = self.xwrap.keycode_to_keysym(event.keycode); - // let mask = xkeysym_lookup::clean_mask(event.state); - // if let Some(keybind) = self.get_keybind((mask, key)) { - // if let Ok(command) = command::denormalize(&keybind.command) { - // return command.execute(self); - // } - // } else { - // return Err(LeftError::CommandNotFound); - // } - // Ok(()) - // } - // - // fn get_keybind(&self, mask_key_pair: (u32, u32)) -> Option { - // let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds { - // keybinds - // } else { - // &self.keybinds - // }; - // keybinds - // .iter() - // .find(|keybind| { - // if let Some(key) = xkeysym_lookup::into_keysym(&keybind.key) { - // let mask = xkeysym_lookup::into_modmask(&keybind.modifier); - // return mask_key_pair == (mask, key); - // } - // false - // }) - // .cloned() - // } + fn handle_event(&mut self, event: &InputEvent) { + let r#type = KeyEventType::from(event.value); + match r#type { + KeyEventType::Release => { + if let EventCode::EV_KEY(key) = event.event_code { + if let Some(index) = self.keys_pressed.iter().position(|&k| k == key) { + self.keys_pressed.remove(index); + } + } + } + KeyEventType::Press => { + let mut new_key = false; + if let EventCode::EV_KEY(key) = event.event_code { + if !self.keys_pressed.contains(&key) { + self.keys_pressed.push(key); + new_key = true; + } + } + if new_key { + println!("Keys: {:?}", self.keys_pressed); + if let Some(keybind) = self.check_for_keybind() { + if let Ok(command) = command::denormalize(&keybind.command) { + let _ = command.execute(self); + } else { + errors::log_on_error!(Err(LeftError::CommandNotFound)); + } + } + } + } + KeyEventType::Repeat => {} + KeyEventType::Unknown => {} + } + } + + fn check_for_keybind(&self) -> Option { + let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds { + keybinds + } else { + &self.keybinds + }; + keybinds + .iter() + .find(|keybind| { + if let Some(key) = keysym_lookup::into_key(&keybind.key) { + let modifiers = keysym_lookup::into_keys(&keybind.modifier); + let keys: Vec = + modifiers.into_iter().chain(std::iter::once(key)).collect(); + return keys.iter().all(|key| self.keys_pressed.contains(key)); + } + false + }) + .cloned() + } // // fn handle_mapping_notify(&self, event: &mut xlib::XMappingEvent) -> Error { // if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard {