Rework the types in constants (#34)

* Replace bitflags with newtypes

* Don't reexport from libc

* Add a wrapper for event_info

* Hide EvdevEnum

* Make the AttributeSet API more setlike

* Move AttributeSet to its own module

* Add some doc, tidy APIs
This commit is contained in:
Noah 2021-02-24 19:09:44 -06:00 committed by GitHub
parent 7da53b57fe
commit 3f32c41fc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 717 additions and 550 deletions

View file

@ -9,10 +9,7 @@ documentation = "https://docs.rs/evdev"
edition = "2018"
[dependencies]
bitflags = "1.2"
libc = "0.2.22"
bitvec = "0.21"
num-derive = "0.3.3"
num-traits = { version = "0.2", default-features = false }
nix = "0.19.0"
thiserror = "1.0.24"

65
src/attribute_set.rs Normal file
View file

@ -0,0 +1,65 @@
use bitvec::prelude::*;
use std::fmt;
#[derive(Copy, Clone)]
pub struct AttributeSet<'a, T> {
bitslice: &'a BitSlice<Lsb0, u8>,
_indexer: std::marker::PhantomData<T>,
}
impl<'a, T: EvdevEnum> AttributeSet<'a, T> {
#[inline]
pub(crate) fn new(bitslice: &'a BitSlice<Lsb0, u8>) -> Self {
Self {
bitslice,
_indexer: std::marker::PhantomData,
}
}
#[inline]
pub fn contains(&self, attr: T) -> bool {
self.bitslice.get(attr.to_index()).map_or(false, |b| *b)
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = T> + 'a {
self.bitslice.iter_ones().map(T::from_index)
}
}
impl<'a, T: EvdevEnum + fmt::Debug> fmt::Debug for AttributeSet<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
pub trait EvdevEnum: Copy + 'static {
fn from_index(i: usize) -> Self;
fn to_index(self) -> usize;
}
macro_rules! evdev_enum {
($t:ty, $($(#[$attr:meta])* $c:ident = $val:expr,)*) => {
impl $t {
$($(#[$attr])* pub const $c: Self = Self($val);)*
}
impl std::fmt::Debug for $t {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
$(Self::$c => f.pad(stringify!($c)),)*
_ => write!(f, "unknown key: {}", self.0),
}
}
}
impl $crate::attribute_set::EvdevEnum for $t {
#[inline]
fn from_index(i: usize) -> Self {
Self(i as _)
}
#[inline]
fn to_index(self) -> usize {
self.0 as _
}
}
}
}

View file

@ -1,284 +1,311 @@
use bitflags::bitflags;
/// Event types supported by the device.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct EventType(pub u16);
bitflags! {
/// Event types supported by the device.
pub struct Types: u32 {
/// A bookkeeping event. Usually not important to applications.
const SYNCHRONIZATION = 1 << 0x00;
/// A key changed state. A key, or button, is usually a momentary switch (in the circuit sense). It has two
/// states: down, or up. There are events for when keys are pressed (become down) and
/// released (become up). There are also "key repeats", where multiple events are sent
/// while a key is down.
const KEY = 1 << 0x01;
/// Movement on a relative axis. There is no absolute coordinate frame, just the fact that
/// there was a change of a certain amount of units. Used for things like mouse movement or
/// scroll wheels.
const RELATIVE = 1 << 0x02;
/// Movement on an absolute axis. Used for things such as touch events and joysticks.
const ABSOLUTE = 1 << 0x03;
/// Miscellaneous events that don't fall into other categories. I'm not quite sure when
/// these happen or what they correspond to.
const MISC = 1 << 0x04;
/// Change in a switch value. Switches are boolean conditions and usually correspond to a
/// toggle switch of some kind in hardware.
const SWITCH = 1 << 0x05;
/// An LED was toggled.
const LED = 1 << 0x11;
/// A sound was made.
const SOUND = 1 << 0x12;
/// There are no events of this type, to my knowledge, but represents metadata about key
/// repeat configuration.
const REPEAT = 1 << 0x14;
/// I believe there are no events of this type, but rather this is used to represent that
/// the device can create haptic effects.
const FORCEFEEDBACK = 1 << 0x15;
/// I think this is unused?
const POWER = 1 << 0x16;
/// A force feedback effect's state changed.
const FORCEFEEDBACKSTATUS = 1 << 0x17;
}
evdev_enum!(
EventType,
/// 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
/// states: down, or up. There are events for when keys are pressed (become down) and
/// released (become up). There are also "key repeats", where multiple events are sent
/// while a key is down.
KEY = 0x01,
/// Movement on a relative axis. There is no absolute coordinate frame, just the fact that
/// there was a change of a certain amount of units. Used for things like mouse movement or
/// scroll wheels.
RELATIVE = 0x02,
/// Movement on an absolute axis. Used for things such as touch events and joysticks.
ABSOLUTE = 0x03,
/// Miscellaneous events that don't fall into other categories. I'm not quite sure when
/// these happen or what they correspond to.
MISC = 0x04,
/// Change in a switch value. Switches are boolean conditions and usually correspond to a
/// toggle switch of some kind in hardware.
SWITCH = 0x05,
/// An LED was toggled.
LED = 0x11,
/// A sound was made.
SOUND = 0x12,
/// There are no events of this type, to my knowledge, but represents metadata about key
/// repeat configuration.
REPEAT = 0x14,
/// I believe there are no events of this type, but rather this is used to represent that
/// the device can create haptic effects.
FORCEFEEDBACK = 0x15,
/// I think this is unused?
POWER = 0x16,
/// A force feedback effect's state changed.
FORCEFEEDBACKSTATUS = 0x17,
);
impl EventType {
pub(crate) const COUNT: usize = 0x20;
}
bitflags! {
/// Device properties.
pub struct Props: u32 {
/// This input device needs a pointer ("cursor") for the user to know its state.
const POINTER = 1 << 0x00;
/// "direct input devices", according to the header.
const DIRECT = 1 << 0x01;
/// "has button(s) under pad", according to the header.
const BUTTONPAD = 1 << 0x02;
/// Touch rectangle only (I think this means that if there are multiple touches, then the
/// bounding rectangle of all the touches is returned, not each touch).
const SEMI_MT = 1 << 0x03;
/// "softbuttons at top of pad", according to the header.
const TOPBUTTONPAD = 1 << 0x04;
/// Is a pointing stick ("nub" etc, https://xkcd.com/243/)
const POINTING_STICK = 1 << 0x05;
/// Has an accelerometer. Probably reports relative events in that case?
const ACCELEROMETER = 1 << 0x06;
}
/// Device properties.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct PropType(pub u16);
evdev_enum!(
PropType,
/// This input device needs a pointer ("cursor") for the user to know its state.
POINTER = 0x00,
/// "direct input devices", according to the header.
DIRECT = 0x01,
/// "has button(s) under pad", according to the header.
BUTTONPAD = 0x02,
/// Touch rectangle only (I think this means that if there are multiple touches, then the
/// bounding rectangle of all the touches is returned, not each touch).
SEMI_MT = 0x03,
/// "softbuttons at top of pad", according to the header.
TOPBUTTONPAD = 0x04,
/// Is a pointing stick ("nub" etc, https://xkcd.com/243/)
POINTING_STICK = 0x05,
/// Has an accelerometer. Probably reports relative events in that case?
ACCELEROMETER = 0x06,
);
impl PropType {
pub(crate) const COUNT: usize = 0x20;
}
bitflags! {
pub struct RelativeAxis: u32 {
const REL_X = 1 << 0x00;
const REL_Y = 1 << 0x01;
const REL_Z = 1 << 0x02;
const REL_RX = 1 << 0x03;
const REL_RY = 1 << 0x04;
const REL_RZ = 1 << 0x05;
const REL_HWHEEL = 1 << 0x06;
const REL_DIAL = 1 << 0x07;
const REL_WHEEL = 1 << 0x08;
const REL_MISC = 1 << 0x09;
const REL_RESERVED = 1 << 0x0a;
const REL_WHEEL_HI_RES = 1 << 0x0b;
const REL_HWHEEL_HI_RES = 1 << 0x0c;
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct RelativeAxisType(pub u16);
evdev_enum!(
RelativeAxisType,
REL_X = 0x00,
REL_Y = 0x01,
REL_Z = 0x02,
REL_RX = 0x03,
REL_RY = 0x04,
REL_RZ = 0x05,
REL_HWHEEL = 0x06,
REL_DIAL = 0x07,
REL_WHEEL = 0x08,
REL_MISC = 0x09,
REL_RESERVED = 0x0a,
REL_WHEEL_HI_RES = 0x0b,
REL_HWHEEL_HI_RES = 0x0c,
);
impl RelativeAxisType {
pub(crate) const COUNT: usize = 0x10;
}
// impl RelativeAxis {
// const MAX: usize = 0x0f;
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct AbsoluteAxisType(pub u16);
evdev_enum!(
AbsoluteAxisType,
ABS_X = 0x00,
ABS_Y = 0x01,
ABS_Z = 0x02,
ABS_RX = 0x03,
ABS_RY = 0x04,
ABS_RZ = 0x05,
ABS_THROTTLE = 0x06,
ABS_RUDDER = 0x07,
ABS_WHEEL = 0x08,
ABS_GAS = 0x09,
ABS_BRAKE = 0x0a,
ABS_HAT0X = 0x10,
ABS_HAT0Y = 0x11,
ABS_HAT1X = 0x12,
ABS_HAT1Y = 0x13,
ABS_HAT2X = 0x14,
ABS_HAT2Y = 0x15,
ABS_HAT3X = 0x16,
ABS_HAT3Y = 0x17,
ABS_PRESSURE = 0x18,
ABS_DISTANCE = 0x19,
ABS_TILT_X = 0x1a,
ABS_TILT_Y = 0x1b,
ABS_TOOL_WIDTH = 0x1c,
ABS_VOLUME = 0x20,
ABS_MISC = 0x28,
/// "MT slot being modified"
ABS_MT_SLOT = 0x2f,
/// "Major axis of touching ellipse"
ABS_MT_TOUCH_MAJOR = 0x30,
/// "Minor axis (omit if circular)"
ABS_MT_TOUCH_MINOR = 0x31,
/// "Major axis of approaching ellipse"
ABS_MT_WIDTH_MAJOR = 0x32,
/// "Minor axis (omit if circular)"
ABS_MT_WIDTH_MINOR = 0x33,
/// "Ellipse orientation"
ABS_MT_ORIENTATION = 0x34,
/// "Center X touch position"
ABS_MT_POSITION_X = 0x35,
/// "Center Y touch position"
ABS_MT_POSITION_Y = 0x36,
/// "Type of touching device"
ABS_MT_TOOL_TYPE = 0x37,
/// "Group a set of packets as a blob"
ABS_MT_BLOB_ID = 0x38,
/// "Unique ID of the initiated contact"
ABS_MT_TRACKING_ID = 0x39,
/// "Pressure on contact area"
ABS_MT_PRESSURE = 0x3a,
/// "Contact over distance"
ABS_MT_DISTANCE = 0x3b,
/// "Center X tool position"
ABS_MT_TOOL_X = 0x3c,
/// "Center Y tool position"
ABS_MT_TOOL_Y = 0x3d,
);
impl AbsoluteAxisType {
pub(crate) const COUNT: usize = 0x40;
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SwitchType(pub u16);
evdev_enum!(
SwitchType,
/// "set = lid shut"
SW_LID = 0x00,
/// "set = tablet mode"
SW_TABLET_MODE = 0x01,
/// "set = inserted"
SW_HEADPHONE_INSERT = 0x02,
/// "rfkill master switch, type 'any'"
SW_RFKILL_ALL = 0x03,
/// "set = inserted"
SW_MICROPHONE_INSERT = 0x04,
/// "set = plugged into doc"
SW_DOCK = 0x05,
/// "set = inserted"
SW_LINEOUT_INSERT = 0x06,
/// "set = mechanical switch set"
SW_JACK_PHYSICAL_INSERT = 0x07,
/// "set = inserted"
SW_VIDEOOUT_INSERT = 0x08,
/// "set = lens covered"
SW_CAMERA_LENS_COVER = 0x09,
/// "set = keypad slide out"
SW_KEYPAD_SLIDE = 0x0a,
/// "set = front proximity sensor active"
SW_FRONT_PROXIMITY = 0x0b,
/// "set = rotate locked/disabled"
SW_ROTATE_LOCK = 0x0c,
/// "set = inserted"
SW_LINEIN_INSERT = 0x0d,
/// "set = device disabled"
SW_MUTE_DEVICE = 0x0e,
/// "set = pen inserted"
SW_PEN_INSERTED = 0x0f,
/// "set = cover closed"
SW_MACHINE_COVER = 0x10,
);
impl SwitchType {
pub(crate) const COUNT: usize = 0x11;
}
/// LEDs specified by USB HID.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct LedType(pub u16);
evdev_enum!(
LedType,
LED_NUML = 0x00,
LED_CAPSL = 0x01,
LED_SCROLLL = 0x02,
LED_COMPOSE = 0x03,
LED_KANA = 0x04,
/// "Stand-by"
LED_SLEEP = 0x05,
LED_SUSPEND = 0x06,
LED_MUTE = 0x07,
/// "Generic indicator"
LED_MISC = 0x08,
/// "Message waiting"
LED_MAIL = 0x09,
/// "External power connected"
LED_CHARGING = 0x0a,
);
impl LedType {
pub(crate) const COUNT: usize = 0x10;
}
/// Various miscellaneous event types. Current as of kernel 4.1.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct MiscType(pub u16);
evdev_enum!(
MiscType,
/// Serial number, only exported for tablets ("Transducer Serial Number")
MSC_SERIAL = 0x00,
/// Only used by the PowerMate driver, right now.
MSC_PULSELED = 0x01,
/// Completely unused.
MSC_GESTURE = 0x02,
/// "Raw" event, rarely used.
MSC_RAW = 0x03,
/// Key scancode
MSC_SCAN = 0x04,
/// Completely unused.
MSC_TIMESTAMP = 0x05,
);
impl MiscType {
pub(crate) const COUNT: usize = 0x08;
}
// pub enum FFStatusDataIndex {
// FF_STATUS_STOPPED = 0x00,
// FF_STATUS_PLAYING = 0x01,
// }
bitflags! {
pub struct AbsoluteAxis: u64 {
const ABS_X = 1 << 0x00;
const ABS_Y = 1 << 0x01;
const ABS_Z = 1 << 0x02;
const ABS_RX = 1 << 0x03;
const ABS_RY = 1 << 0x04;
const ABS_RZ = 1 << 0x05;
const ABS_THROTTLE = 1 << 0x06;
const ABS_RUDDER = 1 << 0x07;
const ABS_WHEEL = 1 << 0x08;
const ABS_GAS = 1 << 0x09;
const ABS_BRAKE = 1 << 0x0a;
const ABS_HAT0X = 1 << 0x10;
const ABS_HAT0Y = 1 << 0x11;
const ABS_HAT1X = 1 << 0x12;
const ABS_HAT1Y = 1 << 0x13;
const ABS_HAT2X = 1 << 0x14;
const ABS_HAT2Y = 1 << 0x15;
const ABS_HAT3X = 1 << 0x16;
const ABS_HAT3Y = 1 << 0x17;
const ABS_PRESSURE = 1 << 0x18;
const ABS_DISTANCE = 1 << 0x19;
const ABS_TILT_X = 1 << 0x1a;
const ABS_TILT_Y = 1 << 0x1b;
const ABS_TOOL_WIDTH = 1 << 0x1c;
const ABS_VOLUME = 1 << 0x20;
const ABS_MISC = 1 << 0x28;
/// "MT slot being modified"
const ABS_MT_SLOT = 1 << 0x2f;
/// "Major axis of touching ellipse"
const ABS_MT_TOUCH_MAJOR = 1 << 0x30;
/// "Minor axis (omit if circular)"
const ABS_MT_TOUCH_MINOR = 1 << 0x31;
/// "Major axis of approaching ellipse"
const ABS_MT_WIDTH_MAJOR = 1 << 0x32;
/// "Minor axis (omit if circular)"
const ABS_MT_WIDTH_MINOR = 1 << 0x33;
/// "Ellipse orientation"
const ABS_MT_ORIENTATION = 1 << 0x34;
/// "Center X touch position"
const ABS_MT_POSITION_X = 1 << 0x35;
/// "Center Y touch position"
const ABS_MT_POSITION_Y = 1 << 0x36;
/// "Type of touching device"
const ABS_MT_TOOL_TYPE = 1 << 0x37;
/// "Group a set of packets as a blob"
const ABS_MT_BLOB_ID = 1 << 0x38;
/// "Unique ID of the initiated contact"
const ABS_MT_TRACKING_ID = 1 << 0x39;
/// "Pressure on contact area"
const ABS_MT_PRESSURE = 1 << 0x3a;
/// "Contact over distance"
const ABS_MT_DISTANCE = 1 << 0x3b;
/// "Center X tool position"
const ABS_MT_TOOL_X = 1 << 0x3c;
/// "Center Y tool position"
const ABS_MT_TOOL_Y = 1 << 0x3d;
}
}
impl AbsoluteAxis {
pub(crate) const MAX: usize = 0x3f;
}
bitflags! {
pub struct Switch: u32 {
/// "set = lid shut"
const SW_LID = 1 << 0x00;
/// "set = tablet mode"
const SW_TABLET_MODE = 1 << 0x01;
/// "set = inserted"
const SW_HEADPHONE_INSERT = 1 << 0x02;
/// "rfkill master switch, type 'any'"
const SW_RFKILL_ALL = 1 << 0x03;
/// "set = inserted"
const SW_MICROPHONE_INSERT = 1 << 0x04;
/// "set = plugged into doc"
const SW_DOCK = 1 << 0x05;
/// "set = inserted"
const SW_LINEOUT_INSERT = 1 << 0x06;
/// "set = mechanical switch set"
const SW_JACK_PHYSICAL_INSERT = 1 << 0x07;
/// "set = inserted"
const SW_VIDEOOUT_INSERT = 1 << 0x08;
/// "set = lens covered"
const SW_CAMERA_LENS_COVER = 1 << 0x09;
/// "set = keypad slide out"
const SW_KEYPAD_SLIDE = 1 << 0x0a;
/// "set = front proximity sensor active"
const SW_FRONT_PROXIMITY = 1 << 0x0b;
/// "set = rotate locked/disabled"
const SW_ROTATE_LOCK = 1 << 0x0c;
/// "set = inserted"
const SW_LINEIN_INSERT = 1 << 0x0d;
/// "set = device disabled"
const SW_MUTE_DEVICE = 1 << 0x0e;
/// "set = pen inserted"
const SW_PEN_INSERTED = 1 << 0x0f;
}
}
impl Switch {
pub(crate) const MAX: usize = 0x10;
}
bitflags! {
/// LEDs specified by USB HID.
pub struct Led: u32 {
const LED_NUML = 1 << 0x00;
const LED_CAPSL = 1 << 0x01;
const LED_SCROLLL = 1 << 0x02;
const LED_COMPOSE = 1 << 0x03;
const LED_KANA = 1 << 0x04;
/// "Stand-by"
const LED_SLEEP = 1 << 0x05;
const LED_SUSPEND = 1 << 0x06;
const LED_MUTE = 1 << 0x07;
/// "Generic indicator"
const LED_MISC = 1 << 0x08;
/// "Message waiting"
const LED_MAIL = 1 << 0x09;
/// "External power connected"
const LED_CHARGING = 1 << 0x0a;
}
}
impl Led {
pub(crate) const MAX: usize = 0x10;
}
bitflags! {
/// Various miscellaneous event types. Current as of kernel 4.1.
pub struct Misc: u32 {
/// Serial number, only exported for tablets ("Transducer Serial Number")
const MSC_SERIAL = 1 << 0x00;
/// Only used by the PowerMate driver, right now.
const MSC_PULSELED = 1 << 0x01;
/// Completely unused.
const MSC_GESTURE = 1 << 0x02;
/// "Raw" event, rarely used.
const MSC_RAW = 1 << 0x03;
/// Key scancode
const MSC_SCAN = 1 << 0x04;
/// Completely unused.
const MSC_TIMESTAMP = 1 << 0x05;
}
}
// impl Misc {
// const MAX: usize = 0x07;
// #[derive(Copy, Clone, Copy, Clone)]
// pub enum FFEffect {
// FF_RUMBLE = 0x50,
// FF_PERIODIC = 0x51,
// FF_CONSTANT = 0x52,
// FF_SPRING = 0x53,
// FF_FRICTION = 0x54,
// FF_DAMPER = 0x55,
// FF_INERTIA = 0x56,
// FF_RAMP = 0x57,
// FF_SQUARE = 0x58,
// FF_TRIANGLE = 0x59,
// FF_SINE = 0x5a,
// FF_SAW_UP = 0x5b,
// FF_SAW_DOWN = 0x5c,
// FF_CUSTOM = 0x5d,
// FF_GAIN = 0x60,
// FF_AUTOCENTER = 0x61,
// }
bitflags! {
pub struct FFStatus: u32 {
const FF_STATUS_STOPPED = 1 << 0x00;
const FF_STATUS_PLAYING = 1 << 0x01;
}
}
// impl FFEffect {
// // Needs to be a multiple of 8
// pub const COUNT: usize = 0x80;
// }
#[repr(C)]
#[derive(Copy, Clone)]
pub enum FFEffect {
FF_RUMBLE = 0x50,
FF_PERIODIC = 0x51,
FF_CONSTANT = 0x52,
FF_SPRING = 0x53,
FF_FRICTION = 0x54,
FF_DAMPER = 0x55,
FF_INERTIA = 0x56,
FF_RAMP = 0x57,
FF_SQUARE = 0x58,
FF_TRIANGLE = 0x59,
FF_SINE = 0x5a,
FF_SAW_UP = 0x5b,
FF_SAW_DOWN = 0x5c,
FF_CUSTOM = 0x5d,
FF_GAIN = 0x60,
FF_AUTOCENTER = 0x61,
}
// #[derive(Copy, Clone, PartialEq, Eq)]
// pub struct RepeatType(pub u16);
impl FFEffect {
// Needs to be a multiple of 8
pub const MAX: usize = 0x80;
}
// evdev_enum!(RepeatType, REP_DELAY = 0x00, REP_PERIOD = 0x01,);
bitflags! {
pub struct Repeat: u32 {
const REP_DELAY = 1 << 0x00;
const REP_PERIOD = 1 << 0x01;
}
}
// impl RepeatType {
// pub(crate) const COUNT: usize = 0x02;
// }
bitflags! {
pub struct Sound: u32 {
const SND_CLICK = 1 << 0x00;
const SND_BELL = 1 << 0x01;
const SND_TONE = 1 << 0x02;
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SoundType(pub u16);
evdev_enum!(
SoundType,
SND_CLICK = 0x00,
SND_BELL = 0x01,
SND_TONE = 0x02,
);
impl SoundType {
pub(crate) const COUNT: usize = 0x08;
}

View file

@ -7,15 +7,32 @@
//! - https://www.kernel.org/doc/Documentation/input/event-codes.txt
//! - https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
//!
//! Devices can expose a few different kinds of events, specified by the `Types` bitflag. Each
//! event type (except for RELATIVE and SYNCHRONIZATION) also has some associated state. See the documentation for
//! `Types` on what each type corresponds to.
//! Devices emit events, represented by the [`InputEvent`] type. Each device supports a few different
//! kinds of events, specified by the [`EventType`] struct and the [`Device::supported_events()`]
//! method. Most event types also have a "subtype", e.g. a `KEY` event with a `KEY_ENTER` code. This
//! type+subtype combo is represented by [`InputEventKind`]/[`InputEvent::kind()`]. The individual
//! subtypes of a type that a device supports can be retrieved through the `Device::supported_*()`
//! methods, e.g. [`Device::supported_keys()`]:
//!
//! This state can be queried. For example, the `DeviceState::led_vals` field will tell you which
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use evdev::{Device, Key};
//! let device = Device::open("/dev/input/event0")?;
//! // check if the device has an ENTER key
//! if device.supported_keys().map_or(false, |keys| keys.contains(Key::KEY_ENTER)) {
//! println!("are you prepared to ENTER the world of evdev?");
//! } else {
//! println!(":(");
//! }
//! # Ok(())
//! # }
//! ```
//!
//! 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.
//! [`Device::sync_state`] to explicitly synchronize with the kernel state.
//!
//! 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
@ -28,17 +45,23 @@
//! state with the kernel, only one (or zero, if the switch is in the same state as it was before
//! the sync) switch events will be emulated.
//!
//! It is recommended that you dedicate a thread to processing input events, or use epoll with the
//! fd returned by `Device::fd` to process events when they are ready.
//! It is recommended that you dedicate a thread to processing input events, or use epoll or an
//! async runtime with the fd returned by `<Device as AsRawFd>::as_raw_fd` to process events when
//! they are ready.
#![cfg(any(unix, target_os = "android"))]
#![allow(non_camel_case_types)]
// has to be first for its macro
#[macro_use]
mod attribute_set;
mod constants;
pub mod raw;
mod scancodes;
use bitvec::prelude::*;
use std::fmt;
use std::fs::File;
use std::fs::OpenOptions;
use std::mem;
@ -47,18 +70,15 @@ use std::os::unix::{
io::{AsRawFd, RawFd},
};
use std::path::Path;
use std::time::SystemTime;
use std::time::{Duration, SystemTime};
use std::{ffi::CString, mem::MaybeUninit};
pub use crate::constants::FFEffect::*;
// pub use crate::constants::FFEffect::*;
pub use crate::attribute_set::AttributeSet;
pub use crate::constants::*;
pub use crate::scancodes::*;
pub use crate::Synchronization::*;
pub use crate::constants::*;
use crate::raw::*;
type ByteBitBox = BitBox<Lsb0, u8>;
fn ioctl_get_cstring(
f: unsafe fn(RawFd, &mut [u8]) -> nix::Result<libc::c_int>,
fd: RawFd,
@ -84,35 +104,6 @@ fn ioctl_get_cstring(
}
}
macro_rules! impl_number {
($($t:ident),*) => {
$(impl $t {
/// Given a bitflag with only a single flag set, returns the event code corresponding to that
/// event. If multiple flags are set, the one with the most significant bit wins. In debug
/// mode,
#[inline(always)]
pub fn number<T: num_traits::FromPrimitive>(&self) -> T {
let val = self.bits().trailing_zeros();
debug_assert!(self.bits() == 1 << val, "{:?} ought to have only one flag set to be used with .number()", self);
T::from_u32(val).unwrap()
}
})*
}
}
impl_number!(
Types,
Props,
RelativeAxis,
AbsoluteAxis,
Switch,
Led,
Misc,
FFStatus,
Repeat,
Sound
);
#[repr(u16)]
#[derive(Copy, Clone, Debug)]
pub enum Synchronization {
@ -126,17 +117,49 @@ pub enum Synchronization {
SYN_DROPPED = 3,
}
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)]
pub struct DeviceState {
/// The state corresponds to kernel state at this timestamp.
pub timestamp: libc::timeval,
timestamp: libc::timeval,
/// Set = key pressed
pub key_vals: Option<ByteBitBox>,
pub abs_vals: Option<Vec<input_absinfo>>,
key_vals: Option<Box<KeyArray>>,
abs_vals: Option<Box<[libc::input_absinfo; AbsoluteAxisType::COUNT]>>,
/// Set = switch enabled (closed)
pub switch_vals: Option<ByteBitBox>,
switch_vals: Option<BitArr!(for SwitchType::COUNT, in u8)>,
/// Set = LED lit
pub led_vals: Option<ByteBitBox>,
led_vals: Option<BitArr!(for LedType::COUNT, in u8)>,
}
impl DeviceState {
pub fn key_vals(&self) -> Option<AttributeSet<'_, Key>> {
self.key_vals
.as_deref()
.map(|v| AttributeSet::new(BitSlice::from_slice(v).unwrap()))
}
pub fn timestamp(&self) -> SystemTime {
timeval_to_systime(&self.timestamp)
}
pub fn abs_vals(&self) -> Option<&[libc::input_absinfo]> {
self.abs_vals.as_deref().map(|v| &v[..])
}
pub fn switch_vals(&self) -> Option<AttributeSet<'_, SwitchType>> {
self.switch_vals.as_deref().map(AttributeSet::new)
}
pub fn led_vals(&self) -> Option<AttributeSet<'_, LedType>> {
self.led_vals.as_deref().map(AttributeSet::new)
}
}
impl Default for DeviceState {
@ -166,24 +189,24 @@ pub enum Error {
#[derive(Debug)]
pub struct Device {
file: File,
ty: Types,
ty: BitArr!(for EventType::COUNT, in u8),
name: Option<String>,
phys: Option<String>,
uniq: Option<String>,
id: input_id,
props: Props,
id: libc::input_id,
props: BitArr!(for PropType::COUNT, in u8),
driver_version: (u8, u8, u8),
supported_keys: Option<ByteBitBox>,
supported_relative: Option<RelativeAxis>,
supported_absolute: Option<AbsoluteAxis>,
supported_switch: Option<Switch>,
supported_led: Option<Led>,
supported_misc: Option<Misc>,
// ff: Option<ByteBitBox>,
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<Sound>,
pending_events: Vec<input_event>,
supported_snd: Option<BitArr!(for SoundType::COUNT, in u8)>,
pending_events: Vec<libc::input_event>,
// pending_events[last_seen..] is the events that have occurred since the last sync.
last_seen: usize,
state: DeviceState,
@ -220,9 +243,9 @@ const fn bus_name(x: u16) -> &'static str {
}
}
impl std::fmt::Display for Device {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "{}", self.name.as_deref().unwrap_or("Unnamed device"))?;
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: {}.{}.{}",
@ -236,30 +259,32 @@ impl std::fmt::Display for Device {
}
writeln!(f, " Bus: {}", bus_name(self.id.bustype))?;
writeln!(f, " Vendor: 0x{:x}", self.id.vendor)?;
writeln!(f, " Product: 0x{:x}", self.id.product)?;
writeln!(f, " Version: 0x{:x}", self.id.version)?;
writeln!(f, " Properties: {:?}", self.props)?;
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)
if let (Some(supported_keys), Some(key_vals)) =
(self.supported_keys(), self.state.key_vals())
{
writeln!(f, " Keys supported:")?;
for (key_idx, (key_supported, key_enabled)) in
supported_keys.iter().zip(key_vals.iter()).enumerate()
{
if *key_supported {
writeln!(
f,
" {:?} ({}index {})",
Key::new(key_idx as u32),
if *key_enabled { "pressed, " } else { "" },
key_idx
)?;
}
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 {
if let Some(supported_relative) = self.supported_relative_axes() {
writeln!(f, " Relative Axes: {:?}", supported_relative)?;
}
@ -267,15 +292,13 @@ impl std::fmt::Display for Device {
(self.supported_absolute, &self.state.abs_vals)
{
writeln!(f, " Absolute Axes:")?;
for idx in 0..AbsoluteAxis::MAX {
let abs = AbsoluteAxis::from_bits_truncate(1 << idx);
if supported_abs.contains(abs) {
writeln!(f, " {:?} ({:?}, index {})", abs, abs_vals[idx], idx)?;
}
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.supported_misc {
if let Some(supported_misc) = self.misc_properties() {
writeln!(f, " Miscellaneous capabilities: {:?}", supported_misc)?;
}
@ -283,41 +306,41 @@ impl std::fmt::Display for Device {
(self.supported_switch, &self.state.switch_vals)
{
writeln!(f, " Switches:")?;
for idx in 0..Switch::MAX {
let sw = Switch::from_bits(1 << idx).unwrap();
if supported_switch.contains(sw) {
writeln!(f, " {:?} ({:?}, index {})", sw, switch_vals[idx], idx)?;
}
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 0..Led::MAX {
let led = Led::from_bits_truncate(1 << idx);
if supported_led.contains(led) {
writeln!(f, " {:?} ({:?}, index {})", led, led_vals[idx], idx)?;
}
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 {
writeln!(f, " Sound: {:?}", 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.contains(Types::FORCEFEEDBACK) {
if self.ty[EventType::FORCEFEEDBACK.0 as usize] {
writeln!(f, " Force Feedback supported")?;
}
if self.ty.contains(Types::POWER) {
if self.ty[EventType::POWER.0 as usize] {
writeln!(f, " Power supported")?;
}
if self.ty.contains(Types::FORCEFEEDBACKSTATUS) {
if self.ty[EventType::FORCEFEEDBACKSTATUS.0 as usize] {
writeln!(f, " Force Feedback status supported")?;
}
@ -326,8 +349,8 @@ impl std::fmt::Display for Device {
}
impl Device {
pub fn events_supported(&self) -> Types {
self.ty
pub fn supported_events(&self) -> AttributeSet<'_, EventType> {
AttributeSet::new(&self.ty)
}
pub fn name(&self) -> Option<&str> {
@ -342,48 +365,50 @@ impl Device {
self.uniq.as_deref()
}
pub fn input_id(&self) -> input_id {
pub fn input_id(&self) -> libc::input_id {
self.id
}
pub fn properties(&self) -> Props {
self.props
pub fn properties(&self) -> AttributeSet<'_, PropType> {
AttributeSet::new(&self.props)
}
pub fn driver_version(&self) -> (u8, u8, u8) {
self.driver_version
}
pub fn keys_supported(&self) -> Option<&BitSlice<Lsb0, u8>> {
self.supported_keys.as_deref()
pub fn supported_keys(&self) -> Option<AttributeSet<'_, Key>> {
self.supported_keys
.as_deref()
.map(|v| AttributeSet::new(BitSlice::from_slice(v).unwrap()))
}
pub fn relative_axes_supported(&self) -> Option<RelativeAxis> {
self.supported_relative
pub fn supported_relative_axes(&self) -> Option<AttributeSet<'_, RelativeAxisType>> {
self.supported_relative.as_deref().map(AttributeSet::new)
}
pub fn absolute_axes_supported(&self) -> Option<AbsoluteAxis> {
self.supported_absolute
pub fn supported_absolute_axes(&self) -> Option<AttributeSet<'_, AbsoluteAxisType>> {
self.supported_absolute.as_deref().map(AttributeSet::new)
}
pub fn switches_supported(&self) -> Option<Switch> {
self.supported_switch
pub fn supported_switches(&self) -> Option<AttributeSet<'_, SwitchType>> {
self.supported_switch.as_deref().map(AttributeSet::new)
}
pub fn leds_supported(&self) -> Option<Led> {
self.supported_led
pub fn supported_leds(&self) -> Option<AttributeSet<'_, LedType>> {
self.supported_led.as_deref().map(AttributeSet::new)
}
pub fn misc_properties(&self) -> Option<Misc> {
self.supported_misc
pub fn misc_properties(&self) -> Option<AttributeSet<'_, MiscType>> {
self.supported_misc.as_deref().map(AttributeSet::new)
}
// pub fn repeats_supported(&self) -> Option<Repeat> {
// pub fn supported_repeats(&self) -> Option<Repeat> {
// self.rep
// }
pub fn sounds_supported(&self) -> Option<Sound> {
self.supported_snd
pub fn supported_sounds(&self) -> Option<AttributeSet<'_, SoundType>> {
self.supported_snd.as_deref().map(AttributeSet::new)
}
pub fn state(&self) -> &DeviceState {
@ -406,25 +431,27 @@ impl Device {
.open(path)
.or_else(|_| options.write(false).open(path))?;
let mut ty = 0;
unsafe { eviocgbit_type(file.as_raw_fd(), &mut ty)? };
let ty = Types::from_bits(ty).expect("evdev: unexpected type bits! report a bug");
let ty = {
let mut ty = BitArray::zeroed();
unsafe { raw::eviocgbit_type(file.as_raw_fd(), ty.as_mut_raw_slice())? };
ty
};
let name = ioctl_get_cstring(eviocgname, file.as_raw_fd())
let name = ioctl_get_cstring(raw::eviocgname, file.as_raw_fd())
.map(|s| s.to_string_lossy().into_owned());
let phys = ioctl_get_cstring(eviocgphys, file.as_raw_fd())
let phys = ioctl_get_cstring(raw::eviocgphys, file.as_raw_fd())
.map(|s| s.to_string_lossy().into_owned());
let uniq = ioctl_get_cstring(eviocguniq, file.as_raw_fd())
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();
eviocgid(file.as_raw_fd(), id.as_mut_ptr())?;
raw::eviocgid(file.as_raw_fd(), id.as_mut_ptr())?;
id.assume_init()
};
let mut driver_version: i32 = 0;
unsafe {
eviocgversion(file.as_raw_fd(), &mut driver_version)?;
raw::eviocgversion(file.as_raw_fd(), &mut driver_version)?;
}
let driver_version = (
((driver_version >> 16) & 0xff) as u8,
@ -432,84 +459,83 @@ impl Device {
(driver_version & 0xff) as u8,
);
let mut props = 0;
unsafe {
eviocgprop(file.as_raw_fd(), &mut props)?;
} // FIXME: handle old kernel
let props = Props::from_bits(props).expect("evdev: unexpected prop bits! report a bug");
let props = {
let mut props = BitArray::zeroed();
unsafe { raw::eviocgprop(file.as_raw_fd(), props.as_mut_raw_slice())? };
props
}; // FIXME: handle old kernel
let mut state = DeviceState::default();
let supported_keys = if ty.contains(Types::KEY) {
let mut supported_keys = bitbox![_, _; 0; Key::MAX];
debug_assert!(supported_keys.len() % 8 == 0);
let key_slice = supported_keys.as_mut_slice();
unsafe {
debug_assert!(key_slice.len() == Key::MAX / 8);
eviocgbit_key(file.as_raw_fd(), key_slice)?;
}
let key_vals = bitbox![_, _; 0; Key::MAX];
debug_assert!(key_vals.len() % 8 == 0);
state.key_vals = Some(key_vals);
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)? };
Some(supported_keys)
} else {
None
};
let supported_relative = if ty.contains(Types::RELATIVE) {
let mut rel = 0;
unsafe { eviocgbit_relative(file.as_raw_fd(), &mut rel)? };
Some(RelativeAxis::from_bits(rel).expect("evdev: unexpected rel bits! report a bug"))
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())? };
Some(rel)
} else {
None
};
let supported_absolute = if ty.contains(Types::ABSOLUTE) {
let mut abs = 0;
unsafe { eviocgbit_absolute(file.as_raw_fd(), &mut abs)? };
state.abs_vals = Some(vec![input_absinfo_default(); 0x3f]);
Some(AbsoluteAxis::from_bits(abs).expect("evdev: unexpected abs bits! report a bug"))
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())? };
Some(abs)
} else {
None
};
let supported_switch = if ty.contains(Types::SWITCH) {
let mut switch = 0;
unsafe { eviocgbit_switch(file.as_raw_fd(), &mut switch)? };
state.switch_vals = Some(bitbox![_, _; 0; Switch::MAX]);
Some(Switch::from_bits(switch).expect("evdev: unexpected switch bits! report a bug"))
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())? };
Some(switch)
} else {
None
};
let supported_led = if ty.contains(Types::LED) {
let mut led = 0;
unsafe { eviocgbit_led(file.as_raw_fd(), &mut led)? };
let led_vals = bitbox![_, _; 0; Led::MAX];
debug_assert!(led_vals.len() % 8 == 0);
state.led_vals = Some(led_vals);
Some(Led::from_bits(led).expect("evdev: unexpected led bits! report a bug"))
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())? };
Some(led)
} else {
None
};
let supported_misc = if ty.contains(Types::MISC) {
let mut misc = 0;
unsafe { eviocgbit_misc(file.as_raw_fd(), &mut misc)? };
Some(Misc::from_bits(misc).expect("evdev: unexpected misc bits! report a bug"))
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())? };
Some(misc)
} else {
None
};
//unsafe { eviocgbit(file.as_raw_fd(), ffs(FORCEFEEDBACK.bits()), 0x7f, bits_as_u8_slice)?; }
//unsafe { raw::eviocgbit(file.as_raw_fd(), ffs(FORCEFEEDBACK.bits()), 0x7f, bits_as_u8_slice)?; }
let supported_snd = if ty.contains(Types::SOUND) {
let mut snd = 0;
unsafe { eviocgbit_sound(file.as_raw_fd(), &mut snd)? };
Some(Sound::from_bits(snd).expect("evdev: unexpected sound bits! report a bug"))
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())? };
Some(snd)
} else {
None
};
@ -546,32 +572,29 @@ impl Device {
pub fn sync_state(&mut self) -> Result<(), Error> {
let fd = self.as_raw_fd();
if let Some(key_vals) = &mut self.state.key_vals {
unsafe { eviocgkey(fd, key_vals.as_mut_slice())? };
unsafe { raw::eviocgkey(fd, &mut key_vals[..])? };
}
if let (Some(supported_abs), Some(abs_vals)) =
(self.supported_absolute, &mut self.state.abs_vals)
{
for idx in 0..AbsoluteAxis::MAX {
let abs = AbsoluteAxis::from_bits_truncate(1 << idx);
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)
if supported_abs.contains(abs) {
unsafe {
eviocgabs(fd, idx as u32, &mut abs_vals[idx])?;
}
unsafe {
raw::eviocgabs(fd, idx as u32, &mut abs_vals[idx])?;
}
}
}
if let Some(switch_vals) = &mut self.state.switch_vals {
unsafe { eviocgsw(fd, switch_vals.as_mut_slice())? };
unsafe { raw::eviocgsw(fd, switch_vals.as_mut_raw_slice())? };
}
if let Some(led_vals) = &mut self.state.led_vals {
unsafe { eviocgled(fd, led_vals.as_mut_slice())? };
unsafe { raw::eviocgled(fd, led_vals.as_mut_raw_slice())? };
}
Ok(())
@ -610,19 +633,21 @@ impl Device {
let old_state = self.state.clone();
self.sync_state()?;
let time = into_timeval(&SystemTime::now()).unwrap();
let time = systime_to_timeval(&SystemTime::now());
if let (Some(supported_keys), Some(key_vals)) = (&self.supported_keys, &self.state.key_vals)
if let (Some(supported_keys), Some(key_vals)) =
(&self.supported_keys, self.state.key_vals())
{
for (key_idx, key_supported) in supported_keys.iter().enumerate() {
if *key_supported
&& old_state.key_vals.as_ref().map(|v| v[key_idx]) != Some(key_vals[key_idx])
{
self.pending_events.push(raw::input_event {
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(libc::input_event {
time,
type_: Types::KEY.number(),
code: key_idx as u16,
value: if key_vals[key_idx] { 1 } else { 0 },
type_: EventType::KEY.0 as _,
code: key.code() as u16,
value: if key_vals.contains(key) { 1 } else { 0 },
});
}
}
@ -631,14 +656,11 @@ impl Device {
if let (Some(supported_abs), Some(abs_vals)) =
(self.supported_absolute, &self.state.abs_vals)
{
for idx in 0..AbsoluteAxis::MAX {
let abs = AbsoluteAxis::from_bits_truncate(1 << idx);
if supported_abs.contains(abs)
&& old_state.abs_vals.as_ref().map(|v| v[idx]) != Some(abs_vals[idx])
{
self.pending_events.push(raw::input_event {
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(libc::input_event {
time,
type_: Types::ABSOLUTE.number(),
type_: EventType::ABSOLUTE.0 as _,
code: idx as u16,
value: abs_vals[idx].value,
});
@ -649,14 +671,11 @@ impl Device {
if let (Some(supported_switch), Some(switch_vals)) =
(self.supported_switch, &self.state.switch_vals)
{
for idx in 0..Switch::MAX {
let sw = Switch::from_bits(1 << idx).unwrap();
if supported_switch.contains(sw)
&& old_state.switch_vals.as_ref().map(|v| v[idx]) != Some(switch_vals[idx])
{
self.pending_events.push(raw::input_event {
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(libc::input_event {
time,
type_: Types::SWITCH.number(),
type_: EventType::SWITCH.0 as _,
code: idx as u16,
value: if switch_vals[idx] { 1 } else { 0 },
});
@ -665,14 +684,11 @@ impl Device {
}
if let (Some(supported_led), Some(led_vals)) = (self.supported_led, &self.state.led_vals) {
for idx in 0..Led::MAX {
let led = Led::from_bits_truncate(1 << idx);
if supported_led.contains(led)
&& old_state.led_vals.as_ref().map(|v| v[idx]) != Some(led_vals[idx])
{
self.pending_events.push(raw::input_event {
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(libc::input_event {
time,
type_: Types::LED.number(),
type_: EventType::LED.0 as _,
code: idx as u16,
value: if led_vals[idx] { 1 } else { 0 },
});
@ -680,9 +696,9 @@ impl Device {
}
}
self.pending_events.push(raw::input_event {
self.pending_events.push(libc::input_event {
time,
type_: Types::SYNCHRONIZATION.number(),
type_: EventType::SYNCHRONIZATION.0 as _,
code: SYN_REPORT as u16,
value: 0,
});
@ -705,7 +721,7 @@ impl Device {
Ok(bytes_read) => unsafe {
let pre_len = buf.len();
buf.set_len(
pre_len + (bytes_read as usize / mem::size_of::<raw::input_event>()),
pre_len + (bytes_read as usize / mem::size_of::<libc::input_event>()),
);
},
Err(e) => {
@ -763,11 +779,91 @@ impl<'a> Drop for RawEvents<'a> {
}
impl<'a> Iterator for RawEvents<'a> {
type Item = raw::input_event;
type Item = InputEvent;
#[inline(always)]
fn next(&mut self) -> Option<raw::input_event> {
self.0.pending_events.pop()
fn next(&mut self) -> Option<InputEvent> {
self.0.pending_events.pop().map(InputEvent)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum InputEventKind {
Synchronization,
Key(Key),
RelAxis(RelativeAxisType),
AbsAxis(AbsoluteAxisType),
Misc(MiscType),
Switch(SwitchType),
Led(LedType),
Sound(SoundType),
Other,
}
#[repr(transparent)]
pub struct InputEvent(libc::input_event);
impl InputEvent {
#[inline]
pub fn timestamp(&self) -> SystemTime {
timeval_to_systime(&self.0.time)
}
#[inline]
pub fn event_type(&self) -> EventType {
EventType(self.0.type_)
}
#[inline]
pub fn code(&self) -> u16 {
self.0.code
}
/// A convenience function to return `self.code()` wrapped in a certain newtype determined by
/// the type of this event.
#[inline]
pub fn kind(&self) -> InputEventKind {
let code = self.code();
match self.event_type() {
EventType::SYNCHRONIZATION => InputEventKind::Synchronization,
EventType::KEY => InputEventKind::Key(Key::new(code)),
EventType::RELATIVE => InputEventKind::RelAxis(RelativeAxisType(code)),
EventType::ABSOLUTE => InputEventKind::AbsAxis(AbsoluteAxisType(code)),
EventType::MISC => InputEventKind::Misc(MiscType(code)),
EventType::SWITCH => InputEventKind::Switch(SwitchType(code)),
EventType::LED => InputEventKind::Led(LedType(code)),
EventType::SOUND => InputEventKind::Sound(SoundType(code)),
_ => InputEventKind::Other,
}
}
#[inline]
pub fn value(&self) -> i32 {
self.0.value
}
pub fn from_raw(raw: libc::input_event) -> Self {
Self(raw)
}
pub fn as_raw(&self) -> &libc::input_event {
&self.0
}
}
impl fmt::Debug for InputEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug = f.debug_struct("InputEvent");
debug.field("time", &self.timestamp());
let kind = self.kind();
if let InputEventKind::Other = kind {
debug
.field("type", &self.event_type())
.field("code", &self.code());
} else {
debug.field("kind", &kind);
}
debug.field("value", &self.value()).finish()
}
}
@ -799,13 +895,25 @@ impl Iterator for EnumerateDevices {
}
/// A safe Rust version of clock_gettime against CLOCK_REALTIME
fn into_timeval(time: &SystemTime) -> Result<libc::timeval, std::time::SystemTimeError> {
let now_duration = time.duration_since(SystemTime::UNIX_EPOCH)?;
fn systime_to_timeval(time: &SystemTime) -> libc::timeval {
let (sign, dur) = match time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(dur) => (1, dur),
Err(e) => (-1, e.duration()),
};
Ok(libc::timeval {
tv_sec: now_duration.as_secs() as libc::time_t,
tv_usec: now_duration.subsec_micros() as libc::suseconds_t,
})
libc::timeval {
tv_sec: dur.as_secs() as libc::time_t * sign,
tv_usec: dur.subsec_micros() as libc::suseconds_t,
}
}
fn timeval_to_systime(tv: &libc::timeval) -> SystemTime {
let dur = Duration::new(tv.tv_sec.abs() as u64, tv.tv_usec as u32 * 1000);
if tv.tv_sec >= 0 {
SystemTime::UNIX_EPOCH + dur
} else {
SystemTime::UNIX_EPOCH - dur
}
}
/// A copy of the unstable Vec::spare_capacity_mut

View file

@ -1,25 +1,14 @@
use libc::c_int;
pub use libc::{
ff_condition_effect, ff_constant_effect, ff_effect, ff_envelope, ff_periodic_effect,
ff_ramp_effect, ff_replay, ff_rumble_effect, ff_trigger, input_absinfo, input_event, input_id,
input_keymap_entry,
};
use libc::{ff_effect, input_absinfo, input_id};
// use libc::{
// ff_condition_effect, ff_constant_effect, ff_envelope, ff_periodic_effect, ff_ramp_effect,
// ff_replay, ff_rumble_effect, ff_trigger, input_event, input_keymap_entry,
// };
use nix::{
convert_ioctl_res, ioctl_read, ioctl_read_buf, ioctl_write_int, ioctl_write_ptr,
request_code_read,
};
pub(crate) const fn input_absinfo_default() -> input_absinfo {
input_absinfo {
value: 0,
minimum: 0,
maximum: 0,
fuzz: 0,
flat: 0,
resolution: 0,
}
}
ioctl_read!(eviocgeffects, b'E', 0x84, ::libc::c_int);
ioctl_read!(eviocgid, b'E', 0x02, /*struct*/ input_id);
ioctl_read!(eviocgkeycode, b'E', 0x04, [::libc::c_uint; 2]);
@ -35,7 +24,7 @@ ioctl_write_ptr!(eviocsrep, b'E', 0x03, [::libc::c_uint; 2]);
ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
ioctl_read_buf!(eviocgphys, b'E', 0x07, u8);
ioctl_read_buf!(eviocguniq, b'E', 0x08, u8);
ioctl_read!(eviocgprop, b'E', 0x09, u32);
ioctl_read_buf!(eviocgprop, b'E', 0x09, u8);
ioctl_read_buf!(eviocgmtslots, b'E', 0x0a, u8);
ioctl_read_buf!(eviocgkey, b'E', 0x18, u8);
ioctl_read_buf!(eviocgled, b'E', 0x19, u8);
@ -49,21 +38,21 @@ ioctl_write_int!(eviocsclockid, b'E', 0xa0);
macro_rules! eviocgbit_ioctl {
($mac:ident!($name:ident, $ev:ident, $ty:ty)) => {
eviocgbit_ioctl!($mac!($name, $crate::Types::$ev.number::<u32>(), $ty));
eviocgbit_ioctl!($mac!($name, $crate::EventType::$ev.0, $ty));
};
($mac:ident!($name:ident, $ev:expr, $ty:ty)) => {
$mac!($name, b'E', 0x20 + $ev, $ty);
};
}
eviocgbit_ioctl!(ioctl_read!(eviocgbit_type, 0, u32));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_type, 0, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_key, KEY, u8));
eviocgbit_ioctl!(ioctl_read!(eviocgbit_relative, RELATIVE, u32));
eviocgbit_ioctl!(ioctl_read!(eviocgbit_absolute, ABSOLUTE, u64));
eviocgbit_ioctl!(ioctl_read!(eviocgbit_misc, MISC, u32));
eviocgbit_ioctl!(ioctl_read!(eviocgbit_switch, SWITCH, u32));
eviocgbit_ioctl!(ioctl_read!(eviocgbit_led, LED, u32));
eviocgbit_ioctl!(ioctl_read!(eviocgbit_sound, SOUND, u32));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_relative, RELATIVE, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_absolute, ABSOLUTE, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_misc, MISC, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_switch, SWITCH, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_led, LED, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_sound, SOUND, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_repeat, REPEAT, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_ff, FORCEFEEDBACK, u8));
eviocgbit_ioctl!(ioctl_read_buf!(eviocgbit_power, POWER, u8));

View file

@ -1,45 +1,26 @@
use std::fmt;
/// Scancodes for key presses.
///
/// Each associated constant for this struct represents a distinct key.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Key(u32);
pub struct Key(u16);
impl Key {
#[inline]
pub const fn new(code: u32) -> Self {
pub const fn new(code: u16) -> Self {
Self(code)
}
#[inline]
pub const fn code(self) -> u32 {
pub const fn code(self) -> u16 {
self.0
}
// This needs to be a multiple of 8, otherwise we fetch keys we can't process
/// The highest key index that will be fetched by Device
pub const MAX: usize = 0x300;
pub(crate) const COUNT: usize = 0x300;
}
macro_rules! key_consts {
($($c:ident = $val:expr,)*) => {
impl Key {
$(const $c: Self = Self($val);)*
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
$(Self::$c => f.pad(stringify!($c)),)*
_ => write!(f, "unknown key: {}", self.0),
}
}
}
}
}
key_consts!(
evdev_enum!(
Key,
KEY_RESERVED = 0,
KEY_ESC = 1,
KEY_1 = 2,