diff --git a/espanso-detect/src/lib.rs b/espanso-detect/src/lib.rs index 393b40e..6d210ed 100644 --- a/espanso-detect/src/lib.rs +++ b/espanso-detect/src/lib.rs @@ -66,6 +66,12 @@ pub struct SourceCreationOptions { // 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, + + // The maximum interval (in milliseconds) for which a keyboard layout + // can be cached. If switching often between different layouts, you + // could lower this amount to avoid the "lost detection" effect described + // in this issue: https://github.com/federico-terzi/espanso/issues/745 + pub win32_keyboard_layout_cache_interval: i64, } // This struct identifies the keyboard layout that @@ -87,6 +93,7 @@ impl Default for SourceCreationOptions { evdev_keyboard_rmlvo: None, hotkeys: Vec::new(), win32_exclude_orphan_events: true, + win32_keyboard_layout_cache_interval: 2000, } } } @@ -97,6 +104,7 @@ pub fn get_source(options: SourceCreationOptions) -> Result> { Ok(Box::new(win32::Win32Source::new( &options.hotkeys, options.win32_exclude_orphan_events, + options.win32_keyboard_layout_cache_interval, ))) } diff --git a/espanso-detect/src/win32/mod.rs b/espanso-detect/src/win32/mod.rs index d25e528..dbde573 100644 --- a/espanso-detect/src/win32/mod.rs +++ b/espanso-detect/src/win32/mod.rs @@ -17,6 +17,7 @@ * along with espanso. If not, see . */ +use std::os::raw::c_long; use std::{convert::TryInto, ffi::c_void}; use lazycell::LazyCell; @@ -79,10 +80,19 @@ pub struct RawHotKey { pub flags: u32, } +#[repr(C)] +pub struct InitOptions { + pub keyboard_layout_cache_interval: c_long, +} + #[allow(improper_ctypes)] #[link(name = "espansodetect", kind = "static")] extern "C" { - pub fn detect_initialize(_self: *const Win32Source, error_code: *mut i32) -> *mut c_void; + pub fn detect_initialize( + _self: *const Win32Source, + options: *const InitOptions, + error_code: *mut i32, + ) -> *mut c_void; pub fn detect_register_hotkey(window: *const c_void, hotkey: RawHotKey) -> i32; pub fn detect_eventloop( @@ -99,24 +109,35 @@ pub struct Win32Source { hotkeys: Vec, exclude_orphan_events: bool, + keyboard_layout_cache_interval: i64, } #[allow(clippy::new_without_default)] impl Win32Source { - pub fn new(hotkeys: &[HotKey], exclude_orphan_events: bool) -> Win32Source { + pub fn new( + hotkeys: &[HotKey], + exclude_orphan_events: bool, + keyboard_layout_cache_interval: i64, + ) -> Win32Source { Self { handle: std::ptr::null_mut(), callback: LazyCell::new(), hotkeys: hotkeys.to_vec(), exclude_orphan_events, + keyboard_layout_cache_interval, } } } impl Source for Win32Source { fn initialize(&mut self) -> Result<()> { + let options = InitOptions { + keyboard_layout_cache_interval: self.keyboard_layout_cache_interval.try_into().unwrap(), + }; + let mut error_code = 0; - let handle = unsafe { detect_initialize(self as *const Win32Source, &mut error_code) }; + let handle = + unsafe { detect_initialize(self as *const Win32Source, &options, &mut error_code) }; if handle.is_null() { let error = match error_code { diff --git a/espanso-detect/src/win32/native.cpp b/espanso-detect/src/win32/native.cpp index 1ac9d41..c54c396 100644 --- a/espanso-detect/src/win32/native.cpp +++ b/espanso-detect/src/win32/native.cpp @@ -39,8 +39,6 @@ #include #include -// How many milliseconds must pass between events before refreshing the keyboard layout -const long DETECT_REFRESH_KEYBOARD_LAYOUT_INTERVAL = 2000; const wchar_t *const DETECT_WINCLASS = L"EspansoDetect"; const USHORT MOUSE_DOWN_FLAGS = RI_MOUSE_LEFT_BUTTON_DOWN | RI_MOUSE_RIGHT_BUTTON_DOWN | RI_MOUSE_MIDDLE_BUTTON_DOWN | RI_MOUSE_BUTTON_1_DOWN | RI_MOUSE_BUTTON_2_DOWN | RI_MOUSE_BUTTON_3_DOWN | @@ -52,6 +50,8 @@ const USHORT MOUSE_UP_FLAGS = RI_MOUSE_LEFT_BUTTON_UP | RI_MOUSE_RIGHT_BUTTON_UP typedef struct { HKL current_keyboard_layout; DWORD last_key_press_tick; + // How many milliseconds must pass between events before refreshing the keyboard layout + long keyboard_layout_cache_interval; // Rust interop void * rust_instance; @@ -130,7 +130,7 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w DWORD currentTick = GetTickCount(); // If enough time has passed between the last keypress and now, refresh the keyboard layout - if ((currentTick - variables->last_key_press_tick) > DETECT_REFRESH_KEYBOARD_LAYOUT_INTERVAL) + if ((currentTick - variables->last_key_press_tick) > variables->keyboard_layout_cache_interval) { // Because keyboard layouts on windows are Window-specific, to get the current @@ -269,7 +269,7 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w } } -void * detect_initialize(void *_self, int32_t *error_code) +void * detect_initialize(void *_self, InitOptions * options, int32_t *error_code) { HWND window = NULL; @@ -297,6 +297,7 @@ void * detect_initialize(void *_self, int32_t *error_code) // Initialize the default keyboard layout variables->current_keyboard_layout = GetKeyboardLayout(0); + variables->keyboard_layout_cache_interval = options->keyboard_layout_cache_interval; // Docs: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw window = CreateWindowEx( diff --git a/espanso-detect/src/win32/native.h b/espanso-detect/src/win32/native.h index a93b9f7..7e187d3 100644 --- a/espanso-detect/src/win32/native.h +++ b/espanso-detect/src/win32/native.h @@ -76,9 +76,12 @@ typedef struct { typedef void (*EventCallback)(void * rust_istance, InputEvent data); +typedef struct { + long keyboard_layout_cache_interval; +} InitOptions; // Initialize the Raw Input API and the Window. -extern "C" void * detect_initialize(void * rust_istance, int32_t *error_code); +extern "C" void * detect_initialize(void * rust_istance, InitOptions * options, int32_t *error_code); // Register the given hotkey, return a non-zero code if successful extern "C" int32_t detect_register_hotkey(void * window, HotKey hotkey);