Fix all the current issues:

Changes in Rust, changes in the evdev ABI, and a bug
This commit is contained in:
Corey Richardson 2017-04-27 19:42:45 -04:00
parent ee5bab2689
commit 60a7523d4b
No known key found for this signature in database
GPG key ID: A89E9F8FB71E9B8B
4 changed files with 49 additions and 48 deletions

View file

@ -1,19 +1,19 @@
[package] [package]
name = "evdev" name = "evdev"
version = "0.9.2" version = "0.9.3"
authors = ["Corey Richardson <corey@octayn.net>"] authors = ["Corey Richardson <corey@octayn.net>"]
description = "evdev interface for Linux" description = "evdev interface for Linux"
license = "BSL-1.0/Apache-2.0" license = "Apache-2.0 OR MIT"
repository = "https://github.com/cmr/evdev" repository = "https://github.com/cmr/evdev"
documentation = "https://cmr.github.io/evdev" documentation = "https://docs.rs/evdev"
[dependencies] [dependencies]
ioctl = "0.3.3" ioctl = "0.3.3"
bitflags = "*" bitflags = "0.8.2"
errno = "*" errno = "0.2.3"
libc = "*" libc = "0.2.22"
fixedbitset = "*" fixedbitset = "0.1.6"
num = "*" num = "0.1.37"
[features] [features]
unstable = [] unstable = []

View file

@ -4,14 +4,16 @@
[![Travis](https://img.shields.io/travis/cmr/evdev.svg?style=flat-square)](https://travis-ci.org/cmr/evdev) [![Travis](https://img.shields.io/travis/cmr/evdev.svg?style=flat-square)](https://travis-ci.org/cmr/evdev)
[![Crates.io](https://img.shields.io/crates/v/evdev.svg?style=flat-square)](https://crates.io/crates/evdev) [![Crates.io](https://img.shields.io/crates/v/evdev.svg?style=flat-square)](https://crates.io/crates/evdev)
[Documentation](https://cmr.github.io/evdev) [Documentation](https://docs.rs/evdev)
Nice(r) access to `evdev`. Works on Rust >= 1.2.0. Nice(r) access to `evdev` devices.
What is `evdev`? What is `evdev`?
=================== ===================
`evdev` is the Linux kernel's generic input interface. `evdev` is the Linux kernel's generic input interface. This crate exposes
access to these sorts of input devices. There is some trickery involved, so
please read the crate documentation.
What does this library support? What does this library support?
=============================== ===============================
@ -23,6 +25,10 @@ closely, where possible.
Writing to devices is not yet supported (eg, turning LEDs on). Writing to devices is not yet supported (eg, turning LEDs on).
There is no abstraction for gamepad-like devices that allows mapping button
numbers to logical buttons, nor is one planned. Such a thing should take place
in a higher-level crate, likely supporting multiple platforms.
Example Example
======= =======

View file

@ -18,7 +18,7 @@
//! `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 //! As the state changes, the kernel will write events into a ring buffer. The application can read
//! from this ring buffer, thus retreiving events. However, if the ring buffer becomes full, the //! from this ring buffer, thus retrieving events. However, if the ring buffer becomes full, the
//! kernel will *drop* every event in the ring buffer and leave an event telling userspace that it //! kernel will *drop* every event in the ring buffer and leave an event telling userspace that it
//! did so. At this point, if the application were using the events it received to update its //! did so. At this point, if the application were using the events it received to update its
//! internal idea of what state the hardware device is in, it will be wrong: it is missing some //! internal idea of what state the hardware device is in, it will be wrong: it is missing some
@ -32,6 +32,7 @@
//! fd returned by `Device::fd` to process events when they are ready. //! fd returned by `Device::fd` to process events when they are ready.
#![cfg(any(target_os = "linux", target_os = "android"))] #![cfg(any(target_os = "linux", target_os = "android"))]
#![allow(non_camel_case_types)]
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
@ -47,6 +48,7 @@ use std::path::Path;
use std::ffi::CString; use std::ffi::CString;
use std::mem::size_of; use std::mem::size_of;
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
use num::traits::WrappingSub;
pub use Key::*; pub use Key::*;
pub use FFEffect::*; pub use FFEffect::*;
@ -101,7 +103,7 @@ impl std::ops::Deref for Fd {
bitflags! { bitflags! {
/// Event types supported by the device. /// Event types supported by the device.
flags Types: u32 { pub flags Types: u32 {
/// A bookkeeping event. Usually not important to applications. /// A bookkeeping event. Usually not important to applications.
const SYNCHRONIZATION = 1 << 0x00, const SYNCHRONIZATION = 1 << 0x00,
/// A key changed state. A key, or button, is usually a momentary switch (in the circuit sense). It has two /// A key changed state. A key, or button, is usually a momentary switch (in the circuit sense). It has two
@ -140,7 +142,7 @@ bitflags! {
bitflags! { bitflags! {
/// Device properties. /// Device properties.
flags Props: u32 { pub flags Props: u32 {
/// This input device needs a pointer ("cursor") for the user to know its state. /// This input device needs a pointer ("cursor") for the user to know its state.
const POINTER = 1 << 0x00, const POINTER = 1 << 0x00,
/// "direct input devices", according to the header. /// "direct input devices", according to the header.
@ -162,7 +164,7 @@ bitflags! {
include!("scancodes.rs"); // it's a huge glob of text that I'm tired of skipping over. include!("scancodes.rs"); // it's a huge glob of text that I'm tired of skipping over.
bitflags! { bitflags! {
flags RelativeAxis: u32 { pub flags RelativeAxis: u32 {
const REL_X = 1 << 0x00, const REL_X = 1 << 0x00,
const REL_Y = 1 << 0x01, const REL_Y = 1 << 0x01,
const REL_Z = 1 << 0x02, const REL_Z = 1 << 0x02,
@ -177,7 +179,7 @@ bitflags! {
} }
bitflags! { bitflags! {
flags AbsoluteAxis: u64 { pub flags AbsoluteAxis: u64 {
const ABS_X = 1 << 0x00, const ABS_X = 1 << 0x00,
const ABS_Y = 1 << 0x01, const ABS_Y = 1 << 0x01,
const ABS_Z = 1 << 0x02, const ABS_Z = 1 << 0x02,
@ -234,12 +236,11 @@ bitflags! {
const ABS_MT_TOOL_X = 1 << 0x3c, const ABS_MT_TOOL_X = 1 << 0x3c,
/// "Center Y tool position" /// "Center Y tool position"
const ABS_MT_TOOL_Y = 1 << 0x3d, const ABS_MT_TOOL_Y = 1 << 0x3d,
const ABS_MAX = 1 << 0x3f,
} }
} }
bitflags! { bitflags! {
flags Switch: u32 { pub flags Switch: u32 {
/// "set = lid shut" /// "set = lid shut"
const SW_LID = 1 << 0x00, const SW_LID = 1 << 0x00,
/// "set = tablet mode" /// "set = tablet mode"
@ -270,16 +271,19 @@ bitflags! {
const SW_LINEIN_INSERT = 1 << 0x0d, const SW_LINEIN_INSERT = 1 << 0x0d,
/// "set = device disabled" /// "set = device disabled"
const SW_MUTE_DEVICE = 1 << 0x0e, const SW_MUTE_DEVICE = 1 << 0x0e,
const SW_MAX = 1 << 0x0f, /// "set = pen inserted"
const SW_PEN_INSERTED = 1 << 0x0f,
const SW_MAX = 0xf,
} }
} }
bitflags! { bitflags! {
/// LEDs specified by USB HID. /// LEDs specified by USB HID.
flags Led: u32 { pub flags Led: u32 {
const LED_NUML = 1 << 0x00, const LED_NUML = 1 << 0x00,
const LED_CAPSL = 1 << 0x01, const LED_CAPSL = 1 << 0x01,
const LED_SCROLLL = 1 << 0x02, const LED_SCROLLL = 1 << 0x02,
const LED_COMPOSE = 1 << 0x03,
const LED_KANA = 1 << 0x04, const LED_KANA = 1 << 0x04,
/// "Stand-by" /// "Stand-by"
const LED_SLEEP = 1 << 0x05, const LED_SLEEP = 1 << 0x05,
@ -297,7 +301,7 @@ bitflags! {
bitflags! { bitflags! {
/// Various miscellaneous event types. Current as of kernel 4.1. /// Various miscellaneous event types. Current as of kernel 4.1.
flags Misc: u32 { pub flags Misc: u32 {
/// Serial number, only exported for tablets ("Transducer Serial Number") /// Serial number, only exported for tablets ("Transducer Serial Number")
const MSC_SERIAL = 1 << 0x00, const MSC_SERIAL = 1 << 0x00,
/// Only used by the PowerMate driver, right now. /// Only used by the PowerMate driver, right now.
@ -315,7 +319,7 @@ bitflags! {
} }
bitflags! { bitflags! {
flags FFStatus: u32 { pub flags FFStatus: u32 {
const FF_STATUS_STOPPED = 1 << 0x00, const FF_STATUS_STOPPED = 1 << 0x00,
const FF_STATUS_PLAYING = 1 << 0x01, const FF_STATUS_PLAYING = 1 << 0x01,
} }
@ -344,14 +348,14 @@ pub enum FFEffect {
} }
bitflags! { bitflags! {
flags Repeat: u32 { pub flags Repeat: u32 {
const REP_DELAY = 1 << 0x00, const REP_DELAY = 1 << 0x00,
const REP_PERIOD = 1 << 0x01, const REP_PERIOD = 1 << 0x01,
} }
} }
bitflags! { bitflags! {
flags Sound: u32 { pub flags Sound: u32 {
const SND_CLICK = 1 << 0x00, const SND_CLICK = 1 << 0x00,
const SND_BELL = 1 << 0x01, const SND_BELL = 1 << 0x01,
const SND_TONE = 1 << 0x02, const SND_TONE = 1 << 0x02,
@ -366,14 +370,9 @@ macro_rules! impl_number {
/// mode, /// mode,
#[inline(always)] #[inline(always)]
pub fn number<T: num::FromPrimitive>(&self) -> T { pub fn number<T: num::FromPrimitive>(&self) -> T {
if cfg!(debug_assertions) { let val = self.bits().trailing_zeros();
let val = ffs(self.bits()); debug_assert!(self.bits() == 1 << val, "{:?} ought to have only one flag set to be used with .number()", self);
self.bits() == val; // hack to induce the constraint typeof(self.bits()) = typeof(val) T::from_u32(val).unwrap()
if self.bits() != 1 << val {
panic!("{:?} ought to have only one flag set to be used with .number()", self);
}
}
T::from_u32(ffs(self.bits())).unwrap()
} }
})* })*
} }
@ -392,7 +391,6 @@ pub enum Synchronization {
SYN_MT_REPORT = 2, SYN_MT_REPORT = 2,
/// Ring buffer filled, events were dropped. /// Ring buffer filled, events were dropped.
SYN_DROPPED = 3, SYN_DROPPED = 3,
SYN_MAX = 0xf,
} }
#[derive(Clone)] #[derive(Clone)]
@ -460,7 +458,7 @@ impl std::fmt::Debug for Device {
} }
if self.ty.contains(ABSOLUTE) { if self.ty.contains(ABSOLUTE) {
ds.field("abs", &self.abs); ds.field("abs", &self.abs);
for idx in (0..0x3f) { for idx in 0..0x3f {
let abs = 1 << idx; let abs = 1 << idx;
// ignore multitouch, we'll handle that later. // ignore multitouch, we'll handle that later.
if (self.abs.bits() & abs) == 1 { if (self.abs.bits() & abs) == 1 {
@ -546,7 +544,7 @@ impl std::fmt::Display for Device {
if self.ty.contains(KEY) { if self.ty.contains(KEY) {
try!(writeln!(f, " Keys supported:")); try!(writeln!(f, " Keys supported:"));
for key_idx in (0..self.key_bits.len()) { for key_idx in 0..self.key_bits.len() {
if self.key_bits.contains(key_idx) { if self.key_bits.contains(key_idx) {
// Cross our fingers... (what did this mean?) // Cross our fingers... (what did this mean?)
try!(writeln!(f, " {:?} ({}index {})", try!(writeln!(f, " {:?} ({}index {})",
@ -561,7 +559,7 @@ impl std::fmt::Display for Device {
} }
if self.ty.contains(ABSOLUTE) { if self.ty.contains(ABSOLUTE) {
try!(writeln!(f, " Absolute Axes:")); try!(writeln!(f, " Absolute Axes:"));
for idx in (0..0x3f) { for idx in 0..0x3f {
let abs = 1<< idx; let abs = 1<< idx;
if self.abs.bits() & abs != 0 { if self.abs.bits() & abs != 0 {
// FIXME: abs val Debug is gross // FIXME: abs val Debug is gross
@ -577,7 +575,7 @@ impl std::fmt::Display for Device {
} }
if self.ty.contains(SWITCH) { if self.ty.contains(SWITCH) {
try!(writeln!(f, " Switches:")); try!(writeln!(f, " Switches:"));
for idx in (0..0xf) { for idx in 0..0xf {
let sw = 1 << idx; let sw = 1 << idx;
if sw < SW_MAX.bits() && self.switch.bits() & sw == 1 { if sw < SW_MAX.bits() && self.switch.bits() & sw == 1 {
try!(writeln!(f, " {:?} ({:?}, index {})", try!(writeln!(f, " {:?} ({:?}, index {})",
@ -589,7 +587,7 @@ impl std::fmt::Display for Device {
} }
if self.ty.contains(LED) { if self.ty.contains(LED) {
try!(writeln!(f, " LEDs:")); try!(writeln!(f, " LEDs:"));
for idx in (0..0xf) { for idx in 0..0xf {
let led = 1 << idx; let led = 1 << idx;
if led < LED_MAX.bits() && self.led.bits() & led == 1 { if led < LED_MAX.bits() && self.led.bits() & led == 1 {
try!(writeln!(f, " {:?} ({:?}, index {})", try!(writeln!(f, " {:?} ({:?}, index {})",
@ -625,10 +623,6 @@ impl Drop for Device {
} }
} }
fn ffs<T: num::FromPrimitive, U: num::ToPrimitive>(x: U) -> T {
T::from_u32(31 - U::to_u64(&x).unwrap().leading_zeros()).unwrap()
}
impl Device { impl Device {
pub fn fd(&self) -> RawFd { pub fn fd(&self) -> RawFd {
self.fd self.fd
@ -800,6 +794,7 @@ impl Device {
if dev.ty.contains(LED) { if dev.ty.contains(LED) {
do_ioctl!(eviocgbit(*fd, LED.number(), 0xf, &mut bits as *mut u32 as *mut u8)); do_ioctl!(eviocgbit(*fd, LED.number(), 0xf, &mut bits as *mut u32 as *mut u8));
println!("{:b}", bits);
dev.led = Led::from_bits(bits).expect("evdev: unexpected led bits! report a bug"); dev.led = Led::from_bits(bits).expect("evdev: unexpected led bits! report a bug");
} }
@ -829,7 +824,7 @@ impl Device {
do_ioctl!(eviocgkey(self.fd, self.state.key_vals.as_mut_slice().as_mut_ptr() as *mut u32 as *mut u8, self.state.key_vals.len())); do_ioctl!(eviocgkey(self.fd, self.state.key_vals.as_mut_slice().as_mut_ptr() as *mut u32 as *mut u8, self.state.key_vals.len()));
} }
if self.ty.contains(ABSOLUTE) { if self.ty.contains(ABSOLUTE) {
for idx in (0..0x28) { for idx in 0..0x28 {
let abs = 1 << idx; let abs = 1 << idx;
// ignore multitouch, we'll handle that later. // ignore multitouch, we'll handle that later.
if abs < ABS_MT_SLOT.bits() && self.abs.bits() & abs != 1 { if abs < ABS_MT_SLOT.bits() && self.abs.bits() & abs != 1 {
@ -885,7 +880,7 @@ impl Device {
}; };
if self.ty.contains(KEY) { if self.ty.contains(KEY) {
for key_idx in (0..self.key_bits.len()) { for key_idx in 0..self.key_bits.len() {
if self.key_bits.contains(key_idx) { if self.key_bits.contains(key_idx) {
if old_state.key_vals[key_idx] != self.state.key_vals[key_idx] { if old_state.key_vals[key_idx] != self.state.key_vals[key_idx] {
self.pending_events.push(ioctl::input_event { self.pending_events.push(ioctl::input_event {
@ -899,7 +894,7 @@ impl Device {
} }
} }
if self.ty.contains(ABSOLUTE) { if self.ty.contains(ABSOLUTE) {
for idx in (0..0x3f) { for idx in 0..0x3f {
let abs = 1 << idx; let abs = 1 << idx;
if self.abs.bits() & abs != 0 { if self.abs.bits() & abs != 0 {
if old_state.abs_vals[idx as usize] != self.state.abs_vals[idx as usize] { if old_state.abs_vals[idx as usize] != self.state.abs_vals[idx as usize] {
@ -914,7 +909,7 @@ impl Device {
} }
} }
if self.ty.contains(SWITCH) { if self.ty.contains(SWITCH) {
for idx in (0..0xf) { for idx in 0..0xf {
let sw = 1 << idx; let sw = 1 << idx;
if sw < SW_MAX.bits() && self.switch.bits() & sw == 1 { if sw < SW_MAX.bits() && self.switch.bits() & sw == 1 {
if old_state.switch_vals[idx as usize] != self.state.switch_vals[idx as usize] { if old_state.switch_vals[idx as usize] != self.state.switch_vals[idx as usize] {
@ -929,7 +924,7 @@ impl Device {
} }
} }
if self.ty.contains(LED) { if self.ty.contains(LED) {
for idx in (0..0xf) { for idx in 0..0xf {
let led = 1 << idx; let led = 1 << idx;
if led < LED_MAX.bits() && self.led.bits() & led == 1 { if led < LED_MAX.bits() && self.led.bits() & led == 1 {
if old_state.led_vals[idx as usize] != self.state.led_vals[idx as usize] { if old_state.led_vals[idx as usize] != self.state.led_vals[idx as usize] {

View file

@ -1,2 +1,2 @@
// woo tests! should really test compensate_droped... I don't even know how it's *supposed* to // woo tests! should really test compensate_dropped... I don't even know how it's *supposed* to
// behave yet though. // behave yet though.