Skip to content

Commit

Permalink
Lets try and make this headless
Browse files Browse the repository at this point in the history
  • Loading branch information
AethanFoot committed Feb 7, 2024
1 parent 3274ad2 commit ed114ff
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 67 deletions.
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions lefthk-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,24 @@ repository = "https://github.com/leftwm/lefthk"
description = "A hotkey daemon for Adventurers"

[dependencies]
evdev-rs = { version = "0.6.1", features = ["serde"] }
mio = "0.8.0"
nix = {version = "0.27.1", features = ["fs", "signal"]}
nix = { version = "0.27.1", features = ["fs", "signal"] }
signal-hook = "0.3.4"
thiserror = "1.0.30"
tokio = { version = "1.14.0", features = ["fs", "io-util", "macros", "net", "rt-multi-thread", "sync", "time"] }
tokio = { version = "1.14.0", features = [
"fs",
"io-util",
"macros",
"net",
"rt-multi-thread",
"sync",
"time",
] }
x11-dl = "2.19.1"
xdg = "2.4.0"
ron = "0.8.0"
serde = { version = "1.0.145", features= ["derive"]}
serde = { version = "1.0.145", features = ["derive"] }
inventory = "0.3.2"

# logging
Expand Down
120 changes: 120 additions & 0 deletions lefthk-core/src/]
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use evdev::Device;
use std::cmp::Ordering;
use std::path::PathBuf;

use crate::errors::{self, Error, LeftError, Result};

#[derive(Debug, Clone)]
pub struct EvDev {
pub name: String,
pub path: PathBuf,
pub phys: String,
}

impl EvDev {
pub fn with_path(path: PathBuf) -> Result<Self> {
let mut input = Device::open(path)?;

Ok(Self {
name: input.name().unwrap_or("").to_string(),
phys: input.phys().unwrap_or("").to_string(),
path,
})
}

pub fn with_name(name: &str, phys: Option<&str>) -> Result<Self> {
let mut devices = Self::obtain_device_list()?;

if let Some(phys) = phys {
match devices.iter().position(|item| item.phys == phys) {
Some(idx) => return Ok(devices.remove(idx)),
None => {
tracing::error!("Exiting due to error: 1");
std::process::exit(1);
}
}
}

let mut devices_with_name: Vec<_> = devices
.into_iter()
.filter(|item| item.name == name)
.collect();

if devices_with_name.is_empty() {
tracing::error!("Exiting due to error: No devices");
std::process::exit(1);
}

if devices_with_name.len() > 1 {
log::warn!("The following devices match name `{}`:", name);
for dev in &devices_with_name {
log::warn!("{:?}", dev);
}
log::warn!(
"evremap will use the first entry. If you want to \
use one of the others, add the corresponding phys \
value to your configuration, for example, \
`phys = \"{}\"` for the second entry in the list.",
devices_with_name[1].phys
);
}

Ok(devices_with_name.remove(0))
}

fn obtain_device_list() -> Result<Vec<EvDev>> {
let mut devices = vec![];
for entry in std::fs::read_dir("/dev/input")? {
let entry = entry?;

if !entry
.file_name()
.to_str()
.unwrap_or("")
.starts_with("event")
{
continue;
}
let path = entry.path();
if path.is_dir() {
continue;
}

match EvDev::with_path(path) {
Ok(item) => devices.push(item),
Err(err) => log::error!("{:#}", err),
}
}

// Order by name, but when multiple devices have the same name,
// order by the event device unit number
devices.sort_by(|a, b| match a.name.cmp(&b.name) {
Ordering::Equal => {
event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path))
}
different => different,
});
Ok(devices)
}
}

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(())
}
2 changes: 1 addition & 1 deletion lefthk-core/src/config/command/chord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl Command for Chord {
}

fn execute(&self, worker: &mut Worker) -> Error {
worker.xwrap.grab_keys(&self.0);
// worker.xwrap.grab_keys(&self.0);
worker.chord_ctx.keybinds = Some(self.0.clone());
Ok(())
}
Expand Down
158 changes: 158 additions & 0 deletions lefthk-core/src/evdev.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use evdev_rs::{Device, DeviceWrapper};
use std::cmp::Ordering;
use std::future::Future;
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::time::Duration;

use crate::errors::{self, Error, LeftError, Result};

pub struct EvDev {
pub devices: Vec<Device>,
pub task_notify: Arc<Notify>,
_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 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);
});

Self {
devices,
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;
})
}

// 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)?)
}

// 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(())
// }
1 change: 1 addition & 0 deletions lefthk-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod tests;
pub mod child;
pub mod config;
pub mod errors;
pub mod evdev;
pub mod ipc;
pub mod worker;
pub mod xkeysym_lookup;
Expand Down
2 changes: 1 addition & 1 deletion lefthk-core/src/worker/context/chord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl Chord {
impl Worker {
pub fn evaluate_chord(&mut self) {
if self.chord_ctx.elapsed {
self.xwrap.grab_keys(&self.keybinds);
// self.xwrap.grab_keys(&self.keybinds);
self.chord_ctx.keybinds = None;
self.chord_ctx.elapsed = false;
}
Expand Down
Loading

0 comments on commit ed114ff

Please # to comment.