Syncing impl (#36)
* WIP syncing impl * Finish up syncing impl * Reorganize synchronization code + add more docs * Wrap libc::input_id * Remove thread::sleep used for testing * Make EventStream::Item io::Result<InputEvent> * Add RawDevice::empty_state() * Update Device rustdoc * Make raw/sync_stream naming consistent * Update crate docs * Fix missing first event of block * Owned AttributeSet, borrowed AttributeSetRef * Add some basic syncing tests * Add some more syncing tests
This commit is contained in:
parent
6b13fd3d45
commit
3581aa25e0
14 changed files with 1671 additions and 943 deletions
|
@ -21,7 +21,7 @@ futures-core = { version = "0.3", optional = true }
|
|||
|
||||
[dev-dependencies]
|
||||
tokio_1 = { package = "tokio", version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||
futures-util = "0.3"
|
||||
itertools = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "evtest_tokio"
|
||||
|
|
|
@ -7,7 +7,7 @@ fn main() {
|
|||
let mut d = if args.len() > 1 {
|
||||
evdev::Device::open(&args.nth(1).unwrap()).unwrap()
|
||||
} else {
|
||||
let mut devices = evdev::enumerate().collect::<Vec<_>>();
|
||||
let devices = evdev::enumerate().collect::<Vec<_>>();
|
||||
for (i, d) in devices.iter().enumerate() {
|
||||
println!("{}: {}", i, d.name().unwrap_or("Unnamed device"));
|
||||
}
|
||||
|
@ -15,12 +15,13 @@ fn main() {
|
|||
let _ = std::io::stdout().flush();
|
||||
let mut chosen = String::new();
|
||||
std::io::stdin().read_line(&mut chosen).unwrap();
|
||||
devices.swap_remove(chosen.trim().parse::<usize>().unwrap())
|
||||
let n = chosen.trim().parse::<usize>().unwrap();
|
||||
devices.into_iter().nth(n).unwrap()
|
||||
};
|
||||
println!("{}", d);
|
||||
println!("Events:");
|
||||
loop {
|
||||
for ev in d.fetch_events_no_sync().unwrap() {
|
||||
for ev in d.fetch_events().unwrap() {
|
||||
println!("{:?}", ev);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
println!("Events:");
|
||||
loop {
|
||||
match d.fetch_events_no_sync() {
|
||||
match d.fetch_events() {
|
||||
Ok(iterator) => {
|
||||
for ev in iterator {
|
||||
println!("{:?}", ev);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use tokio_1 as tokio;
|
||||
|
||||
use futures_util::TryStreamExt;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut args = std::env::args_os();
|
||||
|
@ -20,10 +18,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
};
|
||||
println!("{}", d);
|
||||
println!("Events:");
|
||||
let mut events = d.into_event_stream_no_sync()?;
|
||||
while let Some(ev) = events.try_next().await? {
|
||||
let mut events = d.into_event_stream()?;
|
||||
loop {
|
||||
let ev = events.next_event().await?;
|
||||
println!("{:?}", ev);
|
||||
}
|
||||
println!("EOF!");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,51 +1,183 @@
|
|||
use bitvec::prelude::*;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// A collection of bits representing either device capability or state.
|
||||
///
|
||||
/// This can be used to iterate across all keys supported by a keyboard, or all buttons supported
|
||||
/// by a joystick. You can also query directly whether a specific bit is set (corresponding to
|
||||
/// whether a key or button is depressed).
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct AttributeSet<'a, T> {
|
||||
bitslice: &'a BitSlice<Lsb0, u8>,
|
||||
#[repr(transparent)]
|
||||
pub struct AttributeSetRef<T> {
|
||||
_indexer: std::marker::PhantomData<T>,
|
||||
bitslice: BitSlice<Lsb0, u8>,
|
||||
}
|
||||
|
||||
impl<'a, T: EvdevEnum> AttributeSet<'a, T> {
|
||||
impl<T: EvdevEnum> AttributeSetRef<T> {
|
||||
#[inline]
|
||||
pub(crate) fn new(bitslice: &'a BitSlice<Lsb0, u8>) -> Self {
|
||||
Self {
|
||||
bitslice,
|
||||
_indexer: std::marker::PhantomData,
|
||||
}
|
||||
fn new(bitslice: &BitSlice<Lsb0, u8>) -> &Self {
|
||||
// SAFETY: for<T> AttributeSet<T> is repr(transparent) over BitSlice<Lsb0, u8>
|
||||
unsafe { &*(bitslice as *const BitSlice<Lsb0, u8> as *const Self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_mut(bitslice: &mut BitSlice<Lsb0, u8>) -> &mut Self {
|
||||
// SAFETY: for<T> AttributeSet<T> is repr(transparent) over BitSlice<Lsb0, u8>
|
||||
unsafe { &mut *(bitslice as *mut BitSlice<Lsb0, u8> as *mut Self) }
|
||||
}
|
||||
|
||||
/// Returns `true` if this AttributeSet contains the passed T.
|
||||
#[inline]
|
||||
pub fn contains(&self, attr: T) -> bool {
|
||||
self.bitslice.get(attr.to_index()).map_or(false, |b| *b)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Provides an iterator over all "set" bits in the collection.
|
||||
pub fn iter(&self) -> impl Iterator<Item = T> + 'a {
|
||||
#[inline]
|
||||
pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
|
||||
self.bitslice.iter_ones().map(T::from_index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn slice(&self, start: T) -> &Self {
|
||||
Self::new(&self.bitslice[start.to_index()..])
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, attr: T) {
|
||||
self.set(attr, true)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, attr: T) {
|
||||
self.set(attr, false)
|
||||
}
|
||||
|
||||
// TODO: figure out a good name for this if we make it public
|
||||
#[inline]
|
||||
pub(crate) fn set(&mut self, attr: T, on: bool) {
|
||||
self.bitslice.set(attr.to_index(), on)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: EvdevEnum + fmt::Debug> fmt::Debug for AttributeSet<'a, T> {
|
||||
impl<T: EvdevEnum + fmt::Debug> fmt::Debug for AttributeSetRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_set().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AttributeSet<T: ArrayedEvdevEnum> {
|
||||
container: T::Array,
|
||||
}
|
||||
|
||||
impl<T: ArrayedEvdevEnum> AttributeSet<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
container: T::zeroed_array(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bitslice(&self) -> &BitSlice<Lsb0, u8> {
|
||||
T::array_as_slice(&self.container)
|
||||
}
|
||||
|
||||
fn as_mut_bitslice(&mut self) -> &mut BitSlice<Lsb0, u8> {
|
||||
T::array_as_slice_mut(&mut self.container)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn as_mut_raw_slice(&mut self) -> &mut [u8] {
|
||||
T::array_as_buf(&mut self.container)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArrayedEvdevEnum> Default for AttributeSet<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArrayedEvdevEnum> Deref for AttributeSet<T> {
|
||||
type Target = AttributeSetRef<T>;
|
||||
fn deref(&self) -> &AttributeSetRef<T> {
|
||||
AttributeSetRef::new(self.as_bitslice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArrayedEvdevEnum> DerefMut for AttributeSet<T> {
|
||||
fn deref_mut(&mut self) -> &mut AttributeSetRef<T> {
|
||||
AttributeSetRef::new_mut(self.as_mut_bitslice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArrayedEvdevEnum> Clone for AttributeSet<T>
|
||||
where
|
||||
T::Array: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
container: self.container.clone(),
|
||||
}
|
||||
}
|
||||
fn clone_from(&mut self, other: &Self) {
|
||||
self.container.clone_from(&other.container)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArrayedEvdevEnum + fmt::Debug> fmt::Debug for AttributeSet<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EvdevEnum: Copy + 'static {
|
||||
fn from_index(i: usize) -> Self;
|
||||
fn to_index(self) -> usize;
|
||||
}
|
||||
|
||||
pub trait ArrayedEvdevEnum: EvdevEnum {
|
||||
type Array;
|
||||
fn array_as_slice(arr: &Self::Array) -> &BitSlice<Lsb0, u8>;
|
||||
fn array_as_slice_mut(arr: &mut Self::Array) -> &mut BitSlice<Lsb0, u8>;
|
||||
fn array_as_buf(arr: &mut Self::Array) -> &mut [u8];
|
||||
fn zeroed_array() -> Self::Array;
|
||||
}
|
||||
|
||||
macro_rules! evdev_enum {
|
||||
($t:ty, Array, $($(#[$attr:meta])* $c:ident = $val:expr,)*) => {
|
||||
evdev_enum!(
|
||||
$t,
|
||||
Array:bitvec::BitArr!(for <$t>::COUNT, in u8),
|
||||
|x| x,
|
||||
|x| x,
|
||||
bitvec::array::BitArray::as_mut_raw_slice,
|
||||
bitvec::array::BitArray::zeroed,
|
||||
$($(#[$attr])* $c = $val,)*
|
||||
);
|
||||
};
|
||||
(
|
||||
$t:ty,
|
||||
Array: $Array:ty, $arr_as_slice:expr, $arr_as_slice_mut:expr, $arr_as_buf:expr, $zero:expr,
|
||||
$($(#[$attr:meta])* $c:ident = $val:expr,)*
|
||||
) => {
|
||||
impl $crate::attribute_set::ArrayedEvdevEnum for $t {
|
||||
type Array = $Array;
|
||||
fn array_as_slice(arr: &Self::Array) -> &bitvec::slice::BitSlice<bitvec::order::Lsb0, u8> {
|
||||
let f: fn(&Self::Array) -> &bitvec::slice::BitSlice<bitvec::order::Lsb0, u8> = $arr_as_slice;
|
||||
f(arr)
|
||||
}
|
||||
fn array_as_slice_mut(arr: &mut Self::Array) -> &mut bitvec::slice::BitSlice<bitvec::order::Lsb0, u8> {
|
||||
let f: fn(&mut Self::Array) -> &mut bitvec::slice::BitSlice<bitvec::order::Lsb0, u8> = $arr_as_slice_mut;
|
||||
f(arr)
|
||||
}
|
||||
fn array_as_buf(arr: &mut Self::Array) -> &mut [u8] {
|
||||
let f: fn(&mut Self::Array) -> &mut [u8] = $arr_as_buf;
|
||||
f(arr)
|
||||
}
|
||||
fn zeroed_array() -> Self::Array {
|
||||
$zero()
|
||||
}
|
||||
}
|
||||
evdev_enum!($t, $($(#[$attr])* $c = $val,)*);
|
||||
};
|
||||
($t:ty, $($(#[$attr:meta])* $c:ident = $val:expr,)*) => {
|
||||
impl $t {
|
||||
$($(#[$attr])* pub const $c: Self = Self($val);)*
|
||||
|
|
|
@ -6,6 +6,7 @@ pub struct EventType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
EventType,
|
||||
Array,
|
||||
/// A bookkeeping event. Usually not important to applications.
|
||||
SYNCHRONIZATION = 0x00,
|
||||
/// A key changed state. A key, or button, is usually a momentary switch (in the circuit sense). It has two
|
||||
|
@ -45,33 +46,21 @@ impl EventType {
|
|||
pub(crate) const COUNT: usize = 0x20;
|
||||
}
|
||||
|
||||
|
||||
/// A "synchronization" message type published by the kernel into the events stream.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Synchronization(pub u16);
|
||||
|
||||
impl Synchronization {
|
||||
evdev_enum!(
|
||||
Synchronization,
|
||||
/// Used to mark the end of a single atomic "reading" from the device.
|
||||
pub const SYN_REPORT: u16 = 0;
|
||||
SYN_REPORT = 0,
|
||||
/// Appears to be unused.
|
||||
pub const SYN_CONFIG: u16 = 1;
|
||||
SYN_CONFIG = 1,
|
||||
/// "Used to synchronize and separate touch events"
|
||||
pub const SYN_MT_REPORT: u16 = 2;
|
||||
SYN_MT_REPORT = 2,
|
||||
/// Ring buffer filled, events were dropped.
|
||||
pub const SYN_DROPPED: u16 = 3;
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Synchronization {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
Synchronization::SYN_REPORT => f.pad(stringify!(SYN_REPORT)),
|
||||
Synchronization::SYN_CONFIG => f.pad(stringify!(SYN_CONFIG)),
|
||||
Synchronization::SYN_MT_REPORT => f.pad(stringify!(SYN_MT_REPORT)),
|
||||
Synchronization::SYN_DROPPED => f.pad(stringify!(SYN_DROPPED)),
|
||||
_ => write!(f, "{}", self.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
SYN_DROPPED = 3,
|
||||
);
|
||||
|
||||
/// Device properties.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
|
@ -79,6 +68,7 @@ pub struct PropType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
PropType,
|
||||
Array,
|
||||
/// This input device needs a pointer ("cursor") for the user to know its state.
|
||||
POINTER = 0x00,
|
||||
/// "direct input devices", according to the header.
|
||||
|
@ -106,6 +96,7 @@ pub struct RelativeAxisType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
RelativeAxisType,
|
||||
Array,
|
||||
REL_X = 0x00,
|
||||
REL_Y = 0x01,
|
||||
REL_Z = 0x02,
|
||||
|
@ -131,6 +122,7 @@ pub struct AbsoluteAxisType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
AbsoluteAxisType,
|
||||
Array,
|
||||
ABS_X = 0x00,
|
||||
ABS_Y = 0x01,
|
||||
ABS_Z = 0x02,
|
||||
|
@ -199,6 +191,7 @@ pub struct SwitchType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
SwitchType,
|
||||
Array,
|
||||
/// "set = lid shut"
|
||||
SW_LID = 0x00,
|
||||
/// "set = tablet mode"
|
||||
|
@ -245,6 +238,7 @@ pub struct LedType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
LedType,
|
||||
Array,
|
||||
LED_NUML = 0x00,
|
||||
LED_CAPSL = 0x01,
|
||||
LED_SCROLLL = 0x02,
|
||||
|
@ -272,6 +266,7 @@ pub struct MiscType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
MiscType,
|
||||
Array,
|
||||
/// Serial number, only exported for tablets ("Transducer Serial Number")
|
||||
MSC_SERIAL = 0x00,
|
||||
/// Only used by the PowerMate driver, right now.
|
||||
|
@ -335,6 +330,7 @@ pub struct SoundType(pub u16);
|
|||
|
||||
evdev_enum!(
|
||||
SoundType,
|
||||
Array,
|
||||
SND_CLICK = 0x00,
|
||||
SND_BELL = 0x01,
|
||||
SND_TONE = 0x02,
|
||||
|
|
93
src/device_state.rs
Normal file
93
src/device_state.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use crate::constants::*;
|
||||
use crate::{AttributeSet, AttributeSetRef, InputEvent, InputEventKind, Key};
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// A cached representation of device state at a certain time.
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceState {
|
||||
/// The state corresponds to kernel state at this timestamp.
|
||||
pub(crate) timestamp: libc::timeval,
|
||||
/// Set = key pressed
|
||||
pub(crate) key_vals: Option<AttributeSet<Key>>,
|
||||
pub(crate) abs_vals: Option<Box<[libc::input_absinfo; AbsoluteAxisType::COUNT]>>,
|
||||
/// Set = switch enabled (closed)
|
||||
pub(crate) switch_vals: Option<AttributeSet<SwitchType>>,
|
||||
/// Set = LED lit
|
||||
pub(crate) led_vals: Option<AttributeSet<LedType>>,
|
||||
}
|
||||
|
||||
// manual Clone impl for clone_from optimization
|
||||
impl Clone for DeviceState {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
timestamp: self.timestamp,
|
||||
key_vals: self.key_vals.clone(),
|
||||
abs_vals: self.abs_vals.clone(),
|
||||
switch_vals: self.switch_vals.clone(),
|
||||
led_vals: self.led_vals.clone(),
|
||||
}
|
||||
}
|
||||
fn clone_from(&mut self, other: &Self) {
|
||||
self.timestamp.clone_from(&other.timestamp);
|
||||
self.key_vals.clone_from(&other.key_vals);
|
||||
self.abs_vals.clone_from(&other.abs_vals);
|
||||
self.switch_vals.clone_from(&other.switch_vals);
|
||||
self.led_vals.clone_from(&other.led_vals);
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceState {
|
||||
/// Returns the time when this snapshot was taken.
|
||||
pub fn timestamp(&self) -> SystemTime {
|
||||
crate::timeval_to_systime(&self.timestamp)
|
||||
}
|
||||
|
||||
/// Returns the set of keys pressed when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if keys are not supported by this device.
|
||||
pub fn key_vals(&self) -> Option<&AttributeSetRef<Key>> {
|
||||
self.key_vals.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the set of absolute axis measurements when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if not supported by this device.
|
||||
pub fn abs_vals(&self) -> Option<&[libc::input_absinfo]> {
|
||||
self.abs_vals.as_deref().map(|v| &v[..])
|
||||
}
|
||||
|
||||
/// Returns the set of switches triggered when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if switches are not supported by this device.
|
||||
pub fn switch_vals(&self) -> Option<&AttributeSetRef<SwitchType>> {
|
||||
self.switch_vals.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the set of LEDs turned on when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if LEDs are not supported by this device.
|
||||
pub fn led_vals(&self) -> Option<&AttributeSetRef<LedType>> {
|
||||
self.led_vals.as_deref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn process_event(&mut self, ev: InputEvent) {
|
||||
match ev.kind() {
|
||||
InputEventKind::Key(code) => {
|
||||
let keys = self
|
||||
.key_vals
|
||||
.as_deref_mut()
|
||||
.expect("got a key event despite not supporting keys");
|
||||
keys.set(code, ev.value() != 0);
|
||||
}
|
||||
InputEventKind::AbsAxis(axis) => {
|
||||
let axes = self
|
||||
.abs_vals
|
||||
.as_deref_mut()
|
||||
.expect("got an abs event despite not supporting absolute axes");
|
||||
axes[axis.0 as usize].value = ev.value();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
104
src/inputid.rs
Normal file
104
src/inputid.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct InputId(libc::input_id);
|
||||
|
||||
impl From<libc::input_id> for InputId {
|
||||
#[inline]
|
||||
fn from(id: libc::input_id) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
impl AsRef<libc::input_id> for InputId {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &libc::input_id {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl InputId {
|
||||
pub fn bus_type(&self) -> BusType {
|
||||
BusType(self.0.bustype)
|
||||
}
|
||||
pub fn vendor(&self) -> u16 {
|
||||
self.0.vendor
|
||||
}
|
||||
pub fn product(&self) -> u16 {
|
||||
self.0.product
|
||||
}
|
||||
pub fn version(&self) -> u16 {
|
||||
self.0.version
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InputId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InputId")
|
||||
.field("bus_type", &self.bus_type())
|
||||
.field("vendor", &format_args!("{:#x}", self.vendor()))
|
||||
.field("product", &format_args!("{:#x}", self.product()))
|
||||
.field("version", &format_args!("{:#x}", self.version()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct BusType(pub u16);
|
||||
|
||||
evdev_enum!(
|
||||
BusType,
|
||||
BUS_PCI = 0x01,
|
||||
BUS_ISAPNP = 0x02,
|
||||
BUS_USB = 0x03,
|
||||
BUS_HIL = 0x04,
|
||||
BUS_BLUETOOTH = 0x05,
|
||||
BUS_VIRTUAL = 0x06,
|
||||
BUS_ISA = 0x10,
|
||||
BUS_I8042 = 0x11,
|
||||
BUS_XTKBD = 0x12,
|
||||
BUS_RS232 = 0x13,
|
||||
BUS_GAMEPORT = 0x14,
|
||||
BUS_PARPORT = 0x15,
|
||||
BUS_AMIGA = 0x16,
|
||||
BUS_ADB = 0x17,
|
||||
BUS_I2C = 0x18,
|
||||
BUS_HOST = 0x19,
|
||||
BUS_GSC = 0x1A,
|
||||
BUS_ATARI = 0x1B,
|
||||
BUS_SPI = 0x1C,
|
||||
BUS_RMI = 0x1D,
|
||||
BUS_CEC = 0x1E,
|
||||
BUS_INTEL_ISHTP = 0x1F,
|
||||
);
|
||||
|
||||
impl fmt::Display for BusType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = match *self {
|
||||
Self::BUS_PCI => "PCI",
|
||||
Self::BUS_ISAPNP => "ISA Plug 'n Play",
|
||||
Self::BUS_USB => "USB",
|
||||
Self::BUS_HIL => "HIL",
|
||||
Self::BUS_BLUETOOTH => "Bluetooth",
|
||||
Self::BUS_VIRTUAL => "Virtual",
|
||||
Self::BUS_ISA => "ISA",
|
||||
Self::BUS_I8042 => "i8042",
|
||||
Self::BUS_XTKBD => "XTKBD",
|
||||
Self::BUS_RS232 => "RS232",
|
||||
Self::BUS_GAMEPORT => "Gameport",
|
||||
Self::BUS_PARPORT => "Parallel Port",
|
||||
Self::BUS_AMIGA => "Amiga",
|
||||
Self::BUS_ADB => "ADB",
|
||||
Self::BUS_I2C => "I2C",
|
||||
Self::BUS_HOST => "Host",
|
||||
Self::BUS_GSC => "GSC",
|
||||
Self::BUS_ATARI => "Atari",
|
||||
Self::BUS_SPI => "SPI",
|
||||
Self::BUS_RMI => "RMI",
|
||||
Self::BUS_CEC => "CEC",
|
||||
Self::BUS_INTEL_ISHTP => "Intel ISHTP",
|
||||
_ => "Unknown",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
867
src/lib.rs
867
src/lib.rs
|
@ -29,10 +29,8 @@
|
|||
//! ```
|
||||
//!
|
||||
//! This state can be queried. For example, the [`DeviceState::led_vals`] method will tell you which
|
||||
//! LEDs are currently lit on the device. This state is not automatically synchronized with the
|
||||
//! kernel. However, as the application reads events, this state will be updated if the event is
|
||||
//! newer than the state timestamp (maintained internally). Additionally, you can call
|
||||
//! [`Device::sync_state`] to explicitly synchronize with the kernel state.
|
||||
//! LEDs are currently lit on the device. As the application reads events, this state will be
|
||||
//! updated, and it will be fully synchronized with the kernel if the stream drops any events.
|
||||
//!
|
||||
//! As the state changes, the kernel will write events into a ring buffer. The application can read
|
||||
//! from this ring buffer, thus retrieving events. However, if the ring buffer becomes full, the
|
||||
|
@ -60,838 +58,33 @@
|
|||
mod attribute_set;
|
||||
|
||||
mod constants;
|
||||
mod raw;
|
||||
mod device_state;
|
||||
mod inputid;
|
||||
pub mod raw_stream;
|
||||
mod scancodes;
|
||||
mod sync_stream;
|
||||
mod sys;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
mod tokio_stream;
|
||||
|
||||
use bitvec::prelude::*;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::{ffi::CString, mem::MaybeUninit};
|
||||
use std::{fmt, io};
|
||||
|
||||
// pub use crate::constants::FFEffect::*;
|
||||
pub use crate::attribute_set::AttributeSet;
|
||||
pub use crate::constants::*;
|
||||
pub use crate::scancodes::*;
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use crate::tokio_stream::EventStream;
|
||||
pub use attribute_set::{AttributeSet, AttributeSetRef};
|
||||
pub use constants::*;
|
||||
pub use device_state::DeviceState;
|
||||
pub use inputid::*;
|
||||
pub use scancodes::*;
|
||||
pub use sync_stream::*;
|
||||
|
||||
fn ioctl_get_cstring(
|
||||
f: unsafe fn(RawFd, &mut [u8]) -> nix::Result<libc::c_int>,
|
||||
fd: RawFd,
|
||||
) -> Option<CString> {
|
||||
const CAPACITY: usize = 256;
|
||||
let mut buf = vec![0; CAPACITY];
|
||||
match unsafe { f(fd, buf.as_mut_slice()) } {
|
||||
Ok(len) if len as usize > CAPACITY => {
|
||||
panic!("ioctl_get_cstring call overran the provided buffer!");
|
||||
}
|
||||
Ok(len) if len > 0 => {
|
||||
// Our ioctl string functions apparently return the number of bytes written, including
|
||||
// trailing \0.
|
||||
buf.truncate(len as usize);
|
||||
assert_eq!(buf.pop().unwrap(), 0);
|
||||
CString::new(buf).ok()
|
||||
}
|
||||
Ok(_) => {
|
||||
// if len < 0 => Explicit errno
|
||||
None
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
const EVENT_BATCH_SIZE: usize = 32;
|
||||
|
||||
const fn bit_elts<T>(bits: usize) -> usize {
|
||||
let width = mem::size_of::<T>() * 8;
|
||||
bits / width + (bits % width != 0) as usize
|
||||
}
|
||||
// TODO: this is a replacement for BitArr!(for Key::COUNT, in u8), since const generics aren't stable
|
||||
// and the BitView impls for arrays only goes up to 64
|
||||
type KeyArray = [u8; bit_elts::<u8>(Key::COUNT)];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A cached representation of device state at a certain time.
|
||||
pub struct DeviceState {
|
||||
/// The state corresponds to kernel state at this timestamp.
|
||||
timestamp: libc::timeval,
|
||||
/// Set = key pressed
|
||||
key_vals: Option<Box<KeyArray>>,
|
||||
abs_vals: Option<Box<[libc::input_absinfo; AbsoluteAxisType::COUNT]>>,
|
||||
/// Set = switch enabled (closed)
|
||||
switch_vals: Option<BitArr!(for SwitchType::COUNT, in u8)>,
|
||||
/// Set = LED lit
|
||||
led_vals: Option<BitArr!(for LedType::COUNT, in u8)>,
|
||||
}
|
||||
|
||||
impl DeviceState {
|
||||
/// Returns the time when this snapshot was taken.
|
||||
pub fn timestamp(&self) -> SystemTime {
|
||||
timeval_to_systime(&self.timestamp)
|
||||
}
|
||||
|
||||
/// Returns the set of keys pressed when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if keys are not supported by this device.
|
||||
pub fn key_vals(&self) -> Option<AttributeSet<'_, Key>> {
|
||||
self.key_vals
|
||||
.as_deref()
|
||||
.map(|v| AttributeSet::new(BitSlice::from_slice(v).unwrap()))
|
||||
}
|
||||
|
||||
/// Returns the set of absolute axis measurements when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if not supported by this device.
|
||||
pub fn abs_vals(&self) -> Option<&[libc::input_absinfo]> {
|
||||
self.abs_vals.as_deref().map(|v| &v[..])
|
||||
}
|
||||
|
||||
/// Returns the set of switches triggered when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if switches are not supported by this device.
|
||||
pub fn switch_vals(&self) -> Option<AttributeSet<'_, SwitchType>> {
|
||||
self.switch_vals.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
/// Returns the set of LEDs turned on when the snapshot was taken.
|
||||
///
|
||||
/// Returns `None` if LEDs are not supported by this device.
|
||||
pub fn led_vals(&self) -> Option<AttributeSet<'_, LedType>> {
|
||||
self.led_vals.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DeviceState {
|
||||
fn default() -> Self {
|
||||
DeviceState {
|
||||
timestamp: libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
},
|
||||
key_vals: None,
|
||||
abs_vals: None,
|
||||
switch_vals: None,
|
||||
led_vals: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A physical or virtual device supported by evdev.
|
||||
///
|
||||
/// Each device corresponds to a path typically found in `/dev/input`, and supports access via
|
||||
/// one or more "types". For example, an optical mouse has buttons that are represented by "keys",
|
||||
/// and reflects changes in its position via "relative axis" reports.
|
||||
pub struct Device {
|
||||
file: File,
|
||||
ty: BitArr!(for EventType::COUNT, in u8),
|
||||
name: Option<String>,
|
||||
phys: Option<String>,
|
||||
uniq: Option<String>,
|
||||
id: libc::input_id,
|
||||
props: BitArr!(for PropType::COUNT, in u8),
|
||||
driver_version: (u8, u8, u8),
|
||||
supported_keys: Option<Box<KeyArray>>,
|
||||
supported_relative: Option<BitArr!(for RelativeAxisType::COUNT, in u8)>,
|
||||
supported_absolute: Option<BitArr!(for AbsoluteAxisType::COUNT, in u8)>,
|
||||
supported_switch: Option<BitArr!(for SwitchType::COUNT, in u8)>,
|
||||
supported_led: Option<BitArr!(for LedType::COUNT, in u8)>,
|
||||
supported_misc: Option<BitArr!(for MiscType::COUNT, in u8)>,
|
||||
// ff: Option<Box<BitArr!(for _, in u8)>>,
|
||||
// ff_stat: Option<FFStatus>,
|
||||
// rep: Option<Repeat>,
|
||||
supported_snd: Option<BitArr!(for SoundType::COUNT, in u8)>,
|
||||
pending_events: VecDeque<libc::input_event>,
|
||||
read_buf: Vec<libc::input_event>,
|
||||
state: DeviceState,
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
const fn bus_name(x: u16) -> &'static str {
|
||||
match x {
|
||||
0x1 => "PCI",
|
||||
0x2 => "ISA Plug 'n Play",
|
||||
0x3 => "USB",
|
||||
0x4 => "HIL",
|
||||
0x5 => "Bluetooth",
|
||||
0x6 => "Virtual",
|
||||
0x10 => "ISA",
|
||||
0x11 => "i8042",
|
||||
0x12 => "XTKBD",
|
||||
0x13 => "RS232",
|
||||
0x14 => "Gameport",
|
||||
0x15 => "Parallel Port",
|
||||
0x16 => "Amiga",
|
||||
0x17 => "ADB",
|
||||
0x18 => "I2C",
|
||||
0x19 => "Host",
|
||||
0x1A => "GSC",
|
||||
0x1B => "Atari",
|
||||
0x1C => "SPI",
|
||||
_ => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Device {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "{}:", self.name.as_deref().unwrap_or("Unnamed device"))?;
|
||||
writeln!(
|
||||
f,
|
||||
" Driver version: {}.{}.{}",
|
||||
self.driver_version.0, self.driver_version.1, self.driver_version.2
|
||||
)?;
|
||||
if let Some(ref phys) = self.phys {
|
||||
writeln!(f, " Physical address: {:?}", phys)?;
|
||||
}
|
||||
if let Some(ref uniq) = self.uniq {
|
||||
writeln!(f, " Unique name: {:?}", uniq)?;
|
||||
}
|
||||
|
||||
writeln!(f, " Bus: {}", bus_name(self.id.bustype))?;
|
||||
writeln!(f, " Vendor: {:#x}", self.id.vendor)?;
|
||||
writeln!(f, " Product: {:#x}", self.id.product)?;
|
||||
writeln!(f, " Version: {:#x}", self.id.version)?;
|
||||
writeln!(f, " Properties: {:?}", self.properties())?;
|
||||
|
||||
if let (Some(supported_keys), Some(key_vals)) =
|
||||
(self.supported_keys(), self.state.key_vals())
|
||||
{
|
||||
writeln!(f, " Keys supported:")?;
|
||||
for key in supported_keys.iter() {
|
||||
let key_idx = key.code() as usize;
|
||||
writeln!(
|
||||
f,
|
||||
" {:?} ({}index {})",
|
||||
key,
|
||||
if key_vals.contains(key) {
|
||||
"pressed, "
|
||||
} else {
|
||||
""
|
||||
},
|
||||
key_idx
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(supported_relative) = self.supported_relative_axes() {
|
||||
writeln!(f, " Relative Axes: {:?}", supported_relative)?;
|
||||
}
|
||||
|
||||
if let (Some(supported_abs), Some(abs_vals)) =
|
||||
(self.supported_absolute, &self.state.abs_vals)
|
||||
{
|
||||
writeln!(f, " Absolute Axes:")?;
|
||||
for idx in supported_abs.iter_ones() {
|
||||
let abs = AbsoluteAxisType(idx as u16);
|
||||
writeln!(f, " {:?} ({:?}, index {})", abs, abs_vals[idx], idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(supported_misc) = self.misc_properties() {
|
||||
writeln!(f, " Miscellaneous capabilities: {:?}", supported_misc)?;
|
||||
}
|
||||
|
||||
if let (Some(supported_switch), Some(switch_vals)) =
|
||||
(self.supported_switch, &self.state.switch_vals)
|
||||
{
|
||||
writeln!(f, " Switches:")?;
|
||||
for idx in supported_switch.iter_ones() {
|
||||
let sw = SwitchType(idx as u16);
|
||||
writeln!(f, " {:?} ({:?}, index {})", sw, switch_vals[idx], idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(supported_led), Some(led_vals)) = (self.supported_led, &self.state.led_vals) {
|
||||
writeln!(f, " LEDs:")?;
|
||||
for idx in supported_led.iter_ones() {
|
||||
let led = LedType(idx as u16);
|
||||
writeln!(f, " {:?} ({:?}, index {})", led, led_vals[idx], idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(supported_snd) = self.supported_snd {
|
||||
write!(f, " Sounds:")?;
|
||||
for idx in supported_snd.iter_ones() {
|
||||
let snd = SoundType(idx as u16);
|
||||
writeln!(f, " {:?} (index {})", snd, idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
// if let Some(rep) = self.rep {
|
||||
// writeln!(f, " Repeats: {:?}", rep)?;
|
||||
// }
|
||||
|
||||
if self.ty[EventType::FORCEFEEDBACK.0 as usize] {
|
||||
writeln!(f, " Force Feedback supported")?;
|
||||
}
|
||||
|
||||
if self.ty[EventType::POWER.0 as usize] {
|
||||
writeln!(f, " Power supported")?;
|
||||
}
|
||||
|
||||
if self.ty[EventType::FORCEFEEDBACKSTATUS.0 as usize] {
|
||||
writeln!(f, " Force Feedback status supported")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_EVENT_COUNT: usize = 32;
|
||||
|
||||
impl Device {
|
||||
#[inline(always)]
|
||||
/// Opens a device, given its system path.
|
||||
///
|
||||
/// Paths are typically something like `/dev/input/event0`.
|
||||
pub fn open(path: impl AsRef<Path>) -> io::Result<Device> {
|
||||
Self::_open(path.as_ref())
|
||||
}
|
||||
|
||||
/// Fetches and returns events from the kernel ring buffer without doing synchronization on
|
||||
/// SYN_DROPPED.
|
||||
///
|
||||
/// By default this will block until events are available. Typically, users will want to call
|
||||
/// this in a tight loop within a thread.
|
||||
pub fn fetch_events_no_sync(&mut self) -> io::Result<impl Iterator<Item = InputEvent> + '_> {
|
||||
self.fill_events(DEFAULT_EVENT_COUNT)?;
|
||||
Ok(self.pending_events.drain(..).map(InputEvent))
|
||||
}
|
||||
|
||||
/// Fetches and returns events from the kernel ring buffer, doing synchronization on SYN_DROPPED.
|
||||
///
|
||||
/// By default this will block until events are available. Typically, users will want to call
|
||||
/// this in a tight loop within a thread.
|
||||
/// Will insert "fake" events.
|
||||
pub fn fetch_events(&mut self) -> io::Result<impl Iterator<Item = InputEvent> + '_> {
|
||||
self.fill_events(DEFAULT_EVENT_COUNT)?;
|
||||
self.compensate_dropped()?;
|
||||
|
||||
Ok(self.pending_events.drain(..).map(InputEvent))
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
/// Return a `futures::stream` asynchronous stream of `InputEvent` compatible with Tokio.
|
||||
///
|
||||
/// The stream does NOT compensate for SYN_DROPPED events and will not update internal cached
|
||||
/// state.
|
||||
/// The Tokio runtime is expected to keep up with typical event rates.
|
||||
/// This operation consumes the Device.
|
||||
pub fn into_event_stream_no_sync(self) -> io::Result<tokio_stream::EventStream> {
|
||||
tokio_stream::EventStream::new(self)
|
||||
}
|
||||
|
||||
/// Returns the device's name as read from the kernel.
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.name.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the device's physical location, either as set by the caller or as read from the kernel.
|
||||
pub fn physical_path(&self) -> Option<&str> {
|
||||
self.phys.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the user-defined "unique name" of the device, if one has been set.
|
||||
pub fn unique_name(&self) -> Option<&str> {
|
||||
self.uniq.as_deref()
|
||||
}
|
||||
|
||||
/// Returns a struct containing bustype, vendor, product, and version identifiers
|
||||
pub fn input_id(&self) -> libc::input_id {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers)
|
||||
pub fn properties(&self) -> AttributeSet<'_, PropType> {
|
||||
AttributeSet::new(&self.props)
|
||||
}
|
||||
|
||||
/// Returns a tuple of the driver version containing major, minor, rev
|
||||
pub fn driver_version(&self) -> (u8, u8, u8) {
|
||||
self.driver_version
|
||||
}
|
||||
|
||||
/// Returns a set of the event types supported by this device (Key, Switch, etc)
|
||||
///
|
||||
/// If you're interested in the individual keys or switches supported, it's probably easier
|
||||
/// to just call the appropriate `supported_*` function instead.
|
||||
pub fn supported_events(&self) -> AttributeSet<'_, EventType> {
|
||||
AttributeSet::new(&self.ty)
|
||||
}
|
||||
|
||||
/// Returns the set of supported keys reported by the device.
|
||||
///
|
||||
/// For keyboards, this is the set of all possible keycodes the keyboard may emit. Controllers,
|
||||
/// mice, and other peripherals may also report buttons as keys.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, Key};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does this device have an ENTER key?
|
||||
/// let supported = device.supported_keys().map_or(false, |keys| keys.contains(Key::KEY_ENTER));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_keys(&self) -> Option<AttributeSet<'_, Key>> {
|
||||
self.supported_keys
|
||||
.as_deref()
|
||||
.map(|v| AttributeSet::new(BitSlice::from_slice(v).unwrap()))
|
||||
}
|
||||
|
||||
/// Returns the set of supported "relative axes" reported by the device.
|
||||
///
|
||||
/// Standard mice will generally report `REL_X` and `REL_Y` along with wheel if supported.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, RelativeAxisType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device have a scroll wheel?
|
||||
/// let supported = device
|
||||
/// .supported_relative_axes()
|
||||
/// .map_or(false, |axes| axes.contains(RelativeAxisType::REL_WHEEL));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_relative_axes(&self) -> Option<AttributeSet<'_, RelativeAxisType>> {
|
||||
self.supported_relative.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
/// Returns the set of supported "absolute axes" reported by the device.
|
||||
///
|
||||
/// These are most typically supported by joysticks and touchpads.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, AbsoluteAxisType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device have an absolute X axis?
|
||||
/// let supported = device
|
||||
/// .supported_absolute_axes()
|
||||
/// .map_or(false, |axes| axes.contains(AbsoluteAxisType::ABS_X));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_absolute_axes(&self) -> Option<AttributeSet<'_, AbsoluteAxisType>> {
|
||||
self.supported_absolute.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
/// Returns the set of supported switches reported by the device.
|
||||
///
|
||||
/// These are typically used for things like software switches on laptop lids (which the
|
||||
/// system reacts to by suspending or locking), or virtual switches to indicate whether a
|
||||
/// headphone jack is plugged in (used to disable external speakers).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, SwitchType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device report a laptop lid switch?
|
||||
/// let supported = device
|
||||
/// .supported_switches()
|
||||
/// .map_or(false, |axes| axes.contains(SwitchType::SW_LID));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_switches(&self) -> Option<AttributeSet<'_, SwitchType>> {
|
||||
self.supported_switch.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
/// Returns a set of supported LEDs on the device.
|
||||
///
|
||||
/// Most commonly these are state indicator lights for things like Scroll Lock, but they
|
||||
/// can also be found in cameras and other devices.
|
||||
pub fn supported_leds(&self) -> Option<AttributeSet<'_, LedType>> {
|
||||
self.supported_led.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
/// Returns a set of supported "miscellaneous" capabilities.
|
||||
///
|
||||
/// Aside from vendor-specific key scancodes, most of these are uncommon.
|
||||
pub fn misc_properties(&self) -> Option<AttributeSet<'_, MiscType>> {
|
||||
self.supported_misc.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
// pub fn supported_repeats(&self) -> Option<Repeat> {
|
||||
// self.rep
|
||||
// }
|
||||
|
||||
/// Returns the set of supported simple sounds supported by a device.
|
||||
///
|
||||
/// You can use these to make really annoying beep sounds come from an internal self-test
|
||||
/// speaker, for instance.
|
||||
pub fn supported_sounds(&self) -> Option<AttributeSet<'_, SoundType>> {
|
||||
self.supported_snd.as_deref().map(AttributeSet::new)
|
||||
}
|
||||
|
||||
/// Returns the *cached* state of the device.
|
||||
///
|
||||
/// Pulling updates via `fetch_events` or manually invoking `sync_state` will refresh the cache.
|
||||
pub fn state(&self) -> &DeviceState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
fn _open(path: &Path) -> io::Result<Device> {
|
||||
let mut options = OpenOptions::new();
|
||||
|
||||
// Try to load read/write, then fall back to read-only.
|
||||
let file = options
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.or_else(|_| options.write(false).open(path))?;
|
||||
|
||||
let ty = {
|
||||
let mut ty = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_type(file.as_raw_fd(), ty.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
ty
|
||||
};
|
||||
|
||||
let name = ioctl_get_cstring(raw::eviocgname, file.as_raw_fd())
|
||||
.map(|s| s.to_string_lossy().into_owned());
|
||||
let phys = ioctl_get_cstring(raw::eviocgphys, file.as_raw_fd())
|
||||
.map(|s| s.to_string_lossy().into_owned());
|
||||
let uniq = ioctl_get_cstring(raw::eviocguniq, file.as_raw_fd())
|
||||
.map(|s| s.to_string_lossy().into_owned());
|
||||
|
||||
let id = unsafe {
|
||||
let mut id = MaybeUninit::uninit();
|
||||
raw::eviocgid(file.as_raw_fd(), id.as_mut_ptr()).map_err(nix_err)?;
|
||||
id.assume_init()
|
||||
};
|
||||
let mut driver_version: i32 = 0;
|
||||
unsafe {
|
||||
raw::eviocgversion(file.as_raw_fd(), &mut driver_version).map_err(nix_err)?;
|
||||
}
|
||||
let driver_version = (
|
||||
((driver_version >> 16) & 0xff) as u8,
|
||||
((driver_version >> 8) & 0xff) as u8,
|
||||
(driver_version & 0xff) as u8,
|
||||
);
|
||||
|
||||
let props = {
|
||||
let mut props = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgprop(file.as_raw_fd(), props.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
props
|
||||
}; // FIXME: handle old kernel
|
||||
|
||||
let mut state = DeviceState::default();
|
||||
|
||||
let supported_keys = if ty[EventType::KEY.0 as usize] {
|
||||
const KEY_ARR_INIT: KeyArray = [0; bit_elts::<u8>(Key::COUNT)];
|
||||
|
||||
state.key_vals = Some(Box::new(KEY_ARR_INIT));
|
||||
|
||||
let mut supported_keys = Box::new(KEY_ARR_INIT);
|
||||
let key_slice = &mut supported_keys[..];
|
||||
unsafe { raw::eviocgbit_key(file.as_raw_fd(), key_slice).map_err(nix_err)? };
|
||||
|
||||
Some(supported_keys)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_relative = if ty[EventType::RELATIVE.0 as usize] {
|
||||
let mut rel = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_relative(file.as_raw_fd(), rel.as_mut_raw_slice())
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
Some(rel)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_absolute = if ty[EventType::ABSOLUTE.0 as usize] {
|
||||
#[rustfmt::skip]
|
||||
const ABSINFO_ZERO: libc::input_absinfo = libc::input_absinfo {
|
||||
value: 0, minimum: 0, maximum: 0, fuzz: 0, flat: 0, resolution: 0,
|
||||
};
|
||||
const ABS_VALS_INIT: [libc::input_absinfo; AbsoluteAxisType::COUNT] =
|
||||
[ABSINFO_ZERO; AbsoluteAxisType::COUNT];
|
||||
state.abs_vals = Some(Box::new(ABS_VALS_INIT));
|
||||
let mut abs = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_absolute(file.as_raw_fd(), abs.as_mut_raw_slice())
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
Some(abs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_switch = if ty[EventType::SWITCH.0 as usize] {
|
||||
state.switch_vals = Some(BitArray::zeroed());
|
||||
let mut switch = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_switch(file.as_raw_fd(), switch.as_mut_raw_slice())
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
Some(switch)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_led = if ty[EventType::LED.0 as usize] {
|
||||
state.led_vals = Some(BitArray::zeroed());
|
||||
let mut led = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_led(file.as_raw_fd(), led.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(led)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_misc = if ty[EventType::MISC.0 as usize] {
|
||||
let mut misc = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_misc(file.as_raw_fd(), misc.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(misc)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
//unsafe { raw::eviocgbit(file.as_raw_fd(), ffs(FORCEFEEDBACK.bits()), 0x7f, bits_as_u8_slice)?; }
|
||||
|
||||
let supported_snd = if ty[EventType::SOUND.0 as usize] {
|
||||
let mut snd = BitArray::zeroed();
|
||||
unsafe {
|
||||
raw::eviocgbit_sound(file.as_raw_fd(), snd.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(snd)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut dev = Device {
|
||||
file,
|
||||
ty,
|
||||
name,
|
||||
phys,
|
||||
uniq,
|
||||
id,
|
||||
props,
|
||||
driver_version,
|
||||
supported_keys,
|
||||
supported_relative,
|
||||
supported_absolute,
|
||||
supported_switch,
|
||||
supported_led,
|
||||
supported_misc,
|
||||
supported_snd,
|
||||
pending_events: VecDeque::with_capacity(64),
|
||||
read_buf: Vec::new(),
|
||||
state,
|
||||
};
|
||||
|
||||
dev.sync_state()?;
|
||||
|
||||
Ok(dev)
|
||||
}
|
||||
|
||||
/// Synchronize the `Device` state with the kernel device state.
|
||||
///
|
||||
/// If there is an error at any point, the state will not be synchronized completely.
|
||||
pub fn sync_state(&mut self) -> io::Result<()> {
|
||||
let fd = self.as_raw_fd();
|
||||
if let Some(key_vals) = &mut self.state.key_vals {
|
||||
unsafe { raw::eviocgkey(fd, &mut key_vals[..]).map_err(nix_err)? };
|
||||
}
|
||||
|
||||
if let (Some(supported_abs), Some(abs_vals)) =
|
||||
(self.supported_absolute, &mut self.state.abs_vals)
|
||||
{
|
||||
for idx in supported_abs.iter_ones() {
|
||||
// ignore multitouch, we'll handle that later.
|
||||
//
|
||||
// handling later removed. not sure what the intention of "handling that later" was
|
||||
// the abs data seems to be fine (tested ABS_MT_POSITION_X/Y)
|
||||
unsafe { raw::eviocgabs(fd, idx as u32, &mut abs_vals[idx]).map_err(nix_err)? };
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(switch_vals) = &mut self.state.switch_vals {
|
||||
unsafe { raw::eviocgsw(fd, switch_vals.as_mut_raw_slice()).map_err(nix_err)? };
|
||||
}
|
||||
|
||||
if let Some(led_vals) = &mut self.state.led_vals {
|
||||
unsafe { raw::eviocgled(fd, led_vals.as_mut_raw_slice()).map_err(nix_err)? };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Do SYN_DROPPED synchronization, and compensate for missing events by inserting events into
|
||||
/// the stream which, when applied to any state being kept outside of this `Device`, will
|
||||
/// synchronize it with the kernel state.
|
||||
fn compensate_dropped(&mut self) -> io::Result<()> {
|
||||
let mut drop_from = None;
|
||||
for (idx, event) in self.pending_events.iter().enumerate() {
|
||||
if event.type_ == EventType::SYNCHRONIZATION.0 && event.code == Synchronization::SYN_DROPPED {
|
||||
drop_from = Some(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// FIXME: see if we can *not* drop EV_REL events. EV_REL doesn't have any state, so
|
||||
// dropping its events isn't really helping much.
|
||||
if let Some(idx) = drop_from {
|
||||
// look for the nearest SYN_REPORT before the SYN_DROPPED, remove everything after it.
|
||||
let mut prev_report = 0; // (if there's no previous SYN_REPORT, then the entire vector is bogus)
|
||||
for (idx, event) in self.pending_events.iter().take(idx).enumerate().rev() {
|
||||
if event.type_ == EventType::SYNCHRONIZATION.0 && event.code == Synchronization::SYN_REPORT {
|
||||
prev_report = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.pending_events.truncate(prev_report);
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Alright, pending_events is in a sane state. Now, let's sync the local state. We will
|
||||
// create a phony packet that contains deltas from the previous device state to the current
|
||||
// device state.
|
||||
let old_state = self.state.clone();
|
||||
self.sync_state()?;
|
||||
|
||||
let time = systime_to_timeval(&SystemTime::now());
|
||||
|
||||
if let (Some(supported_keys), Some(key_vals)) =
|
||||
(&self.supported_keys, self.state.key_vals())
|
||||
{
|
||||
let supported_keys =
|
||||
AttributeSet::new(BitSlice::from_slice(&supported_keys[..]).unwrap());
|
||||
let old_vals = old_state.key_vals();
|
||||
for key in supported_keys.iter() {
|
||||
if old_vals.map(|v| v.contains(key)) != Some(key_vals.contains(key)) {
|
||||
self.pending_events.push_back(libc::input_event {
|
||||
time,
|
||||
type_: EventType::KEY.0 as _,
|
||||
code: key.code() as u16,
|
||||
value: if key_vals.contains(key) { 1 } else { 0 },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(supported_abs), Some(abs_vals)) =
|
||||
(self.supported_absolute, &self.state.abs_vals)
|
||||
{
|
||||
for idx in supported_abs.iter_ones() {
|
||||
if old_state.abs_vals.as_ref().map(|v| v[idx]) != Some(abs_vals[idx]) {
|
||||
self.pending_events.push_back(libc::input_event {
|
||||
time,
|
||||
type_: EventType::ABSOLUTE.0 as _,
|
||||
code: idx as u16,
|
||||
value: abs_vals[idx].value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(supported_switch), Some(switch_vals)) =
|
||||
(self.supported_switch, &self.state.switch_vals)
|
||||
{
|
||||
for idx in supported_switch.iter_ones() {
|
||||
if old_state.switch_vals.as_ref().map(|v| v[idx]) != Some(switch_vals[idx]) {
|
||||
self.pending_events.push_back(libc::input_event {
|
||||
time,
|
||||
type_: EventType::SWITCH.0 as _,
|
||||
code: idx as u16,
|
||||
value: if switch_vals[idx] { 1 } else { 0 },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(supported_led), Some(led_vals)) = (self.supported_led, &self.state.led_vals) {
|
||||
for idx in supported_led.iter_ones() {
|
||||
if old_state.led_vals.as_ref().map(|v| v[idx]) != Some(led_vals[idx]) {
|
||||
self.pending_events.push_back(libc::input_event {
|
||||
time,
|
||||
type_: EventType::LED.0 as _,
|
||||
code: idx as u16,
|
||||
value: if led_vals[idx] { 1 } else { 0 },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.pending_events.push_back(libc::input_event {
|
||||
time,
|
||||
type_: EventType::SYNCHRONIZATION.0 as _,
|
||||
code: Synchronization::SYN_REPORT,
|
||||
value: 0,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read a maximum of `num` events into the internal buffer. If the underlying fd is not
|
||||
/// O_NONBLOCK, this will block.
|
||||
///
|
||||
/// Returns the number of events that were read, or an error.
|
||||
fn fill_events(&mut self, num: usize) -> io::Result<usize> {
|
||||
let fd = self.as_raw_fd();
|
||||
self.read_buf.clear();
|
||||
self.read_buf.reserve_exact(num);
|
||||
|
||||
// TODO: use Vec::spare_capacity_mut or Vec::split_at_spare_mut when they stabilize
|
||||
let spare_capacity = vec_spare_capacity_mut(&mut self.read_buf);
|
||||
let (_, uninit_buf, _) = unsafe { spare_capacity.align_to_mut::<mem::MaybeUninit<u8>>() };
|
||||
|
||||
// use libc::read instead of nix::unistd::read b/c we need to pass an uninitialized buf
|
||||
let res = unsafe { libc::read(fd, uninit_buf.as_mut_ptr() as _, uninit_buf.len()) };
|
||||
let bytes_read = nix::errno::Errno::result(res).map_err(nix_err)?;
|
||||
let num_read = bytes_read as usize / mem::size_of::<libc::input_event>();
|
||||
unsafe {
|
||||
let len = self.read_buf.len();
|
||||
self.read_buf.set_len(len + num_read);
|
||||
}
|
||||
self.pending_events.extend(self.read_buf.drain(..));
|
||||
Ok(num_read)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
fn pop_event(&mut self) -> Option<InputEvent> {
|
||||
self.pending_events.pop_front().map(InputEvent)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
/// A convenience mapping from an event `(type, code)` to an enumeration.
|
||||
///
|
||||
/// Note that this does not capture an event's value, just the type and code.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum InputEventKind {
|
||||
Synchronization(Synchronization),
|
||||
Key(Key),
|
||||
|
@ -904,7 +97,6 @@ pub enum InputEventKind {
|
|||
Other,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
/// A wrapped `libc::input_event` returned by the input device via the kernel.
|
||||
///
|
||||
/// `input_event` is a struct containing four fields:
|
||||
|
@ -914,23 +106,24 @@ pub enum InputEventKind {
|
|||
/// - `value: s32`
|
||||
///
|
||||
/// The meaning of the "code" and "value" fields will depend on the underlying type of event.
|
||||
#[repr(transparent)]
|
||||
pub struct InputEvent(libc::input_event);
|
||||
|
||||
impl InputEvent {
|
||||
#[inline]
|
||||
/// Returns the timestamp associated with the event.
|
||||
#[inline]
|
||||
pub fn timestamp(&self) -> SystemTime {
|
||||
timeval_to_systime(&self.0.time)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns the type of event this describes, e.g. Key, Switch, etc.
|
||||
#[inline]
|
||||
pub fn event_type(&self) -> EventType {
|
||||
EventType(self.0.type_)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns the raw "code" field directly from input_event.
|
||||
#[inline]
|
||||
pub fn code(&self) -> u16 {
|
||||
self.0.code
|
||||
}
|
||||
|
@ -956,11 +149,11 @@ impl InputEvent {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns the raw "value" field directly from input_event.
|
||||
///
|
||||
/// For keys and switches the values 0 and 1 map to pressed and not pressed respectively.
|
||||
/// For axes, the values depend on the hardware and driver implementation.
|
||||
#[inline]
|
||||
pub fn value(&self) -> i32 {
|
||||
self.0.value
|
||||
}
|
||||
|
@ -972,8 +165,8 @@ impl From<libc::input_event> for InputEvent {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<&'a libc::input_event> for &'a InputEvent {
|
||||
fn into(self) -> &'a libc::input_event {
|
||||
impl AsRef<libc::input_event> for InputEvent {
|
||||
fn as_ref(&self) -> &libc::input_event {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -1043,7 +236,7 @@ fn timeval_to_systime(tv: &libc::timeval) -> SystemTime {
|
|||
}
|
||||
}
|
||||
|
||||
fn nix_err(err: nix::Error) -> io::Error {
|
||||
pub(crate) fn nix_err(err: nix::Error) -> io::Error {
|
||||
match err {
|
||||
nix::Error::Sys(errno) => io::Error::from_raw_os_error(errno as i32),
|
||||
nix::Error::InvalidPath => io::Error::new(io::ErrorKind::InvalidInput, err),
|
||||
|
@ -1053,18 +246,6 @@ fn nix_err(err: nix::Error) -> io::Error {
|
|||
}
|
||||
}
|
||||
|
||||
/// A copy of the unstable Vec::spare_capacity_mut
|
||||
#[inline]
|
||||
fn vec_spare_capacity_mut<T>(v: &mut Vec<T>) -> &mut [mem::MaybeUninit<T>] {
|
||||
let (len, cap) = (v.len(), v.capacity());
|
||||
unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
v.as_mut_ptr().add(len) as *mut mem::MaybeUninit<T>,
|
||||
cap - len,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::mem::MaybeUninit;
|
||||
|
|
515
src/raw_stream.rs
Normal file
515
src/raw_stream.rs
Normal file
|
@ -0,0 +1,515 @@
|
|||
use std::fs::{File, OpenOptions};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::{io, mem};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::{nix_err, sys, AttributeSet, AttributeSetRef, DeviceState, InputEvent, InputId, Key};
|
||||
|
||||
fn ioctl_get_cstring(
|
||||
f: unsafe fn(RawFd, &mut [u8]) -> nix::Result<libc::c_int>,
|
||||
fd: RawFd,
|
||||
) -> Option<Vec<u8>> {
|
||||
let mut buf = vec![0; 256];
|
||||
match unsafe { f(fd, buf.as_mut_slice()) } {
|
||||
Ok(len) if len as usize > buf.capacity() => {
|
||||
panic!("ioctl_get_cstring call overran the provided buffer!");
|
||||
}
|
||||
Ok(len) if len > 1 => {
|
||||
// Our ioctl string functions apparently return the number of bytes written, including
|
||||
// trailing \0.
|
||||
buf.truncate(len as usize);
|
||||
assert_eq!(buf.pop().unwrap(), 0);
|
||||
Some(buf)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_into_string_lossy(v: Vec<u8>) -> String {
|
||||
String::from_utf8(v).unwrap_or_else(|v| String::from_utf8_lossy(v.as_bytes()).into_owned())
|
||||
}
|
||||
|
||||
/// A physical or virtual device supported by evdev.
|
||||
///
|
||||
/// Each device corresponds to a path typically found in `/dev/input`, and supports access via
|
||||
/// one or more "types". For example, an optical mouse has buttons that are represented by "keys",
|
||||
/// and reflects changes in its position via "relative axis" reports.
|
||||
#[derive(Debug)]
|
||||
pub struct RawDevice {
|
||||
file: File,
|
||||
ty: AttributeSet<EventType>,
|
||||
name: Option<String>,
|
||||
phys: Option<String>,
|
||||
uniq: Option<String>,
|
||||
id: libc::input_id,
|
||||
props: AttributeSet<PropType>,
|
||||
driver_version: (u8, u8, u8),
|
||||
supported_keys: Option<AttributeSet<Key>>,
|
||||
supported_relative: Option<AttributeSet<RelativeAxisType>>,
|
||||
supported_absolute: Option<AttributeSet<AbsoluteAxisType>>,
|
||||
supported_switch: Option<AttributeSet<SwitchType>>,
|
||||
supported_led: Option<AttributeSet<LedType>>,
|
||||
supported_misc: Option<AttributeSet<MiscType>>,
|
||||
// ff: Option<AttributeSet<_>>,
|
||||
// ff_stat: Option<FFStatus>,
|
||||
// rep: Option<Repeat>,
|
||||
supported_snd: Option<AttributeSet<SoundType>>,
|
||||
pub(crate) event_buf: Vec<libc::input_event>,
|
||||
}
|
||||
|
||||
impl RawDevice {
|
||||
/// Opens a device, given its system path.
|
||||
///
|
||||
/// Paths are typically something like `/dev/input/event0`.
|
||||
#[inline(always)]
|
||||
pub fn open(path: impl AsRef<Path>) -> io::Result<RawDevice> {
|
||||
Self::_open(path.as_ref())
|
||||
}
|
||||
|
||||
fn _open(path: &Path) -> io::Result<RawDevice> {
|
||||
let mut options = OpenOptions::new();
|
||||
|
||||
// Try to load read/write, then fall back to read-only.
|
||||
let file = options
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.or_else(|_| options.write(false).open(path))?;
|
||||
|
||||
let ty = {
|
||||
let mut ty = AttributeSet::<EventType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_type(file.as_raw_fd(), ty.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
ty
|
||||
};
|
||||
|
||||
let name =
|
||||
ioctl_get_cstring(sys::eviocgname, file.as_raw_fd()).map(bytes_into_string_lossy);
|
||||
let phys =
|
||||
ioctl_get_cstring(sys::eviocgphys, file.as_raw_fd()).map(bytes_into_string_lossy);
|
||||
let uniq =
|
||||
ioctl_get_cstring(sys::eviocguniq, file.as_raw_fd()).map(bytes_into_string_lossy);
|
||||
|
||||
let id = unsafe {
|
||||
let mut id = MaybeUninit::uninit();
|
||||
sys::eviocgid(file.as_raw_fd(), id.as_mut_ptr()).map_err(nix_err)?;
|
||||
id.assume_init()
|
||||
};
|
||||
let mut driver_version: i32 = 0;
|
||||
unsafe {
|
||||
sys::eviocgversion(file.as_raw_fd(), &mut driver_version).map_err(nix_err)?;
|
||||
}
|
||||
let driver_version = (
|
||||
((driver_version >> 16) & 0xff) as u8,
|
||||
((driver_version >> 8) & 0xff) as u8,
|
||||
(driver_version & 0xff) as u8,
|
||||
);
|
||||
|
||||
let props = {
|
||||
let mut props = AttributeSet::<PropType>::new();
|
||||
unsafe {
|
||||
sys::eviocgprop(file.as_raw_fd(), props.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
props
|
||||
}; // FIXME: handle old kernel
|
||||
|
||||
let supported_keys = if ty.contains(EventType::KEY) {
|
||||
let mut keys = AttributeSet::<Key>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_key(file.as_raw_fd(), keys.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(keys)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_relative = if ty.contains(EventType::RELATIVE) {
|
||||
let mut rel = AttributeSet::<RelativeAxisType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_relative(file.as_raw_fd(), rel.as_mut_raw_slice())
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
Some(rel)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_absolute = if ty.contains(EventType::ABSOLUTE) {
|
||||
let mut abs = AttributeSet::<AbsoluteAxisType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_absolute(file.as_raw_fd(), abs.as_mut_raw_slice())
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
Some(abs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_switch = if ty.contains(EventType::SWITCH) {
|
||||
let mut switch = AttributeSet::<SwitchType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_switch(file.as_raw_fd(), switch.as_mut_raw_slice())
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
Some(switch)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_led = if ty.contains(EventType::LED) {
|
||||
let mut led = AttributeSet::<LedType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_led(file.as_raw_fd(), led.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(led)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let supported_misc = if ty.contains(EventType::MISC) {
|
||||
let mut misc = AttributeSet::<MiscType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_misc(file.as_raw_fd(), misc.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(misc)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
//unsafe { sys::eviocgbit(file.as_raw_fd(), ffs(FORCEFEEDBACK.bits()), 0x7f, bits_as_u8_slice)?; }
|
||||
|
||||
let supported_snd = if ty.contains(EventType::SOUND) {
|
||||
let mut snd = AttributeSet::<SoundType>::new();
|
||||
unsafe {
|
||||
sys::eviocgbit_sound(file.as_raw_fd(), snd.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
Some(snd)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(RawDevice {
|
||||
file,
|
||||
ty,
|
||||
name,
|
||||
phys,
|
||||
uniq,
|
||||
id,
|
||||
props,
|
||||
driver_version,
|
||||
supported_keys,
|
||||
supported_relative,
|
||||
supported_absolute,
|
||||
supported_switch,
|
||||
supported_led,
|
||||
supported_misc,
|
||||
supported_snd,
|
||||
event_buf: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the device's name as read from the kernel.
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.name.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the device's physical location, either as set by the caller or as read from the kernel.
|
||||
pub fn physical_path(&self) -> Option<&str> {
|
||||
self.phys.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the user-defined "unique name" of the device, if one has been set.
|
||||
pub fn unique_name(&self) -> Option<&str> {
|
||||
self.uniq.as_deref()
|
||||
}
|
||||
|
||||
/// Returns a struct containing bustype, vendor, product, and version identifiers
|
||||
pub fn input_id(&self) -> InputId {
|
||||
InputId::from(self.id)
|
||||
}
|
||||
|
||||
/// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers)
|
||||
pub fn properties(&self) -> &AttributeSetRef<PropType> {
|
||||
&self.props
|
||||
}
|
||||
|
||||
/// Returns a tuple of the driver version containing major, minor, rev
|
||||
pub fn driver_version(&self) -> (u8, u8, u8) {
|
||||
self.driver_version
|
||||
}
|
||||
|
||||
/// Returns a set of the event types supported by this device (Key, Switch, etc)
|
||||
///
|
||||
/// If you're interested in the individual keys or switches supported, it's probably easier
|
||||
/// to just call the appropriate `supported_*` function instead.
|
||||
pub fn supported_events(&self) -> &AttributeSetRef<EventType> {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
/// Returns the set of supported keys reported by the device.
|
||||
///
|
||||
/// For keyboards, this is the set of all possible keycodes the keyboard may emit. Controllers,
|
||||
/// mice, and other peripherals may also report buttons as keys.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, Key};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does this device have an ENTER key?
|
||||
/// let supported = device.supported_keys().map_or(false, |keys| keys.contains(Key::KEY_ENTER));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_keys(&self) -> Option<&AttributeSetRef<Key>> {
|
||||
self.supported_keys.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the set of supported "relative axes" reported by the device.
|
||||
///
|
||||
/// Standard mice will generally report `REL_X` and `REL_Y` along with wheel if supported.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, RelativeAxisType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device have a scroll wheel?
|
||||
/// let supported = device
|
||||
/// .supported_relative_axes()
|
||||
/// .map_or(false, |axes| axes.contains(RelativeAxisType::REL_WHEEL));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_relative_axes(&self) -> Option<&AttributeSetRef<RelativeAxisType>> {
|
||||
self.supported_relative.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the set of supported "absolute axes" reported by the device.
|
||||
///
|
||||
/// These are most typically supported by joysticks and touchpads.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, AbsoluteAxisType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device have an absolute X axis?
|
||||
/// let supported = device
|
||||
/// .supported_absolute_axes()
|
||||
/// .map_or(false, |axes| axes.contains(AbsoluteAxisType::ABS_X));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_absolute_axes(&self) -> Option<&AttributeSetRef<AbsoluteAxisType>> {
|
||||
self.supported_absolute.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the set of supported switches reported by the device.
|
||||
///
|
||||
/// These are typically used for things like software switches on laptop lids (which the
|
||||
/// system reacts to by suspending or locking), or virtual switches to indicate whether a
|
||||
/// headphone jack is plugged in (used to disable external speakers).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, SwitchType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device report a laptop lid switch?
|
||||
/// let supported = device
|
||||
/// .supported_switches()
|
||||
/// .map_or(false, |axes| axes.contains(SwitchType::SW_LID));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_switches(&self) -> Option<&AttributeSetRef<SwitchType>> {
|
||||
self.supported_switch.as_deref()
|
||||
}
|
||||
|
||||
/// Returns a set of supported LEDs on the device.
|
||||
///
|
||||
/// Most commonly these are state indicator lights for things like Scroll Lock, but they
|
||||
/// can also be found in cameras and other devices.
|
||||
pub fn supported_leds(&self) -> Option<&AttributeSetRef<LedType>> {
|
||||
self.supported_led.as_deref()
|
||||
}
|
||||
|
||||
/// Returns a set of supported "miscellaneous" capabilities.
|
||||
///
|
||||
/// Aside from vendor-specific key scancodes, most of these are uncommon.
|
||||
pub fn misc_properties(&self) -> Option<&AttributeSetRef<MiscType>> {
|
||||
self.supported_misc.as_deref()
|
||||
}
|
||||
|
||||
// pub fn supported_repeats(&self) -> Option<Repeat> {
|
||||
// self.rep
|
||||
// }
|
||||
|
||||
/// Returns the set of supported simple sounds supported by a device.
|
||||
///
|
||||
/// You can use these to make really annoying beep sounds come from an internal self-test
|
||||
/// speaker, for instance.
|
||||
pub fn supported_sounds(&self) -> Option<&AttributeSetRef<SoundType>> {
|
||||
self.supported_snd.as_deref()
|
||||
}
|
||||
|
||||
/// Read a maximum of `num` events into the internal buffer. If the underlying fd is not
|
||||
/// O_NONBLOCK, this will block.
|
||||
///
|
||||
/// Returns the number of events that were read, or an error.
|
||||
pub(crate) fn fill_events(&mut self) -> io::Result<usize> {
|
||||
let fd = self.as_raw_fd();
|
||||
self.event_buf.reserve(crate::EVENT_BATCH_SIZE);
|
||||
|
||||
// TODO: use Vec::spare_capacity_mut or Vec::split_at_spare_mut when they stabilize
|
||||
let spare_capacity = vec_spare_capacity_mut(&mut self.event_buf);
|
||||
let (_, uninit_buf, _) = unsafe { spare_capacity.align_to_mut::<mem::MaybeUninit<u8>>() };
|
||||
|
||||
// use libc::read instead of nix::unistd::read b/c we need to pass an uninitialized buf
|
||||
let res = unsafe { libc::read(fd, uninit_buf.as_mut_ptr() as _, uninit_buf.len()) };
|
||||
let bytes_read = nix::errno::Errno::result(res).map_err(nix_err)?;
|
||||
let num_read = bytes_read as usize / mem::size_of::<libc::input_event>();
|
||||
unsafe {
|
||||
let len = self.event_buf.len();
|
||||
self.event_buf.set_len(len + num_read);
|
||||
}
|
||||
Ok(num_read)
|
||||
}
|
||||
|
||||
/// Fetches and returns events from the kernel ring buffer without doing synchronization on
|
||||
/// SYN_DROPPED.
|
||||
///
|
||||
/// By default this will block until events are available. Typically, users will want to call
|
||||
/// this in a tight loop within a thread.
|
||||
pub fn fetch_events(&mut self) -> io::Result<impl Iterator<Item = InputEvent> + '_> {
|
||||
self.fill_events()?;
|
||||
Ok(self.event_buf.drain(..).map(InputEvent))
|
||||
}
|
||||
|
||||
/// Create an empty `DeviceState`. The `{abs,key,etc}_vals` for the returned state will return
|
||||
/// `Some` if `self.supported_events()` contains that `EventType`.
|
||||
pub fn empty_state(&self) -> DeviceState {
|
||||
let supports = self.supported_events();
|
||||
|
||||
let key_vals = if supports.contains(EventType::KEY) {
|
||||
Some(AttributeSet::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let abs_vals = if supports.contains(EventType::ABSOLUTE) {
|
||||
#[rustfmt::skip]
|
||||
const ABSINFO_ZERO: libc::input_absinfo = libc::input_absinfo {
|
||||
value: 0, minimum: 0, maximum: 0, fuzz: 0, flat: 0, resolution: 0,
|
||||
};
|
||||
const ABS_VALS_INIT: [libc::input_absinfo; AbsoluteAxisType::COUNT] =
|
||||
[ABSINFO_ZERO; AbsoluteAxisType::COUNT];
|
||||
Some(Box::new(ABS_VALS_INIT))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let switch_vals = if supports.contains(EventType::SWITCH) {
|
||||
Some(AttributeSet::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let led_vals = if supports.contains(EventType::LED) {
|
||||
Some(AttributeSet::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
DeviceState {
|
||||
timestamp: libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
},
|
||||
key_vals,
|
||||
abs_vals,
|
||||
switch_vals,
|
||||
led_vals,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_state(&self, state: &mut DeviceState) -> io::Result<()> {
|
||||
self.sync_key_state(state)?;
|
||||
self.sync_abs_state(state)?;
|
||||
self.sync_switch_state(state)?;
|
||||
self.sync_led_state(state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sync_key_state(&self, state: &mut DeviceState) -> io::Result<()> {
|
||||
if let Some(key_vals) = &mut state.key_vals {
|
||||
unsafe {
|
||||
sys::eviocgkey(self.as_raw_fd(), key_vals.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sync_abs_state(&self, state: &mut DeviceState) -> io::Result<()> {
|
||||
if let (Some(supported_abs), Some(abs_vals)) =
|
||||
(self.supported_absolute_axes(), &mut state.abs_vals)
|
||||
{
|
||||
for AbsoluteAxisType(idx) in supported_abs.iter() {
|
||||
// ignore multitouch, we'll handle that later.
|
||||
//
|
||||
// handling later removed. not sure what the intention of "handling that later" was
|
||||
// the abs data seems to be fine (tested ABS_MT_POSITION_X/Y)
|
||||
unsafe {
|
||||
sys::eviocgabs(self.as_raw_fd(), idx as u32, &mut abs_vals[idx as usize])
|
||||
.map_err(nix_err)?
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sync_switch_state(&self, state: &mut DeviceState) -> io::Result<()> {
|
||||
if let Some(switch_vals) = &mut state.switch_vals {
|
||||
unsafe {
|
||||
sys::eviocgsw(self.as_raw_fd(), switch_vals.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sync_led_state(&self, state: &mut DeviceState) -> io::Result<()> {
|
||||
if let Some(led_vals) = &mut state.led_vals {
|
||||
unsafe {
|
||||
sys::eviocgled(self.as_raw_fd(), led_vals.as_mut_raw_slice()).map_err(nix_err)?
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RawDevice {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/// A copy of the unstable Vec::spare_capacity_mut
|
||||
#[inline]
|
||||
fn vec_spare_capacity_mut<T>(v: &mut Vec<T>) -> &mut [mem::MaybeUninit<T>] {
|
||||
let (len, cap) = (v.len(), v.capacity());
|
||||
unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
v.as_mut_ptr().add(len) as *mut mem::MaybeUninit<T>,
|
||||
cap - len,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
/// Each associated constant for this struct represents a distinct key.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Key(u16);
|
||||
pub struct Key(pub u16);
|
||||
|
||||
impl Key {
|
||||
#[inline]
|
||||
|
@ -19,8 +19,22 @@ impl Key {
|
|||
pub(crate) const COUNT: usize = 0x300;
|
||||
}
|
||||
|
||||
const fn bit_elts<T>(bits: usize) -> usize {
|
||||
let width = std::mem::size_of::<T>() * 8;
|
||||
bits / width + (bits % width != 0) as usize
|
||||
}
|
||||
// TODO: replace with BitArr!() once const generics is stable and BitView is implemented for any [T; N]
|
||||
const KEY_ARRAY_LEN: usize = bit_elts::<u8>(Key::COUNT);
|
||||
type KeyArray = [u8; KEY_ARRAY_LEN];
|
||||
const KEY_ARRAY_INIT: KeyArray = [0; KEY_ARRAY_LEN];
|
||||
|
||||
evdev_enum!(
|
||||
Key,
|
||||
Array: Box<[u8; KEY_ARRAY_LEN]>,
|
||||
|x| bitvec::slice::BitSlice::from_slice(&x[..]).unwrap(),
|
||||
|x| bitvec::slice::BitSlice::from_slice_mut(&mut x[..]).unwrap(),
|
||||
|x| &mut x[..],
|
||||
|| Box::new(KEY_ARRAY_INIT),
|
||||
KEY_RESERVED = 0,
|
||||
KEY_ESC = 1,
|
||||
KEY_1 = 2,
|
||||
|
|
752
src/sync_stream.rs
Normal file
752
src/sync_stream.rs
Normal file
|
@ -0,0 +1,752 @@
|
|||
use crate::constants::*;
|
||||
use crate::raw_stream::RawDevice;
|
||||
use crate::{AttributeSetRef, DeviceState, InputEvent, InputEventKind, InputId, Key};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// A physical or virtual device supported by evdev.
|
||||
///
|
||||
/// Each device corresponds to a path typically found in `/dev/input`, and supports access via
|
||||
/// one or more "types". For example, an optical mouse has buttons that are represented by "keys",
|
||||
/// and reflects changes in its position via "relative axis" reports.
|
||||
///
|
||||
/// This type specifically is a wrapper over [`RawDevice`],that synchronizes with the kernel's
|
||||
/// state when events are dropped.
|
||||
///
|
||||
/// If `fetch_events()` isn't called often enough and the kernel drops events from its internal
|
||||
/// buffer, synthetic events will be injected into the iterator returned by `fetch_events()` and
|
||||
/// [`Device::state()`] will be kept up to date when `fetch_events()` is called.
|
||||
pub struct Device {
|
||||
raw: RawDevice,
|
||||
prev_state: DeviceState,
|
||||
state: DeviceState,
|
||||
block_dropped: bool,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Opens a device, given its system path.
|
||||
///
|
||||
/// Paths are typically something like `/dev/input/event0`.
|
||||
#[inline(always)]
|
||||
pub fn open(path: impl AsRef<Path>) -> io::Result<Device> {
|
||||
Self::_open(path.as_ref())
|
||||
}
|
||||
|
||||
fn _open(path: &Path) -> io::Result<Device> {
|
||||
let raw = RawDevice::open(path)?;
|
||||
|
||||
let state = raw.empty_state();
|
||||
let prev_state = state.clone();
|
||||
|
||||
Ok(Device {
|
||||
raw,
|
||||
prev_state,
|
||||
state,
|
||||
block_dropped: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn state(&self) -> &DeviceState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
/// Returns the device's name as read from the kernel.
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.raw.name()
|
||||
}
|
||||
|
||||
/// Returns the device's physical location, either as set by the caller or as read from the kernel.
|
||||
pub fn physical_path(&self) -> Option<&str> {
|
||||
self.raw.physical_path()
|
||||
}
|
||||
|
||||
/// Returns the user-defined "unique name" of the device, if one has been set.
|
||||
pub fn unique_name(&self) -> Option<&str> {
|
||||
self.raw.unique_name()
|
||||
}
|
||||
|
||||
/// Returns a struct containing bustype, vendor, product, and version identifiers
|
||||
pub fn input_id(&self) -> InputId {
|
||||
self.raw.input_id()
|
||||
}
|
||||
|
||||
/// Returns the set of supported "properties" for the device (see `INPUT_PROP_*` in kernel headers)
|
||||
pub fn properties(&self) -> &AttributeSetRef<PropType> {
|
||||
self.raw.properties()
|
||||
}
|
||||
|
||||
/// Returns a tuple of the driver version containing major, minor, rev
|
||||
pub fn driver_version(&self) -> (u8, u8, u8) {
|
||||
self.raw.driver_version()
|
||||
}
|
||||
|
||||
/// Returns a set of the event types supported by this device (Key, Switch, etc)
|
||||
///
|
||||
/// If you're interested in the individual keys or switches supported, it's probably easier
|
||||
/// to just call the appropriate `supported_*` function instead.
|
||||
pub fn supported_events(&self) -> &AttributeSetRef<EventType> {
|
||||
self.raw.supported_events()
|
||||
}
|
||||
|
||||
/// Returns the set of supported keys reported by the device.
|
||||
///
|
||||
/// For keyboards, this is the set of all possible keycodes the keyboard may emit. Controllers,
|
||||
/// mice, and other peripherals may also report buttons as keys.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, Key};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does this device have an ENTER key?
|
||||
/// let supported = device.supported_keys().map_or(false, |keys| keys.contains(Key::KEY_ENTER));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_keys(&self) -> Option<&AttributeSetRef<Key>> {
|
||||
self.raw.supported_keys()
|
||||
}
|
||||
|
||||
/// Returns the set of supported "relative axes" reported by the device.
|
||||
///
|
||||
/// Standard mice will generally report `REL_X` and `REL_Y` along with wheel if supported.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, RelativeAxisType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device have a scroll wheel?
|
||||
/// let supported = device
|
||||
/// .supported_relative_axes()
|
||||
/// .map_or(false, |axes| axes.contains(RelativeAxisType::REL_WHEEL));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_relative_axes(&self) -> Option<&AttributeSetRef<RelativeAxisType>> {
|
||||
self.raw.supported_relative_axes()
|
||||
}
|
||||
|
||||
/// Returns the set of supported "absolute axes" reported by the device.
|
||||
///
|
||||
/// These are most typically supported by joysticks and touchpads.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, AbsoluteAxisType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device have an absolute X axis?
|
||||
/// let supported = device
|
||||
/// .supported_absolute_axes()
|
||||
/// .map_or(false, |axes| axes.contains(AbsoluteAxisType::ABS_X));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_absolute_axes(&self) -> Option<&AttributeSetRef<AbsoluteAxisType>> {
|
||||
self.raw.supported_absolute_axes()
|
||||
}
|
||||
|
||||
/// Returns the set of supported switches reported by the device.
|
||||
///
|
||||
/// These are typically used for things like software switches on laptop lids (which the
|
||||
/// system reacts to by suspending or locking), or virtual switches to indicate whether a
|
||||
/// headphone jack is plugged in (used to disable external speakers).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use evdev::{Device, SwitchType};
|
||||
/// let device = Device::open("/dev/input/event0")?;
|
||||
///
|
||||
/// // Does the device report a laptop lid switch?
|
||||
/// let supported = device
|
||||
/// .supported_switches()
|
||||
/// .map_or(false, |axes| axes.contains(SwitchType::SW_LID));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn supported_switches(&self) -> Option<&AttributeSetRef<SwitchType>> {
|
||||
self.raw.supported_switches()
|
||||
}
|
||||
|
||||
/// Returns a set of supported LEDs on the device.
|
||||
///
|
||||
/// Most commonly these are state indicator lights for things like Scroll Lock, but they
|
||||
/// can also be found in cameras and other devices.
|
||||
pub fn supported_leds(&self) -> Option<&AttributeSetRef<LedType>> {
|
||||
self.raw.supported_leds()
|
||||
}
|
||||
|
||||
/// Returns a set of supported "miscellaneous" capabilities.
|
||||
///
|
||||
/// Aside from vendor-specific key scancodes, most of these are uncommon.
|
||||
pub fn misc_properties(&self) -> Option<&AttributeSetRef<MiscType>> {
|
||||
self.raw.misc_properties()
|
||||
}
|
||||
|
||||
// pub fn supported_repeats(&self) -> Option<Repeat> {
|
||||
// self.rep
|
||||
// }
|
||||
|
||||
/// Returns the set of supported simple sounds supported by a device.
|
||||
///
|
||||
/// You can use these to make really annoying beep sounds come from an internal self-test
|
||||
/// speaker, for instance.
|
||||
pub fn supported_sounds(&self) -> Option<&AttributeSetRef<SoundType>> {
|
||||
self.raw.supported_sounds()
|
||||
}
|
||||
|
||||
/// Fetches and returns events from the kernel ring buffer, doing synchronization on SYN_DROPPED.
|
||||
///
|
||||
/// By default this will block until events are available. Typically, users will want to call
|
||||
/// this in a tight loop within a thread.
|
||||
/// Will insert "fake" events.
|
||||
pub fn fetch_events(&mut self) -> io::Result<FetchEventsSynced<'_>> {
|
||||
let block_dropped = std::mem::take(&mut self.block_dropped);
|
||||
let sync = if block_dropped {
|
||||
self.prev_state.clone_from(&self.state);
|
||||
self.raw.sync_state(&mut self.state)?;
|
||||
Some(SyncState::Keys {
|
||||
time: crate::systime_to_timeval(&std::time::SystemTime::now()),
|
||||
start: Key::new(0),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.raw.fill_events()?;
|
||||
|
||||
Ok(FetchEventsSynced {
|
||||
dev: self,
|
||||
range: 0..0,
|
||||
consumed_to: 0,
|
||||
sync,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub fn into_event_stream(self) -> io::Result<EventStream> {
|
||||
EventStream::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.raw.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over events of a [`Device`], produced by [`Device::fetch_events`].
|
||||
pub struct FetchEventsSynced<'a> {
|
||||
dev: &'a mut Device,
|
||||
/// The current block of the events we're returning to the consumer. If empty
|
||||
/// (i.e. for any x, range == x..x) then we'll find another block on the next `next()` call.
|
||||
range: std::ops::Range<usize>,
|
||||
/// The index into dev.raw.event_buf up to which we'll delete events when dropped.
|
||||
consumed_to: usize,
|
||||
/// Our current synchronization state, i.e. whether we're currently diffing key_vals,
|
||||
/// abs_vals, switch_vals, led_vals, or none of them.
|
||||
sync: Option<SyncState>,
|
||||
}
|
||||
|
||||
enum SyncState {
|
||||
Keys {
|
||||
time: libc::timeval,
|
||||
start: Key,
|
||||
},
|
||||
Absolutes {
|
||||
time: libc::timeval,
|
||||
start: AbsoluteAxisType,
|
||||
},
|
||||
Switches {
|
||||
time: libc::timeval,
|
||||
start: SwitchType,
|
||||
},
|
||||
Leds {
|
||||
time: libc::timeval,
|
||||
start: LedType,
|
||||
},
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compensate_events(state: &mut Option<SyncState>, dev: &mut Device) -> Option<InputEvent> {
|
||||
let sync = state.as_mut()?;
|
||||
// this macro checks if there are any differences between the old state and the new for the
|
||||
// specific substate(?) that we're checking and if so returns an input_event with the value set
|
||||
// to the value from the up-to-date state
|
||||
macro_rules! try_compensate {
|
||||
($time:expr, $start:ident : $typ:ident, $evtype:ident, $sync:ident, $supporteds:ident, $state:ty, $get_state:expr, $get_value:expr) => {
|
||||
if let Some(supported_types) = dev.$supporteds() {
|
||||
let types_to_check = supported_types.slice(*$start);
|
||||
let get_state: fn(&DeviceState) -> $state = $get_state;
|
||||
let vals = get_state(&dev.state);
|
||||
let old_vals = get_state(&dev.prev_state);
|
||||
let get_value: fn($state, $typ) -> _ = $get_value;
|
||||
for typ in types_to_check.iter() {
|
||||
let prev = get_value(old_vals, typ);
|
||||
let value = get_value(vals, typ);
|
||||
if prev != value {
|
||||
$start.0 = typ.0 + 1;
|
||||
let ev = InputEvent(libc::input_event {
|
||||
time: *$time,
|
||||
type_: EventType::$evtype.0,
|
||||
code: typ.0,
|
||||
value: value as _,
|
||||
});
|
||||
return Some(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
loop {
|
||||
// check keys, then abs axes, then switches, then leds
|
||||
match sync {
|
||||
SyncState::Keys { time, start } => {
|
||||
try_compensate!(
|
||||
time,
|
||||
start: Key,
|
||||
KEY,
|
||||
Keys,
|
||||
supported_keys,
|
||||
&AttributeSetRef<Key>,
|
||||
|st| st.key_vals().unwrap(),
|
||||
|vals, key| vals.contains(key)
|
||||
);
|
||||
*sync = SyncState::Absolutes {
|
||||
time: *time,
|
||||
start: AbsoluteAxisType(0),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
SyncState::Absolutes { time, start } => {
|
||||
try_compensate!(
|
||||
time,
|
||||
start: AbsoluteAxisType,
|
||||
ABSOLUTE,
|
||||
Absolutes,
|
||||
supported_absolute_axes,
|
||||
&[libc::input_absinfo],
|
||||
|st| st.abs_vals().unwrap(),
|
||||
|vals, abs| vals[abs.0 as usize].value
|
||||
);
|
||||
*sync = SyncState::Switches {
|
||||
time: *time,
|
||||
start: SwitchType(0),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
SyncState::Switches { time, start } => {
|
||||
try_compensate!(
|
||||
time,
|
||||
start: SwitchType,
|
||||
SWITCH,
|
||||
Switches,
|
||||
supported_switches,
|
||||
&AttributeSetRef<SwitchType>,
|
||||
|st| st.switch_vals().unwrap(),
|
||||
|vals, sw| vals.contains(sw)
|
||||
);
|
||||
*sync = SyncState::Leds {
|
||||
time: *time,
|
||||
start: LedType(0),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
SyncState::Leds { time, start } => {
|
||||
try_compensate!(
|
||||
time,
|
||||
start: LedType,
|
||||
LED,
|
||||
Leds,
|
||||
supported_leds,
|
||||
&AttributeSetRef<LedType>,
|
||||
|st| st.led_vals().unwrap(),
|
||||
|vals, led| vals.contains(led)
|
||||
);
|
||||
let ev = InputEvent(libc::input_event {
|
||||
time: *time,
|
||||
type_: EventType::SYNCHRONIZATION.0,
|
||||
code: Synchronization::SYN_REPORT.0,
|
||||
value: 0,
|
||||
});
|
||||
*state = None;
|
||||
return Some(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FetchEventsSynced<'a> {
|
||||
type Item = InputEvent;
|
||||
fn next(&mut self) -> Option<InputEvent> {
|
||||
// first: check if we need to emit compensatory events due to a SYN_DROPPED we found in the
|
||||
// last batch of blocks
|
||||
if let Some(ev) = compensate_events(&mut self.sync, &mut self.dev) {
|
||||
return Some(ev);
|
||||
}
|
||||
let state = &mut self.dev.state;
|
||||
let (res, consumed_to) = sync_events(&mut self.range, &self.dev.raw.event_buf, |ev| {
|
||||
state.process_event(ev)
|
||||
});
|
||||
if let Some(end) = consumed_to {
|
||||
self.consumed_to = end
|
||||
}
|
||||
match res {
|
||||
Ok(ev) => Some(InputEvent(ev)),
|
||||
Err(requires_sync) => {
|
||||
if requires_sync {
|
||||
self.dev.block_dropped = true;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for FetchEventsSynced<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.dev.raw.event_buf.drain(..self.consumed_to);
|
||||
}
|
||||
}
|
||||
|
||||
/// Err(true) means the device should sync the state with ioctl
|
||||
#[inline]
|
||||
fn sync_events(
|
||||
range: &mut std::ops::Range<usize>,
|
||||
event_buf: &[libc::input_event],
|
||||
mut handle_event: impl FnMut(InputEvent),
|
||||
) -> (Result<libc::input_event, bool>, Option<usize>) {
|
||||
let mut consumed_to = None;
|
||||
let res = 'outer: loop {
|
||||
if let Some(idx) = range.next() {
|
||||
// we're going through and emitting the events of a block that we checked
|
||||
break Ok(event_buf[idx]);
|
||||
}
|
||||
// find the range of this new block: look for a SYN_REPORT
|
||||
let block_start = range.end;
|
||||
let mut block_dropped = false;
|
||||
for (i, ev) in event_buf.iter().enumerate().skip(block_start) {
|
||||
let ev = InputEvent(*ev);
|
||||
match ev.kind() {
|
||||
InputEventKind::Synchronization(Synchronization::SYN_DROPPED) => {
|
||||
block_dropped = true;
|
||||
}
|
||||
InputEventKind::Synchronization(Synchronization::SYN_REPORT) => {
|
||||
consumed_to = Some(i + 1);
|
||||
if block_dropped {
|
||||
*range = event_buf.len()..event_buf.len();
|
||||
break 'outer Err(true);
|
||||
} else {
|
||||
*range = block_start..i + 1;
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
_ => handle_event(ev),
|
||||
}
|
||||
}
|
||||
break Err(false);
|
||||
};
|
||||
(res, consumed_to)
|
||||
}
|
||||
|
||||
impl fmt::Display for Device {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "{}:", self.name().unwrap_or("Unnamed device"))?;
|
||||
let (maj, min, pat) = self.driver_version();
|
||||
writeln!(f, " Driver version: {}.{}.{}", maj, min, pat)?;
|
||||
if let Some(ref phys) = self.physical_path() {
|
||||
writeln!(f, " Physical address: {:?}", phys)?;
|
||||
}
|
||||
if let Some(ref uniq) = self.unique_name() {
|
||||
writeln!(f, " Unique name: {:?}", uniq)?;
|
||||
}
|
||||
|
||||
let id = self.input_id();
|
||||
|
||||
writeln!(f, " Bus: {}", id.bus_type())?;
|
||||
writeln!(f, " Vendor: {:#x}", id.vendor())?;
|
||||
writeln!(f, " Product: {:#x}", id.product())?;
|
||||
writeln!(f, " Version: {:#x}", id.version())?;
|
||||
writeln!(f, " Properties: {:?}", self.properties())?;
|
||||
|
||||
if let (Some(supported_keys), Some(key_vals)) =
|
||||
(self.supported_keys(), self.state.key_vals())
|
||||
{
|
||||
writeln!(f, " Keys supported:")?;
|
||||
for key in supported_keys.iter() {
|
||||
let key_idx = key.code() as usize;
|
||||
writeln!(
|
||||
f,
|
||||
" {:?} ({}index {})",
|
||||
key,
|
||||
if key_vals.contains(key) {
|
||||
"pressed, "
|
||||
} else {
|
||||
""
|
||||
},
|
||||
key_idx
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(supported_relative) = self.supported_relative_axes() {
|
||||
writeln!(f, " Relative Axes: {:?}", supported_relative)?;
|
||||
}
|
||||
|
||||
if let (Some(supported_abs), Some(abs_vals)) =
|
||||
(self.supported_absolute_axes(), &self.state.abs_vals)
|
||||
{
|
||||
writeln!(f, " Absolute Axes:")?;
|
||||
for abs in supported_abs.iter() {
|
||||
writeln!(
|
||||
f,
|
||||
" {:?} ({:?}, index {})",
|
||||
abs, abs_vals[abs.0 as usize], abs.0
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(supported_misc) = self.misc_properties() {
|
||||
writeln!(f, " Miscellaneous capabilities: {:?}", supported_misc)?;
|
||||
}
|
||||
|
||||
if let (Some(supported_switch), Some(switch_vals)) =
|
||||
(self.supported_switches(), self.state.switch_vals())
|
||||
{
|
||||
writeln!(f, " Switches:")?;
|
||||
for sw in supported_switch.iter() {
|
||||
writeln!(
|
||||
f,
|
||||
" {:?} ({:?}, index {})",
|
||||
sw,
|
||||
switch_vals.contains(sw),
|
||||
sw.0
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(supported_led), Some(led_vals)) =
|
||||
(self.supported_leds(), self.state.led_vals())
|
||||
{
|
||||
writeln!(f, " LEDs:")?;
|
||||
for led in supported_led.iter() {
|
||||
writeln!(
|
||||
f,
|
||||
" {:?} ({:?}, index {})",
|
||||
led,
|
||||
led_vals.contains(led),
|
||||
led.0
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(supported_snd) = self.supported_sounds() {
|
||||
write!(f, " Sounds:")?;
|
||||
for snd in supported_snd.iter() {
|
||||
writeln!(f, " {:?} (index {})", snd, snd.0)?;
|
||||
}
|
||||
}
|
||||
|
||||
// if let Some(rep) = self.rep {
|
||||
// writeln!(f, " Repeats: {:?}", rep)?;
|
||||
// }
|
||||
|
||||
let evs = self.supported_events();
|
||||
|
||||
if evs.contains(EventType::FORCEFEEDBACK) {
|
||||
writeln!(f, " Force Feedback supported")?;
|
||||
}
|
||||
|
||||
if evs.contains(EventType::POWER) {
|
||||
writeln!(f, " Power supported")?;
|
||||
}
|
||||
|
||||
if evs.contains(EventType::FORCEFEEDBACKSTATUS) {
|
||||
writeln!(f, " Force Feedback status supported")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
mod tokio_stream {
|
||||
use super::*;
|
||||
|
||||
use tokio_1 as tokio;
|
||||
|
||||
use crate::nix_err;
|
||||
use futures_core::{ready, Stream};
|
||||
use std::collections::VecDeque;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::unix::AsyncFd;
|
||||
|
||||
/// An asynchronous stream of input events.
|
||||
///
|
||||
/// This can be used by calling [`stream.next_event().await?`](Self::next_event), or if you
|
||||
/// need to pass it as a stream somewhere, the [`futures::Stream`](Stream) implementation.
|
||||
/// There's also a lower-level [`poll_event`] function if you need to fetch an event from
|
||||
/// inside a `Future::poll` impl.
|
||||
pub struct EventStream {
|
||||
device: AsyncFd<Device>,
|
||||
events: VecDeque<InputEvent>,
|
||||
}
|
||||
impl Unpin for EventStream {}
|
||||
|
||||
impl EventStream {
|
||||
pub(crate) fn new(device: Device) -> io::Result<Self> {
|
||||
use nix::fcntl;
|
||||
fcntl::fcntl(device.as_raw_fd(), fcntl::F_SETFL(fcntl::OFlag::O_NONBLOCK))
|
||||
.map_err(nix_err)?;
|
||||
let device = AsyncFd::new(device)?;
|
||||
Ok(Self {
|
||||
device,
|
||||
events: VecDeque::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying device
|
||||
pub fn device(&self) -> &Device {
|
||||
self.device.get_ref()
|
||||
}
|
||||
|
||||
/// Try to wait for the next event in this stream. Any errors are likely to be fatal, i.e.
|
||||
/// any calls afterwards will likely error as well.
|
||||
pub async fn next_event(&mut self) -> io::Result<InputEvent> {
|
||||
poll_fn(|cx| self.poll_event(cx)).await
|
||||
}
|
||||
|
||||
/// A lower-level function for directly polling this stream.
|
||||
pub fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<InputEvent>> {
|
||||
let Self { device, events } = self;
|
||||
|
||||
if let Some(ev) = events.pop_front() {
|
||||
return Poll::Ready(Ok(ev));
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut guard = ready!(device.poll_read_ready_mut(cx))?;
|
||||
|
||||
let res = guard.try_io(|device| {
|
||||
events.extend(device.get_mut().fetch_events()?);
|
||||
Ok(())
|
||||
});
|
||||
match res {
|
||||
Ok(res) => {
|
||||
let () = res?;
|
||||
let ret = match events.pop_front() {
|
||||
Some(ev) => Poll::Ready(Ok(ev)),
|
||||
None => Poll::Pending,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
Err(_would_block) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for EventStream {
|
||||
type Item = io::Result<InputEvent>;
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
self.get_mut().poll_event(cx).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
// version of futures_util::future::poll_fn
|
||||
fn poll_fn<T, F: FnMut(&mut Context<'_>) -> Poll<T> + Unpin>(f: F) -> PollFn<F> {
|
||||
PollFn(f)
|
||||
}
|
||||
struct PollFn<F>(F);
|
||||
impl<T, F: FnMut(&mut Context<'_>) -> Poll<T> + Unpin> std::future::Future for PollFn<F> {
|
||||
type Output = T;
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||
(self.get_mut().0)(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use tokio_stream::EventStream;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn result_events_iter(
|
||||
events: &[libc::input_event],
|
||||
) -> impl Iterator<Item = Result<libc::input_event, ()>> + '_ {
|
||||
let mut range = 0..0;
|
||||
std::iter::from_fn(move || {
|
||||
let (res, _) = sync_events(&mut range, events, |_| {});
|
||||
match res {
|
||||
Ok(x) => Some(Ok(x)),
|
||||
Err(true) => Some(Err(())),
|
||||
Err(false) => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn events_iter(events: &[libc::input_event]) -> impl Iterator<Item = libc::input_event> + '_ {
|
||||
result_events_iter(events).flatten()
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const time: libc::timeval = libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
};
|
||||
const KEY4: libc::input_event = libc::input_event {
|
||||
time,
|
||||
type_: EventType::KEY.0,
|
||||
code: Key::KEY_4.0,
|
||||
value: 1,
|
||||
};
|
||||
const REPORT: libc::input_event = libc::input_event {
|
||||
time,
|
||||
type_: EventType::SYNCHRONIZATION.0,
|
||||
code: Synchronization::SYN_REPORT.0,
|
||||
value: 0,
|
||||
};
|
||||
const DROPPED: libc::input_event = libc::input_event {
|
||||
code: Synchronization::SYN_DROPPED.0,
|
||||
..REPORT
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_sync_impl() {
|
||||
itertools::assert_equal(events_iter(&[]), vec![]);
|
||||
itertools::assert_equal(events_iter(&[KEY4]), vec![]);
|
||||
itertools::assert_equal(events_iter(&[KEY4, REPORT]), vec![KEY4, REPORT]);
|
||||
itertools::assert_equal(events_iter(&[KEY4, REPORT, KEY4]), vec![KEY4, REPORT]);
|
||||
itertools::assert_equal(
|
||||
result_events_iter(&[KEY4, REPORT, KEY4, DROPPED, REPORT]),
|
||||
vec![Ok(KEY4), Ok(REPORT), Err(())],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter_consistency() {
|
||||
// once it sees a SYN_DROPPED, it shouldn't mark the block after it as consumed even if we
|
||||
// keep calling the iterator like an idiot
|
||||
let evs = &[KEY4, REPORT, DROPPED, REPORT, KEY4, REPORT, KEY4];
|
||||
let mut range = 0..0;
|
||||
let mut next = || sync_events(&mut range, evs, |_| {});
|
||||
assert_eq!(next(), (Ok(KEY4), Some(2)));
|
||||
assert_eq!(next(), (Ok(REPORT), None));
|
||||
assert_eq!(next(), (Err(true), Some(4)));
|
||||
assert_eq!(next(), (Err(false), None));
|
||||
assert_eq!(next(), (Err(false), None));
|
||||
assert_eq!(next(), (Err(false), None));
|
||||
}
|
||||
}
|
|
@ -1,58 +1 @@
|
|||
use tokio_1 as tokio;
|
||||
|
||||
use crate::{nix_err, Device, InputEvent, DEFAULT_EVENT_COUNT};
|
||||
use futures_core::{ready, Stream};
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::unix::AsyncFd;
|
||||
|
||||
/// An async stream of events.
|
||||
pub struct EventStream {
|
||||
device: AsyncFd<Device>,
|
||||
}
|
||||
impl Unpin for EventStream {}
|
||||
|
||||
impl EventStream {
|
||||
pub(crate) fn new(device: Device) -> io::Result<Self> {
|
||||
use nix::fcntl;
|
||||
fcntl::fcntl(device.as_raw_fd(), fcntl::F_SETFL(fcntl::OFlag::O_NONBLOCK))
|
||||
.map_err(nix_err)?;
|
||||
let device = AsyncFd::new(device)?;
|
||||
Ok(Self { device })
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying device
|
||||
pub fn device(&self) -> &Device {
|
||||
self.device.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for EventStream {
|
||||
type Item = io::Result<InputEvent>;
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let me = self.get_mut();
|
||||
|
||||
if let Some(ev) = me.device.get_mut().pop_event() {
|
||||
return Poll::Ready(Some(Ok(ev)));
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut guard = ready!(me.device.poll_read_ready_mut(cx))?;
|
||||
|
||||
match guard.try_io(|device| device.get_mut().fill_events(DEFAULT_EVENT_COUNT)) {
|
||||
Ok(res) => {
|
||||
let ret = match res {
|
||||
Ok(0) => None,
|
||||
Ok(_) => Some(Ok(me.device.get_mut().pop_event().unwrap())),
|
||||
Err(e) if e.raw_os_error() == Some(libc::ENODEV) => None,
|
||||
Err(e) => Some(Err(e)),
|
||||
};
|
||||
return Poll::Ready(ret);
|
||||
}
|
||||
Err(_would_block) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue