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:
parent
7da53b57fe
commit
3f32c41fc0
6 changed files with 717 additions and 550 deletions
|
@ -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
65
src/attribute_set.rs
Normal 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 _
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
401
src/constants.rs
401
src/constants.rs
|
@ -1,284 +1,311 @@
|
|||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
/// Event types supported by the device.
|
||||
pub struct Types: u32 {
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct EventType(pub u16);
|
||||
|
||||
evdev_enum!(
|
||||
EventType,
|
||||
/// A bookkeeping event. Usually not important to applications.
|
||||
const SYNCHRONIZATION = 1 << 0x00;
|
||||
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.
|
||||
const KEY = 1 << 0x01;
|
||||
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.
|
||||
const RELATIVE = 1 << 0x02;
|
||||
RELATIVE = 0x02,
|
||||
/// Movement on an absolute axis. Used for things such as touch events and joysticks.
|
||||
const ABSOLUTE = 1 << 0x03;
|
||||
ABSOLUTE = 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;
|
||||
MISC = 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;
|
||||
SWITCH = 0x05,
|
||||
/// An LED was toggled.
|
||||
const LED = 1 << 0x11;
|
||||
LED = 0x11,
|
||||
/// A sound was made.
|
||||
const SOUND = 1 << 0x12;
|
||||
SOUND = 0x12,
|
||||
/// There are no events of this type, to my knowledge, but represents metadata about key
|
||||
/// repeat configuration.
|
||||
const REPEAT = 1 << 0x14;
|
||||
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.
|
||||
const FORCEFEEDBACK = 1 << 0x15;
|
||||
FORCEFEEDBACK = 0x15,
|
||||
/// I think this is unused?
|
||||
const POWER = 1 << 0x16;
|
||||
POWER = 0x16,
|
||||
/// A force feedback effect's state changed.
|
||||
const FORCEFEEDBACKSTATUS = 1 << 0x17;
|
||||
}
|
||||
FORCEFEEDBACKSTATUS = 0x17,
|
||||
);
|
||||
|
||||
impl EventType {
|
||||
pub(crate) const COUNT: usize = 0x20;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Device properties.
|
||||
pub struct Props: u32 {
|
||||
#[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.
|
||||
const POINTER = 1 << 0x00;
|
||||
POINTER = 0x00,
|
||||
/// "direct input devices", according to the header.
|
||||
const DIRECT = 1 << 0x01;
|
||||
DIRECT = 0x01,
|
||||
/// "has button(s) under pad", according to the header.
|
||||
const BUTTONPAD = 1 << 0x02;
|
||||
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).
|
||||
const SEMI_MT = 1 << 0x03;
|
||||
SEMI_MT = 0x03,
|
||||
/// "softbuttons at top of pad", according to the header.
|
||||
const TOPBUTTONPAD = 1 << 0x04;
|
||||
TOPBUTTONPAD = 0x04,
|
||||
/// Is a pointing stick ("nub" etc, https://xkcd.com/243/)
|
||||
const POINTING_STICK = 1 << 0x05;
|
||||
POINTING_STICK = 0x05,
|
||||
/// Has an accelerometer. Probably reports relative events in that case?
|
||||
const ACCELEROMETER = 1 << 0x06;
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
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"
|
||||
const ABS_MT_SLOT = 1 << 0x2f;
|
||||
ABS_MT_SLOT = 0x2f,
|
||||
/// "Major axis of touching ellipse"
|
||||
const ABS_MT_TOUCH_MAJOR = 1 << 0x30;
|
||||
ABS_MT_TOUCH_MAJOR = 0x30,
|
||||
/// "Minor axis (omit if circular)"
|
||||
const ABS_MT_TOUCH_MINOR = 1 << 0x31;
|
||||
ABS_MT_TOUCH_MINOR = 0x31,
|
||||
/// "Major axis of approaching ellipse"
|
||||
const ABS_MT_WIDTH_MAJOR = 1 << 0x32;
|
||||
ABS_MT_WIDTH_MAJOR = 0x32,
|
||||
/// "Minor axis (omit if circular)"
|
||||
const ABS_MT_WIDTH_MINOR = 1 << 0x33;
|
||||
ABS_MT_WIDTH_MINOR = 0x33,
|
||||
/// "Ellipse orientation"
|
||||
const ABS_MT_ORIENTATION = 1 << 0x34;
|
||||
ABS_MT_ORIENTATION = 0x34,
|
||||
/// "Center X touch position"
|
||||
const ABS_MT_POSITION_X = 1 << 0x35;
|
||||
ABS_MT_POSITION_X = 0x35,
|
||||
/// "Center Y touch position"
|
||||
const ABS_MT_POSITION_Y = 1 << 0x36;
|
||||
ABS_MT_POSITION_Y = 0x36,
|
||||
/// "Type of touching device"
|
||||
const ABS_MT_TOOL_TYPE = 1 << 0x37;
|
||||
ABS_MT_TOOL_TYPE = 0x37,
|
||||
/// "Group a set of packets as a blob"
|
||||
const ABS_MT_BLOB_ID = 1 << 0x38;
|
||||
ABS_MT_BLOB_ID = 0x38,
|
||||
/// "Unique ID of the initiated contact"
|
||||
const ABS_MT_TRACKING_ID = 1 << 0x39;
|
||||
ABS_MT_TRACKING_ID = 0x39,
|
||||
/// "Pressure on contact area"
|
||||
const ABS_MT_PRESSURE = 1 << 0x3a;
|
||||
ABS_MT_PRESSURE = 0x3a,
|
||||
/// "Contact over distance"
|
||||
const ABS_MT_DISTANCE = 1 << 0x3b;
|
||||
ABS_MT_DISTANCE = 0x3b,
|
||||
/// "Center X tool position"
|
||||
const ABS_MT_TOOL_X = 1 << 0x3c;
|
||||
ABS_MT_TOOL_X = 0x3c,
|
||||
/// "Center Y tool position"
|
||||
const ABS_MT_TOOL_Y = 1 << 0x3d;
|
||||
}
|
||||
ABS_MT_TOOL_Y = 0x3d,
|
||||
);
|
||||
|
||||
impl AbsoluteAxisType {
|
||||
pub(crate) const COUNT: usize = 0x40;
|
||||
}
|
||||
|
||||
impl AbsoluteAxis {
|
||||
pub(crate) const MAX: usize = 0x3f;
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct SwitchType(pub u16);
|
||||
|
||||
bitflags! {
|
||||
pub struct Switch: u32 {
|
||||
evdev_enum!(
|
||||
SwitchType,
|
||||
/// "set = lid shut"
|
||||
const SW_LID = 1 << 0x00;
|
||||
SW_LID = 0x00,
|
||||
/// "set = tablet mode"
|
||||
const SW_TABLET_MODE = 1 << 0x01;
|
||||
SW_TABLET_MODE = 0x01,
|
||||
/// "set = inserted"
|
||||
const SW_HEADPHONE_INSERT = 1 << 0x02;
|
||||
SW_HEADPHONE_INSERT = 0x02,
|
||||
/// "rfkill master switch, type 'any'"
|
||||
const SW_RFKILL_ALL = 1 << 0x03;
|
||||
SW_RFKILL_ALL = 0x03,
|
||||
/// "set = inserted"
|
||||
const SW_MICROPHONE_INSERT = 1 << 0x04;
|
||||
SW_MICROPHONE_INSERT = 0x04,
|
||||
/// "set = plugged into doc"
|
||||
const SW_DOCK = 1 << 0x05;
|
||||
SW_DOCK = 0x05,
|
||||
/// "set = inserted"
|
||||
const SW_LINEOUT_INSERT = 1 << 0x06;
|
||||
SW_LINEOUT_INSERT = 0x06,
|
||||
/// "set = mechanical switch set"
|
||||
const SW_JACK_PHYSICAL_INSERT = 1 << 0x07;
|
||||
SW_JACK_PHYSICAL_INSERT = 0x07,
|
||||
/// "set = inserted"
|
||||
const SW_VIDEOOUT_INSERT = 1 << 0x08;
|
||||
SW_VIDEOOUT_INSERT = 0x08,
|
||||
/// "set = lens covered"
|
||||
const SW_CAMERA_LENS_COVER = 1 << 0x09;
|
||||
SW_CAMERA_LENS_COVER = 0x09,
|
||||
/// "set = keypad slide out"
|
||||
const SW_KEYPAD_SLIDE = 1 << 0x0a;
|
||||
SW_KEYPAD_SLIDE = 0x0a,
|
||||
/// "set = front proximity sensor active"
|
||||
const SW_FRONT_PROXIMITY = 1 << 0x0b;
|
||||
SW_FRONT_PROXIMITY = 0x0b,
|
||||
/// "set = rotate locked/disabled"
|
||||
const SW_ROTATE_LOCK = 1 << 0x0c;
|
||||
SW_ROTATE_LOCK = 0x0c,
|
||||
/// "set = inserted"
|
||||
const SW_LINEIN_INSERT = 1 << 0x0d;
|
||||
SW_LINEIN_INSERT = 0x0d,
|
||||
/// "set = device disabled"
|
||||
const SW_MUTE_DEVICE = 1 << 0x0e;
|
||||
SW_MUTE_DEVICE = 0x0e,
|
||||
/// "set = pen inserted"
|
||||
const SW_PEN_INSERTED = 1 << 0x0f;
|
||||
}
|
||||
SW_PEN_INSERTED = 0x0f,
|
||||
/// "set = cover closed"
|
||||
SW_MACHINE_COVER = 0x10,
|
||||
);
|
||||
|
||||
impl SwitchType {
|
||||
pub(crate) const COUNT: usize = 0x11;
|
||||
}
|
||||
|
||||
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;
|
||||
#[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"
|
||||
const LED_SLEEP = 1 << 0x05;
|
||||
const LED_SUSPEND = 1 << 0x06;
|
||||
const LED_MUTE = 1 << 0x07;
|
||||
LED_SLEEP = 0x05,
|
||||
LED_SUSPEND = 0x06,
|
||||
LED_MUTE = 0x07,
|
||||
/// "Generic indicator"
|
||||
const LED_MISC = 1 << 0x08;
|
||||
LED_MISC = 0x08,
|
||||
/// "Message waiting"
|
||||
const LED_MAIL = 1 << 0x09;
|
||||
LED_MAIL = 0x09,
|
||||
/// "External power connected"
|
||||
const LED_CHARGING = 1 << 0x0a;
|
||||
}
|
||||
LED_CHARGING = 0x0a,
|
||||
);
|
||||
|
||||
impl LedType {
|
||||
pub(crate) const COUNT: usize = 0x10;
|
||||
}
|
||||
|
||||
impl Led {
|
||||
pub(crate) const MAX: usize = 0x10;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Various miscellaneous event types. Current as of kernel 4.1.
|
||||
pub struct Misc: u32 {
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct MiscType(pub u16);
|
||||
|
||||
evdev_enum!(
|
||||
MiscType,
|
||||
/// Serial number, only exported for tablets ("Transducer Serial Number")
|
||||
const MSC_SERIAL = 1 << 0x00;
|
||||
MSC_SERIAL = 0x00,
|
||||
/// Only used by the PowerMate driver, right now.
|
||||
const MSC_PULSELED = 1 << 0x01;
|
||||
MSC_PULSELED = 0x01,
|
||||
/// Completely unused.
|
||||
const MSC_GESTURE = 1 << 0x02;
|
||||
MSC_GESTURE = 0x02,
|
||||
/// "Raw" event, rarely used.
|
||||
const MSC_RAW = 1 << 0x03;
|
||||
MSC_RAW = 0x03,
|
||||
/// Key scancode
|
||||
const MSC_SCAN = 1 << 0x04;
|
||||
MSC_SCAN = 0x04,
|
||||
/// Completely unused.
|
||||
const MSC_TIMESTAMP = 1 << 0x05;
|
||||
}
|
||||
MSC_TIMESTAMP = 0x05,
|
||||
);
|
||||
|
||||
impl MiscType {
|
||||
pub(crate) const COUNT: usize = 0x08;
|
||||
}
|
||||
|
||||
// impl Misc {
|
||||
// const MAX: usize = 0x07;
|
||||
// pub enum FFStatusDataIndex {
|
||||
// FF_STATUS_STOPPED = 0x00,
|
||||
// FF_STATUS_PLAYING = 0x01,
|
||||
// }
|
||||
|
||||
bitflags! {
|
||||
pub struct FFStatus: u32 {
|
||||
const FF_STATUS_STOPPED = 1 << 0x00;
|
||||
const FF_STATUS_PLAYING = 1 << 0x01;
|
||||
}
|
||||
}
|
||||
// #[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,
|
||||
// }
|
||||
|
||||
#[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,
|
||||
}
|
||||
// impl FFEffect {
|
||||
// // Needs to be a multiple of 8
|
||||
// pub const COUNT: usize = 0x80;
|
||||
// }
|
||||
|
||||
impl FFEffect {
|
||||
// Needs to be a multiple of 8
|
||||
pub const MAX: usize = 0x80;
|
||||
}
|
||||
// #[derive(Copy, Clone, PartialEq, Eq)]
|
||||
// pub struct RepeatType(pub u16);
|
||||
|
||||
bitflags! {
|
||||
pub struct Repeat: u32 {
|
||||
const REP_DELAY = 1 << 0x00;
|
||||
const REP_PERIOD = 1 << 0x01;
|
||||
}
|
||||
}
|
||||
// evdev_enum!(RepeatType, REP_DELAY = 0x00, REP_PERIOD = 0x01,);
|
||||
|
||||
bitflags! {
|
||||
pub struct Sound: u32 {
|
||||
const SND_CLICK = 1 << 0x00;
|
||||
const SND_BELL = 1 << 0x01;
|
||||
const SND_TONE = 1 << 0x02;
|
||||
}
|
||||
// impl RepeatType {
|
||||
// pub(crate) const COUNT: usize = 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;
|
||||
}
|
||||
|
|
548
src/lib.rs
548
src/lib.rs
|
@ -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 {
|
||||
for key in supported_keys.iter() {
|
||||
let key_idx = key.code() as usize;
|
||||
writeln!(
|
||||
f,
|
||||
" {:?} ({}index {})",
|
||||
Key::new(key_idx as u32),
|
||||
if *key_enabled { "pressed, " } else { "" },
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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])?;
|
||||
}
|
||||
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
|
||||
|
|
39
src/raw.rs
39
src/raw.rs
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue