From ec7f1772ddae642e71931bbb5b7e48dcd713dd75 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Mon, 1 Nov 2021 21:57:32 +0100 Subject: [PATCH] fix(detect): unregister device from epoll when removed on Wayland. #836 --- espanso-detect/src/evdev/device.rs | 15 ++++++++++---- espanso-detect/src/evdev/mod.rs | 32 +++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/espanso-detect/src/evdev/device.rs b/espanso-detect/src/evdev/device.rs index 818578d..5723ccc 100644 --- a/espanso-detect/src/evdev/device.rs +++ b/espanso-detect/src/evdev/device.rs @@ -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, } diff --git a/espanso-detect/src/evdev/mod.rs b/espanso-detect/src/evdev/mod.rs index d7d6337..4f5f5c3 100644 --- a/espanso-detect/src/evdev/mod.rs +++ b/espanso-detect/src/evdev/mod.rs @@ -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::() { + 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) + } + } } } }