Use nix crate to call select(2)

This commit is contained in:
Takashi Kokubun 2021-12-16 21:59:59 -08:00
parent fc30cb8bca
commit e5ac6af6bd
No known key found for this signature in database
GPG Key ID: 6FFC433B12EE23DD
5 changed files with 21 additions and 68 deletions

6
Cargo.lock generated
View File

@ -78,9 +78,9 @@ dependencies = [
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.23.0" version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cc", "cc",
@ -115,7 +115,7 @@ name = "xremap"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"evdev", "evdev",
"libc", "nix",
"yaml-rust", "yaml-rust",
] ]

View File

@ -7,5 +7,5 @@ edition = "2021"
[dependencies] [dependencies]
evdev = "0.11.3" evdev = "0.11.3"
libc = "0.2" nix = "0.23.1"
yaml-rust = "0.4" yaml-rust = "0.4"

View File

@ -1,10 +1,26 @@
extern crate evdev; extern crate evdev;
extern crate nix;
use evdev::Device; use evdev::Device;
use std::error::Error; use std::error::Error;
use nix::sys::select::FdSet;
use nix::sys::select::select;
use std::os::unix::io::{AsRawFd};
pub fn select_device() -> Result<Device, Box<dyn Error>> { pub fn select_device() -> Result<Device, Box<dyn Error>> {
// TODO: stop hard-coding the device // TODO: stop hard-coding the device
let device = Device::open("/dev/input/event19")?; let device = Device::open("/dev/input/event19")?;
return Ok(device); return Ok(device);
} }
pub fn is_readable(device: &mut Device) -> Result<bool, Box<dyn Error>> {
let mut read_fds = FdSet::new();
read_fds.insert(device.as_raw_fd());
select(None, &mut read_fds, None, None, None)?;
for fd in read_fds.fds(None) {
if fd == device.as_raw_fd() {
return Ok(true);
}
}
return Ok(false);
}

View File

@ -5,7 +5,6 @@ use std::process::exit;
mod input; mod input;
mod output; mod output;
mod select;
mod transform; mod transform;
mod config; mod config;
@ -18,7 +17,7 @@ fn event_loop() -> Result<(), Box<dyn Error>> {
.map_err(|e| format!("Failed to grab an input device: {}", e))?; .map_err(|e| format!("Failed to grab an input device: {}", e))?;
loop { loop {
if !select::is_readable(&mut input_device)? { if !input::is_readable(&mut input_device)? {
continue; continue;
} }

View File

@ -1,62 +0,0 @@
extern crate evdev;
use evdev::Device;
use std::os::unix::io::{AsRawFd, RawFd};
use std::{io, mem, ptr};
use std::error::Error;
// A call of this function blocks until the argument device becomes readable.
// TODO: support selecting multiple devices
pub fn is_readable(device: &mut Device) -> Result<bool, Box<dyn Error>> {
let mut fd_set = FdSet::new(); // TODO: maybe this should be prepared in the caller
fd_set.set(device.as_raw_fd());
let result = match unsafe {
libc::select(
device.as_raw_fd() + 1,
to_fdset_ptr(Some(&mut fd_set)),
to_fdset_ptr(None),
to_fdset_ptr(None),
to_ptr::<libc::timeval>(None) as *mut libc::timeval,
)
} {
-1 => Err(io::Error::last_os_error()),
res => Ok(res as usize),
}?;
return Ok(result == 1 && fd_set.is_set(device.as_raw_fd()));
}
fn to_fdset_ptr(opt: Option<&mut FdSet>) -> *mut libc::fd_set {
match opt {
None => ptr::null_mut(),
Some(&mut FdSet(ref mut raw_fd_set)) => raw_fd_set,
}
}
fn to_ptr<T>(opt: Option<&T>) -> *const T {
match opt {
None => ptr::null::<T>(),
Some(p) => p,
}
}
struct FdSet(libc::fd_set);
impl FdSet {
fn new() -> FdSet {
unsafe {
// Is the memory released properly?
let mut raw_fd_set = mem::MaybeUninit::<libc::fd_set>::uninit();
libc::FD_ZERO(raw_fd_set.as_mut_ptr());
FdSet(raw_fd_set.assume_init())
}
}
fn set(&mut self, fd: RawFd) {
unsafe { libc::FD_SET(fd, &mut self.0) }
}
fn is_set(&mut self, fd: RawFd) -> bool {
unsafe { libc::FD_ISSET(fd, &mut self.0) }
}
}