fix(detect): unregister device from epoll when removed on Wayland. #836

This commit is contained in:
Federico Terzi 2021-11-01 21:57:32 +01:00
parent 846d0a2be3
commit ec7f1772dd
2 changed files with 40 additions and 7 deletions

View File

@ -2,7 +2,7 @@
// https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-evdev.c
use anyhow::Result;
use libc::{input_event, size_t, ssize_t, EWOULDBLOCK, O_CLOEXEC, O_NONBLOCK, O_RDONLY};
use libc::{input_event, size_t, ssize_t, ENODEV, EWOULDBLOCK, O_CLOEXEC, O_NONBLOCK, O_RDONLY};
use log::trace;
use scopeguard::ScopeGuard;
use std::collections::HashMap;
@ -127,7 +127,11 @@ impl Device {
}
if len < 0 && unsafe { *errno_ptr } != EWOULDBLOCK {
return Err(DeviceError::BlockingReadOperation().into());
if unsafe { *errno_ptr } == ENODEV {
return Err(DeviceError::FailedReadNoSuchDevice.into());
}
return Err(DeviceError::FailedRead(unsafe { *errno_ptr }).into());
}
Ok(events)
@ -322,6 +326,9 @@ pub enum DeviceError {
#[error("no devices found")]
NoDevicesFound(),
#[error("read operation can't block device")]
BlockingReadOperation(),
#[error("read operation failed with code: `{0}`")]
FailedRead(i32),
#[error("read operation failed: ENODEV No such device")]
FailedReadNoSuchDevice,
}

View File

@ -38,8 +38,9 @@ use keymap::Keymap;
use lazycell::LazyCell;
use libc::{
__errno_location, close, epoll_ctl, epoll_event, epoll_wait, EINTR, EPOLLIN, EPOLL_CTL_ADD,
EPOLL_CTL_DEL,
};
use log::{debug, error, info, trace};
use log::{debug, error, info, trace, warn};
use thiserror::Error;
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
@ -204,7 +205,9 @@ impl Source for EVDEVSource {
}
}
for ev in evs.iter() {
#[allow(clippy::needless_range_loop)]
for i in 0usize..(ret as usize) {
let ev = evs[i];
let device = &self.devices[ev.u64 as usize];
match device.read() {
Ok(events) if !events.is_empty() => {
@ -226,7 +229,30 @@ impl Source for EVDEVSource {
});
}
Ok(_) => { /* SKIP EMPTY */ }
Err(err) => error!("Can't read from device {}: {}", device.get_path(), err),
Err(err) => {
if let Some(DeviceError::FailedReadNoSuchDevice) = err.downcast_ref::<DeviceError>() {
warn!("Can't read from device {}, this error usually means the device has been disconnected, removing from epoll.", device.get_path());
if unsafe {
epoll_ctl(
*epfd,
EPOLL_CTL_DEL,
device.get_raw_fd(),
std::ptr::null_mut(),
)
} != 0
{
error!(
"Could not remove {} from epoll, errno {}",
device.get_path(),
unsafe { *errno_ptr }
);
return Err(EVDEVSourceError::Internal().into());
}
} else {
error!("Can't read from device {}: {}", device.get_path(), err)
}
}
}
}
}