Skip to content

Commit

Permalink
Simplifiy evdev reading and it actually works
Browse files Browse the repository at this point in the history
  • Loading branch information
AethanFoot committed Mar 5, 2024
1 parent 6fdb435 commit 42ea013
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 184 deletions.
104 changes: 104 additions & 0 deletions lefthk-core/src/]
Original file line number Diff line number Diff line change
@@ -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<File>,
pub task_notify: Arc<Notify>,
_task_guards: Vec<oneshot::Receiver<()>>,
}

impl EvDev {
pub fn new() -> Self {
let task_notify = Arc::new(Notify::new());

let mut task_guards: Vec<oneshot::Receiver<()>> = 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<Box<dyn Future<Output = ()>>> {
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))
}
2 changes: 0 additions & 2 deletions lefthk-core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),

Expand Down
145 changes: 38 additions & 107 deletions lefthk-core/src/evdev.rs
Original file line number Diff line number Diff line change
@@ -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<Device>,
pub task_notify: Arc<Notify>,
pub task_receiver: mpsc::Receiver<InputEvent>,
_task_guards: Vec<oneshot::Receiver<()>>,
}

// 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<oneshot::Receiver<()>> = 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);

Expand All @@ -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());
Expand All @@ -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<Box<dyn Future<Output = ()>>> {
let task_notify = self.task_notify.clone();
Box::pin(async move {
task_notify.notified().await;
})
}

// fn obtain_device_list() -> Result<Vec<EvDev>> {
// let mut devices: Vec<EvDev> = 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<Device> {
let f = std::fs::File::open(&path)?;
Ok(Device::new_from_path(path)?)
pub fn device_with_path(path: PathBuf) -> Option<Device> {
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(())
// }
45 changes: 27 additions & 18 deletions lefthk-core/src/keysym_lookup.rs
Original file line number Diff line number Diff line change
@@ -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<EV_KEY> {
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]
Expand Down Expand Up @@ -422,9 +449,6 @@ pub fn into_key(key: &str) -> Option<EV_KEY> {
"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),
Expand Down Expand Up @@ -454,21 +478,6 @@ pub fn into_key(key: &str) -> Option<EV_KEY> {
"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),
Expand Down
Loading

0 comments on commit 42ea013

Please sign in to comment.