feat(detect): add option to lower the windows keyboard layout cache interval, #745

This commit is contained in:
Federico Terzi 2021-10-25 21:30:16 +02:00
parent 21bc75bc31
commit 92645e0987
4 changed files with 41 additions and 8 deletions

View File

@ -66,6 +66,12 @@ pub struct SourceCreationOptions {
// those from espanso, but might need to be disabled when using some software-level keyboards. // those from espanso, but might need to be disabled when using some software-level keyboards.
// Disabling this option might conflict with the undo feature. // Disabling this option might conflict with the undo feature.
pub win32_exclude_orphan_events: bool, 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 // This struct identifies the keyboard layout that
@ -87,6 +93,7 @@ impl Default for SourceCreationOptions {
evdev_keyboard_rmlvo: None, evdev_keyboard_rmlvo: None,
hotkeys: Vec::new(), hotkeys: Vec::new(),
win32_exclude_orphan_events: true, win32_exclude_orphan_events: true,
win32_keyboard_layout_cache_interval: 2000,
} }
} }
} }
@ -97,6 +104,7 @@ pub fn get_source(options: SourceCreationOptions) -> Result<Box<dyn Source>> {
Ok(Box::new(win32::Win32Source::new( Ok(Box::new(win32::Win32Source::new(
&options.hotkeys, &options.hotkeys,
options.win32_exclude_orphan_events, options.win32_exclude_orphan_events,
options.win32_keyboard_layout_cache_interval,
))) )))
} }

View File

@ -17,6 +17,7 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::os::raw::c_long;
use std::{convert::TryInto, ffi::c_void}; use std::{convert::TryInto, ffi::c_void};
use lazycell::LazyCell; use lazycell::LazyCell;
@ -79,10 +80,19 @@ pub struct RawHotKey {
pub flags: u32, pub flags: u32,
} }
#[repr(C)]
pub struct InitOptions {
pub keyboard_layout_cache_interval: c_long,
}
#[allow(improper_ctypes)] #[allow(improper_ctypes)]
#[link(name = "espansodetect", kind = "static")] #[link(name = "espansodetect", kind = "static")]
extern "C" { 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_register_hotkey(window: *const c_void, hotkey: RawHotKey) -> i32;
pub fn detect_eventloop( pub fn detect_eventloop(
@ -99,24 +109,35 @@ pub struct Win32Source {
hotkeys: Vec<HotKey>, hotkeys: Vec<HotKey>,
exclude_orphan_events: bool, exclude_orphan_events: bool,
keyboard_layout_cache_interval: i64,
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
impl Win32Source { 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 { 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, exclude_orphan_events,
keyboard_layout_cache_interval,
} }
} }
} }
impl Source for Win32Source { impl Source for Win32Source {
fn initialize(&mut self) -> Result<()> { 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 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() { if handle.is_null() {
let error = match error_code { let error = match error_code {

View File

@ -39,8 +39,6 @@
#include <strsafe.h> #include <strsafe.h>
#include <Windows.h> #include <Windows.h>
// 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 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 | 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 | 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 { typedef struct {
HKL current_keyboard_layout; HKL current_keyboard_layout;
DWORD last_key_press_tick; DWORD last_key_press_tick;
// How many milliseconds must pass between events before refreshing the keyboard layout
long keyboard_layout_cache_interval;
// Rust interop // Rust interop
void * rust_instance; void * rust_instance;
@ -130,7 +130,7 @@ LRESULT CALLBACK detect_window_procedure(HWND window, unsigned int msg, WPARAM w
DWORD currentTick = GetTickCount(); DWORD currentTick = GetTickCount();
// If enough time has passed between the last keypress and now, refresh the keyboard layout // 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 // 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; HWND window = NULL;
@ -297,6 +297,7 @@ void * detect_initialize(void *_self, int32_t *error_code)
// Initialize the default keyboard layout // Initialize the default keyboard layout
variables->current_keyboard_layout = GetKeyboardLayout(0); 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 // Docs: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw
window = CreateWindowEx( window = CreateWindowEx(

View File

@ -76,9 +76,12 @@ typedef struct {
typedef void (*EventCallback)(void * rust_istance, InputEvent data); typedef void (*EventCallback)(void * rust_istance, InputEvent data);
typedef struct {
long keyboard_layout_cache_interval;
} InitOptions;
// Initialize the Raw Input API and the Window. // 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 // Register the given hotkey, return a non-zero code if successful
extern "C" int32_t detect_register_hotkey(void * window, HotKey hotkey); extern "C" int32_t detect_register_hotkey(void * window, HotKey hotkey);