pub struct Device { /* private fields */ }
Expand description
Opaque struct representing an evdev device
Unlike libevdev, this Device
mantains an associated file as an invariant
Implementations§
Source§impl Device
impl Device
Sourcepub fn new_from_file(file: File) -> Result<Device>
pub fn new_from_file(file: File) -> Result<Device>
Initialize a new libevdev device from the given file.
This is a shortcut for
use evdev_rs::{Device, UninitDevice};
let uninit_device = UninitDevice::new().unwrap();
let device = uninit_device.set_file(file);
The caller is responsible for opening the file and setting
the O_NONBLOCK
flag and handling permissions.
If the file is opened without O_NONBLOCK flag then next_event
should be called with ReadFlag::BLOCKING. Due to the caching
nature of next_event we might block while trying to buffer
new events even though some events are already present.
Examples found in repository?
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
fn main() -> Result<(), std::io::Error> {
// Parse command line arguments
let mut args = std::env::args();
if args.len() != 2 {
let n = args.nth(0).unwrap();
println!("Usage: `{} DEVICE`, eg. `{} /dev/input/event13`", n, n);
std::process::exit(1);
}
let device = &args.nth(1).unwrap();
// Connect to real keyboard
let f = File::open(device)?;
let d = Device::new_from_file(f)?;
if let Some(n) = d.name() {
println!(
"Connected to device: '{}' ({:04x}:{:04x})",
n,
d.vendor_id(),
d.product_id()
);
}
// Create virtual device
let u = UninitDevice::new().unwrap();
// Setup device
// per: https://01.org/linuxgraphics/gfx-docs/drm/input/uinput.html#mouse-movements
u.set_name("Virtual Mouse");
u.set_bustype(BusType::BUS_USB as u16);
u.set_vendor_id(0xabcd);
u.set_product_id(0xefef);
// Note mouse keys have to be enabled for this to be detected
// as a usable device, see: https://stackoverflow.com/a/64559658/6074942
u.enable_event_type(&EventType::EV_KEY)?;
u.enable_event_code(&EventCode::EV_KEY(EV_KEY::BTN_LEFT), None)?;
u.enable_event_code(&EventCode::EV_KEY(EV_KEY::BTN_RIGHT), None)?;
u.enable_event_type(&EventType::EV_REL)?;
u.enable_event_code(&EventCode::EV_REL(EV_REL::REL_X), None)?;
u.enable_event_code(&EventCode::EV_REL(EV_REL::REL_Y), None)?;
u.enable_event_code(&EventCode::EV_SYN(EV_SYN::SYN_REPORT), None)?;
// Attempt to create UInputDevice from UninitDevice
let v = UInputDevice::create_from_device(&u)?;
loop {
// Fetch keyboard events
let (_status, event) = d.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING)?;
// Map these to mouse events
println!("Event: {:?}", event);
// Map direction keys to mouse events
let e = match event.event_code {
EventCode::EV_KEY(EV_KEY::KEY_RIGHT) => Some((EV_REL::REL_X, MOUSE_STEP_X)),
EventCode::EV_KEY(EV_KEY::KEY_LEFT) => Some((EV_REL::REL_X, -MOUSE_STEP_X)),
EventCode::EV_KEY(EV_KEY::KEY_UP) => Some((EV_REL::REL_Y, -MOUSE_STEP_Y)),
EventCode::EV_KEY(EV_KEY::KEY_DOWN) => Some((EV_REL::REL_Y, MOUSE_STEP_Y)),
_ => None,
};
// Write mapped event
if let Some((e, n)) = e {
v.write_event(&InputEvent {
time: event.time,
event_code: EventCode::EV_REL(e),
value: n,
})?;
v.write_event(&InputEvent {
time: event.time,
event_code: EventCode::EV_SYN(EV_SYN::SYN_REPORT),
value: 0,
})?;
}
}
}
pub fn new_from_fd(file: File) -> Result<Device>
new_from_file
. Some function names were changed so they
more closely match their type signature. See issue 42 for discussion
https://github.com/ndesh26/evdev-rs/issues/42Sourcepub fn new_from_path<P: AsRef<Path>>(path: P) -> Result<Device>
pub fn new_from_path<P: AsRef<Path>>(path: P) -> Result<Device>
Opens a device with the given path as the location of devnode
The devnode file is opened with O_NONBLOCK
and all the pending
events are first read from the file before creating the device.
pub fn fd(&self) -> Option<File>
file
. This function can easily be misused. Calling
this method, then dropping the returned file will lead to failures
e.g. next_event will return an Err()Sourcepub fn change_file(&mut self, file: File) -> Result<File>
pub fn change_file(&mut self, file: File) -> Result<File>
Change the file for this device, without re-reading the actual device.
On success, returns the file that was previously associated with this device.
If the file changes after initializing the device, for example after a VT-switch in the X.org X server, this function updates the internal file to the newly opened. No check is made that new file points to the same device. If the device has changed, evdev’s behavior is undefined.
evdev device does not sync itself after changing the file and keeps the current device state. Use next_event with the FORCE_SYNC flag to force a re-sync.
§Example
use evdev_rs::{Device, UninitDevice, ReadFlag, ReadStatus};
let mut dev = Device::new_from_file(File::open("/dev/input/input0")?)?;
dev.change_file(File::open("/dev/input/input1")?)?;
dev.next_event(ReadFlag::FORCE_SYNC);
while dev.next_event(ReadFlag::SYNC).ok().unwrap().0 == ReadStatus::Sync
{} // noop
After changing the file, the device is assumed ungrabbed and a caller must call libevdev_grab() again.
pub fn change_fd(&mut self, file: File) -> Result<()>
Sourcepub fn grab(&mut self, grab: GrabMode) -> Result<()>
pub fn grab(&mut self, grab: GrabMode) -> Result<()>
Grab or ungrab the device through a kernel EVIOCGRAB.
This prevents other clients (including kernel-internal ones such as rfkill) from receiving events from this device. This is generally a bad idea. Don’t do this. Grabbing an already grabbed device, or ungrabbing an ungrabbed device is a noop and always succeeds.
A grab is an operation tied to a file descriptor, not a device. If a client changes the file descriptor with Device::change_file(), it must also re-issue a grab with libevdev_grab().
Sourcepub fn has_event_pending(&self) -> bool
pub fn has_event_pending(&self) -> bool
Check if there are events waiting for us.
This function does not consume an event and may not access the device file at all. If there are events queued internally this function will return true. If the internal queue is empty, this function will poll the file descriptor for data.
This is a convenience function for simple processes, most complex programs
are expected to use select(2) or poll(2) on the file descriptor. The kernel
guarantees that if data is available, it is a multiple of sizeof(struct
input_event), and thus calling next_event
when select(2) or
poll(2) return is safe. You do not need has_event_pending
if
you’re using select(2) or poll(2).
Sourcepub fn driver_version(&self) -> i32
pub fn driver_version(&self) -> i32
Return the driver version of a device already intialize with set_file
Examples found in repository?
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
fn main() {
let mut args = std::env::args();
if args.len() != 2 {
usage();
std::process::exit(1);
}
let path = &args.nth(1).unwrap();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_NONBLOCK)
.open(path)
.unwrap();
let mut buffer = Vec::new();
let result = file.read_to_end(&mut buffer);
if result.is_ok() || result.unwrap_err().kind() != ErrorKind::WouldBlock {
println!("Failed to drain pending events from device file");
}
let u_d = UninitDevice::new().unwrap();
let d = u_d.set_file(file).unwrap();
println!(
"Input device ID: bus 0x{:x} vendor 0x{:x} product 0x{:x}",
d.bustype(),
d.vendor_id(),
d.product_id()
);
println!("Evdev version: {:x}", d.driver_version());
println!("Input device name: \"{}\"", d.name().unwrap_or(""));
println!("Phys location: {}", d.phys().unwrap_or(""));
println!("Uniq identifier: {}", d.uniq().unwrap_or(""));
print_bits(&d);
print_props(&d);
let mut a: io::Result<(ReadStatus, InputEvent)>;
loop {
a = d.next_event(ReadFlag::NORMAL);
if a.is_ok() {
let mut result = a.ok().unwrap();
match result.0 {
ReadStatus::Sync => {
println!("::::::::::::::::::::: dropped ::::::::::::::::::::::");
while result.0 == ReadStatus::Sync {
print_sync_dropped_event(&result.1);
a = d.next_event(ReadFlag::SYNC);
if a.is_ok() {
result = a.ok().unwrap();
} else {
break;
}
}
println!("::::::::::::::::::::: re-synced ::::::::::::::::::::");
}
ReadStatus::Success => print_event(&result.1),
}
} else {
let err = a.err().unwrap();
match err.raw_os_error() {
Some(libc::EAGAIN) => continue,
_ => {
println!("{}", err);
break;
}
}
}
}
}
Sourcepub fn set_kernel_abs_info(&self, code: &EventCode, absinfo: &AbsInfo)
pub fn set_kernel_abs_info(&self, code: &EventCode, absinfo: &AbsInfo)
Set the device’s EV_ABS axis to the value defined in the abs parameter. This will be written to the kernel.
Sourcepub fn kernel_set_led_value(
&self,
code: &EventCode,
value: LedState,
) -> Result<()>
pub fn kernel_set_led_value( &self, code: &EventCode, value: LedState, ) -> Result<()>
Turn an LED on or off.
enabling an LED requires write permissions on the device’s file descriptor.
Sourcepub fn set_clock_id(&self, clockid: i32) -> Result<()>
pub fn set_clock_id(&self, clockid: i32) -> Result<()>
Set the clock ID to be used for timestamps. Further events from this device will report an event time based on the given clock.
This is a modification only affecting this representation of this device.
Sourcepub fn next_event(&self, flags: ReadFlag) -> Result<(ReadStatus, InputEvent)>
pub fn next_event(&self, flags: ReadFlag) -> Result<(ReadStatus, InputEvent)>
Get the next event from the device. This function operates in two different modes: normal mode or sync mode.
In normal mode (when flags has evdev::NORMAL
set), this function returns
ReadStatus::Success
and returns the event. If no events are available at
this time, it returns -EAGAIN
as Err
.
If the current event is an EV_SYN::SYN_DROPPED
event, this function returns
ReadStatus::Sync
and is set to the EV_SYN
event.The caller should now call
this function with the evdev::SYNC
flag set, to get the set of events that
make up the device state delta. This function returns ReadStatus::Sync for
each event part of that delta, until it returns -EAGAIN
once all events
have been synced.
If a device needs to be synced by the caller but the caller does not call
with the evdev::SYNC
flag set, all events from the diff are dropped after
evdev updates its internal state and event processing continues as normal.
Note that the current slot and the state of touch points may have updated
during the SYN_DROPPED
event, it is strongly recommended that a caller
ignoring all sync events calls current_slot
and checks the
ABS_MT_TRACKING_ID
values for all slots.
If a device has changed state without events being enqueued in evdev,
e.g. after changing the file descriptor, use the evdev::FORCE_SYNC
flag.
This triggers an internal sync of the device and next_event
returns
ReadStatus::Sync
.
Examples found in repository?
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
fn main() {
let mut args = std::env::args();
if args.len() != 2 {
usage();
std::process::exit(1);
}
let path = &args.nth(1).unwrap();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_NONBLOCK)
.open(path)
.unwrap();
let mut buffer = Vec::new();
let result = file.read_to_end(&mut buffer);
if result.is_ok() || result.unwrap_err().kind() != ErrorKind::WouldBlock {
println!("Failed to drain pending events from device file");
}
let u_d = UninitDevice::new().unwrap();
let d = u_d.set_file(file).unwrap();
println!(
"Input device ID: bus 0x{:x} vendor 0x{:x} product 0x{:x}",
d.bustype(),
d.vendor_id(),
d.product_id()
);
println!("Evdev version: {:x}", d.driver_version());
println!("Input device name: \"{}\"", d.name().unwrap_or(""));
println!("Phys location: {}", d.phys().unwrap_or(""));
println!("Uniq identifier: {}", d.uniq().unwrap_or(""));
print_bits(&d);
print_props(&d);
let mut a: io::Result<(ReadStatus, InputEvent)>;
loop {
a = d.next_event(ReadFlag::NORMAL);
if a.is_ok() {
let mut result = a.ok().unwrap();
match result.0 {
ReadStatus::Sync => {
println!("::::::::::::::::::::: dropped ::::::::::::::::::::::");
while result.0 == ReadStatus::Sync {
print_sync_dropped_event(&result.1);
a = d.next_event(ReadFlag::SYNC);
if a.is_ok() {
result = a.ok().unwrap();
} else {
break;
}
}
println!("::::::::::::::::::::: re-synced ::::::::::::::::::::");
}
ReadStatus::Success => print_event(&result.1),
}
} else {
let err = a.err().unwrap();
match err.raw_os_error() {
Some(libc::EAGAIN) => continue,
_ => {
println!("{}", err);
break;
}
}
}
}
}
More examples
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
fn main() -> Result<(), std::io::Error> {
// Parse command line arguments
let mut args = std::env::args();
if args.len() != 2 {
let n = args.nth(0).unwrap();
println!("Usage: `{} DEVICE`, eg. `{} /dev/input/event13`", n, n);
std::process::exit(1);
}
let device = &args.nth(1).unwrap();
// Connect to real keyboard
let f = File::open(device)?;
let d = Device::new_from_file(f)?;
if let Some(n) = d.name() {
println!(
"Connected to device: '{}' ({:04x}:{:04x})",
n,
d.vendor_id(),
d.product_id()
);
}
// Create virtual device
let u = UninitDevice::new().unwrap();
// Setup device
// per: https://01.org/linuxgraphics/gfx-docs/drm/input/uinput.html#mouse-movements
u.set_name("Virtual Mouse");
u.set_bustype(BusType::BUS_USB as u16);
u.set_vendor_id(0xabcd);
u.set_product_id(0xefef);
// Note mouse keys have to be enabled for this to be detected
// as a usable device, see: https://stackoverflow.com/a/64559658/6074942
u.enable_event_type(&EventType::EV_KEY)?;
u.enable_event_code(&EventCode::EV_KEY(EV_KEY::BTN_LEFT), None)?;
u.enable_event_code(&EventCode::EV_KEY(EV_KEY::BTN_RIGHT), None)?;
u.enable_event_type(&EventType::EV_REL)?;
u.enable_event_code(&EventCode::EV_REL(EV_REL::REL_X), None)?;
u.enable_event_code(&EventCode::EV_REL(EV_REL::REL_Y), None)?;
u.enable_event_code(&EventCode::EV_SYN(EV_SYN::SYN_REPORT), None)?;
// Attempt to create UInputDevice from UninitDevice
let v = UInputDevice::create_from_device(&u)?;
loop {
// Fetch keyboard events
let (_status, event) = d.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING)?;
// Map these to mouse events
println!("Event: {:?}", event);
// Map direction keys to mouse events
let e = match event.event_code {
EventCode::EV_KEY(EV_KEY::KEY_RIGHT) => Some((EV_REL::REL_X, MOUSE_STEP_X)),
EventCode::EV_KEY(EV_KEY::KEY_LEFT) => Some((EV_REL::REL_X, -MOUSE_STEP_X)),
EventCode::EV_KEY(EV_KEY::KEY_UP) => Some((EV_REL::REL_Y, -MOUSE_STEP_Y)),
EventCode::EV_KEY(EV_KEY::KEY_DOWN) => Some((EV_REL::REL_Y, MOUSE_STEP_Y)),
_ => None,
};
// Write mapped event
if let Some((e, n)) = e {
v.write_event(&InputEvent {
time: event.time,
event_code: EventCode::EV_REL(e),
value: n,
})?;
v.write_event(&InputEvent {
time: event.time,
event_code: EventCode::EV_SYN(EV_SYN::SYN_REPORT),
value: 0,
})?;
}
}
}
Trait Implementations§
Source§impl DeviceWrapper for Device
impl DeviceWrapper for Device
fn raw(&self) -> *mut libevdev
Source§fn enable<E: Enable>(&self, e: E) -> Result<()>
fn enable<E: Enable>(&self, e: E) -> Result<()>
Source§fn enable_property(&self, prop: &InputProp) -> Result<()>
fn enable_property(&self, prop: &InputProp) -> Result<()>
set_file
will overwrite any previously set values Read moreSource§fn enable_event_type(&self, ev_type: &EventType) -> Result<()>
fn enable_event_type(&self, ev_type: &EventType) -> Result<()>
Source§fn enable_event_code(
&self,
ev_code: &EventCode,
data: Option<EnableCodeData>,
) -> Result<()>
fn enable_event_code( &self, ev_code: &EventCode, data: Option<EnableCodeData>, ) -> Result<()>
Source§fn disable<E: Enable>(&self, d: E) -> Result<()>
fn disable<E: Enable>(&self, d: E) -> Result<()>
Source§fn disable_event_type(&self, ev_type: &EventType) -> Result<()>
fn disable_event_type(&self, ev_type: &EventType) -> Result<()>
Source§fn disable_event_code(&self, code: &EventCode) -> Result<()>
fn disable_event_code(&self, code: &EventCode) -> Result<()>
has_event_code
will return false for
this code. Read moreSource§fn has<E: Enable>(&self, e: E) -> bool
fn has<E: Enable>(&self, e: E) -> bool
true
if device support the InputProp/EventType/EventCode and false otherwiseSource§fn has_property(&self, prop: &InputProp) -> bool
fn has_property(&self, prop: &InputProp) -> bool
true
if device support the property and false otherwise Read moreSource§fn has_event_type(&self, ev_type: &EventType) -> bool
fn has_event_type(&self, ev_type: &EventType) -> bool
Source§fn has_event_code(&self, code: &EventCode) -> bool
fn has_event_code(&self, code: &EventCode) -> bool
Source§fn name(&self) -> Option<&str>
fn name(&self) -> Option<&str>
set_name
Source§fn phys(&self) -> Option<&str>
fn phys(&self) -> Option<&str>
set_phys
Source§fn uniq(&self) -> Option<&str>
fn uniq(&self) -> Option<&str>
set_uniq