fix(detect): exclude software-generated events by default on Windows to avoid reading back espanso's events

This commit is contained in:
Federico Terzi 2021-08-22 21:45:49 +02:00
parent 2129494ae3
commit 53eef3ce7b
4 changed files with 39 additions and 3 deletions

View File

@ -60,6 +60,12 @@ pub struct SourceCreationOptions {
// List of global hotkeys the detection module has to register // List of global hotkeys the detection module has to register
// NOTE: Hotkeys don't work under the EVDEV backend yet (Wayland) // NOTE: Hotkeys don't work under the EVDEV backend yet (Wayland)
pub hotkeys: Vec<HotKey>, pub hotkeys: Vec<HotKey>,
// If true, filter out keyboard events without an explicit HID device source on Windows.
// This is needed to filter out the software-generated events, including
// those from espanso, but might need to be disabled when using some software-level keyboards.
// Disabling this option might conflict with the undo feature.
pub win32_exclude_orphan_events: bool,
} }
// This struct identifies the keyboard layout that // This struct identifies the keyboard layout that
@ -80,6 +86,7 @@ impl Default for SourceCreationOptions {
use_evdev: false, use_evdev: false,
evdev_keyboard_rmlvo: None, evdev_keyboard_rmlvo: None,
hotkeys: Vec::new(), hotkeys: Vec::new(),
win32_exclude_orphan_events: true,
} }
} }
} }
@ -87,7 +94,7 @@ impl Default for SourceCreationOptions {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> { pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
info!("using Win32Source"); info!("using Win32Source");
Ok(Box::new(win32::Win32Source::new(&options.hotkeys))) Ok(Box::new(win32::Win32Source::new(&options.hotkeys, options.win32_exclude_orphan_events)))
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View File

@ -64,6 +64,12 @@ pub struct RawInputEvent {
pub key_code: i32, pub key_code: i32,
pub variant: i32, pub variant: i32,
pub status: i32, pub status: i32,
// Only relevant for keyboard events, this is set to 1
// if a keyboard event has an explicit source, 0 otherwise.
// This is needed to filter out software generated events,
// including those from espanso.
pub has_known_source: i32,
} }
#[repr(C)] #[repr(C)]
@ -91,15 +97,18 @@ pub struct Win32Source {
handle: *mut c_void, handle: *mut c_void,
callback: LazyCell<SourceCallback>, callback: LazyCell<SourceCallback>,
hotkeys: Vec<HotKey>, hotkeys: Vec<HotKey>,
exclude_orphan_events: bool,
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
impl Win32Source { impl Win32Source {
pub fn new(hotkeys: &[HotKey]) -> Win32Source { pub fn new(hotkeys: &[HotKey], exclude_orphan_events: bool) -> Win32Source {
Self { Self {
handle: std::ptr::null_mut(), handle: std::ptr::null_mut(),
callback: LazyCell::new(), callback: LazyCell::new(),
hotkeys: hotkeys.to_vec(), hotkeys: hotkeys.to_vec(),
exclude_orphan_events,
} }
} }
} }
@ -148,6 +157,16 @@ impl Source for Win32Source {
} }
extern "C" fn callback(_self: *mut Win32Source, event: RawInputEvent) { extern "C" fn callback(_self: *mut Win32Source, event: RawInputEvent) {
// Filter out keyboard events without an explicit HID device source.
// This is needed to filter out the software-generated events, including
// those from espanso.
if event.event_type == INPUT_EVENT_TYPE_KEYBOARD && event.has_known_source == 0 {
if unsafe { (*_self).exclude_orphan_events } {
trace!("skipping keyboard event with unknown HID source (probably software generated).");
return;
}
}
let event: Option<InputEvent> = event.into(); let event: Option<InputEvent> = event.into();
if let Some(callback) = unsafe { (*_self).callback.borrow() } { if let Some(callback) = unsafe { (*_self).callback.borrow() } {
if let Some(event) = event { if let Some(event) = event {
@ -391,6 +410,7 @@ mod tests {
key_code: 0, key_code: 0,
variant: INPUT_LEFT_VARIANT, variant: INPUT_LEFT_VARIANT,
status: INPUT_STATUS_PRESSED, status: INPUT_STATUS_PRESSED,
has_known_source: 1,
} }
} }
@ -459,6 +479,7 @@ mod tests {
key_code: 123, key_code: 123,
variant: INPUT_LEFT_VARIANT, variant: INPUT_LEFT_VARIANT,
status: INPUT_STATUS_PRESSED, status: INPUT_STATUS_PRESSED,
has_known_source: 1,
} }
.into(); .into();
assert!(result.is_none()); assert!(result.is_none());

View File

@ -121,6 +121,8 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w
return 0; return 0;
} }
event.has_known_source = (raw->header.hDevice == 0) ? 0 : 1;
// The alt key sends a SYSKEYDOWN instead of KEYDOWN event // The alt key sends a SYSKEYDOWN instead of KEYDOWN event
int is_key_down = raw->data.keyboard.Message == WM_KEYDOWN || int is_key_down = raw->data.keyboard.Message == WM_KEYDOWN ||
raw->data.keyboard.Message == WM_SYSKEYDOWN; raw->data.keyboard.Message == WM_SYSKEYDOWN;

View File

@ -60,6 +60,12 @@ typedef struct {
// Pressed or Released status // Pressed or Released status
int32_t status; int32_t status;
// Only relevant for keyboard events, this is set to 1
// if a keyboard event has an explicit source, 0 otherwise.
// This is needed to filter out software generated events,
// including those from espanso.
int32_t has_known_source;
} InputEvent; } InputEvent;
typedef struct { typedef struct {