Implement specific key modifiers on Windows. See #117
This commit is contained in:
parent
b7a6d09d23
commit
f11d130c45
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#pragma comment( lib, "gdiplus.lib" )
|
#pragma comment( lib, "gdiplus.lib" )
|
||||||
#include <gdiplus.h>
|
#include <gdiplus.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
// How many milliseconds must pass between keystrokes to refresh the keyboard layout
|
// How many milliseconds must pass between keystrokes to refresh the keyboard layout
|
||||||
const long refreshKeyboardLayoutInterval = 2000;
|
const long refreshKeyboardLayoutInterval = 2000;
|
||||||
|
@ -258,16 +259,38 @@ LRESULT CALLBACK window_procedure(HWND window, unsigned int msg, WPARAM wp, LPAR
|
||||||
// We need to call the callback in two different ways based on the type of key
|
// We need to call the callback in two different ways based on the type of key
|
||||||
// The only modifier we use that has a result > 0 is the BACKSPACE, so we have to consider it.
|
// The only modifier we use that has a result > 0 is the BACKSPACE, so we have to consider it.
|
||||||
if (result >= 1 && raw->data.keyboard.VKey != VK_BACK) {
|
if (result >= 1 && raw->data.keyboard.VKey != VK_BACK) {
|
||||||
keypress_callback(manager_instance, reinterpret_cast<uint16_t*>(buffer.data()), buffer.size(), 0, raw->data.keyboard.VKey, is_key_down);
|
keypress_callback(manager_instance, reinterpret_cast<uint16_t*>(buffer.data()), buffer.size(), 0, raw->data.keyboard.VKey, 0, is_key_down);
|
||||||
}else{
|
}else{
|
||||||
keypress_callback(manager_instance, nullptr, 0, 1, raw->data.keyboard.VKey, is_key_down);
|
//std::cout << raw->data.keyboard.MakeCode << " " << raw->data.keyboard.Flags << std::endl;
|
||||||
|
int variant = 0;
|
||||||
|
if (raw->data.keyboard.VKey == VK_SHIFT) {
|
||||||
|
// To discriminate between the left and right shift, we need to employ a workaround.
|
||||||
|
// See: https://stackoverflow.com/questions/5920301/distinguish-between-left-and-right-shift-keys-using-rawinput
|
||||||
|
if (raw->data.keyboard.MakeCode == 42) { // Left shift
|
||||||
|
variant = LEFT_VARIANT;
|
||||||
|
}if (raw->data.keyboard.MakeCode == 54) { // Right shift
|
||||||
|
variant = RIGHT_VARIANT;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
// Also the ALT and CTRL key are special cases
|
||||||
|
// Check out the previous Stackoverflow question for more information
|
||||||
|
if (raw->data.keyboard.VKey == VK_CONTROL || raw->data.keyboard.VKey == VK_MENU) {
|
||||||
|
if ((raw->data.keyboard.Flags & RI_KEY_E0) != 0) {
|
||||||
|
variant = RIGHT_VARIANT;
|
||||||
|
}else{
|
||||||
|
variant = LEFT_VARIANT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keypress_callback(manager_instance, nullptr, 0, 1, raw->data.keyboard.VKey, variant, is_key_down);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if (raw->header.dwType == RIM_TYPEMOUSE) // Mouse input, registered as "other" events. Needed to improve the reliability of word matches
|
}else if (raw->header.dwType == RIM_TYPEMOUSE) // Mouse input, registered as "other" events. Needed to improve the reliability of word matches
|
||||||
{
|
{
|
||||||
if ((raw->data.mouse.usButtonFlags & (RI_MOUSE_LEFT_BUTTON_DOWN | RI_MOUSE_RIGHT_BUTTON_DOWN | RI_MOUSE_MIDDLE_BUTTON_DOWN)) != 0) {
|
if ((raw->data.mouse.usButtonFlags & (RI_MOUSE_LEFT_BUTTON_DOWN | RI_MOUSE_RIGHT_BUTTON_DOWN | RI_MOUSE_MIDDLE_BUTTON_DOWN)) != 0) {
|
||||||
//std::cout << "mouse down" << std::endl;
|
//std::cout << "mouse down" << std::endl;
|
||||||
keypress_callback(manager_instance, nullptr, 0, 2, raw->data.mouse.usButtonFlags, 0);
|
keypress_callback(manager_instance, nullptr, 0, 2, raw->data.mouse.usButtonFlags, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,14 @@ extern void * manager_instance;
|
||||||
*/
|
*/
|
||||||
extern "C" int32_t initialize(void * self, wchar_t * ico_path, wchar_t * bmp_path);
|
extern "C" int32_t initialize(void * self, wchar_t * ico_path, wchar_t * bmp_path);
|
||||||
|
|
||||||
|
#define LEFT_VARIANT 1
|
||||||
|
#define RIGHT_VARIANT 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called when a new keypress is made, the first argument is an int array,
|
* Called when a new keypress is made, the first argument is an int array,
|
||||||
* while the second is the size of the array.
|
* while the second is the size of the array.
|
||||||
*/
|
*/
|
||||||
typedef void (*KeypressCallback)(void * self, uint16_t *buffer, int32_t len, int32_t event_type, int32_t key_code, int32_t is_key_down);
|
typedef void (*KeypressCallback)(void * self, uint16_t *buffer, int32_t len, int32_t event_type, int32_t key_code, int32_t is_key_down, int32_t variant);
|
||||||
extern KeypressCallback keypress_callback;
|
extern KeypressCallback keypress_callback;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -51,7 +51,7 @@ extern {
|
||||||
|
|
||||||
// KEYBOARD
|
// KEYBOARD
|
||||||
pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u16,
|
pub fn register_keypress_callback(cb: extern fn(_self: *mut c_void, *const u16,
|
||||||
i32, i32, i32, i32));
|
i32, i32, i32, i32, i32));
|
||||||
|
|
||||||
pub fn eventloop();
|
pub fn eventloop();
|
||||||
pub fn send_string(string: *const u16);
|
pub fn send_string(string: *const u16);
|
||||||
|
|
|
@ -108,7 +108,7 @@ impl super::Context for WindowsContext {
|
||||||
// Native bridge code
|
// Native bridge code
|
||||||
|
|
||||||
extern fn keypress_callback(_self: *mut c_void, raw_buffer: *const u16, len: i32,
|
extern fn keypress_callback(_self: *mut c_void, raw_buffer: *const u16, len: i32,
|
||||||
event_type: i32, key_code: i32, is_key_down: i32) {
|
event_type: i32, key_code: i32, variant: i32, is_key_down: i32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _self = _self as *mut WindowsContext;
|
let _self = _self as *mut WindowsContext;
|
||||||
|
|
||||||
|
@ -145,12 +145,16 @@ extern fn keypress_callback(_self: *mut c_void, raw_buffer: *const u16, len: i32
|
||||||
}
|
}
|
||||||
}else{ // KEY UP event
|
}else{ // KEY UP event
|
||||||
if event_type == 1 { // Modifier event
|
if event_type == 1 { // Modifier event
|
||||||
let modifier: Option<KeyModifier> = match key_code {
|
let modifier: Option<KeyModifier> = match (key_code, variant) {
|
||||||
0x5B | 0x5C => Some(META),
|
(0x5B, _) => Some(LEFT_META),
|
||||||
0x10 => Some(SHIFT),
|
(0x5C, _) => Some(RIGHT_META),
|
||||||
0x12 => Some(ALT),
|
(0x10, 1) => Some(LEFT_SHIFT),
|
||||||
0x11 => Some(CTRL),
|
(0x10, 2) => Some(RIGHT_SHIFT),
|
||||||
0x08 => Some(BACKSPACE),
|
(0x12, 1) => Some(LEFT_ALT),
|
||||||
|
(0x12, 2) => Some(RIGHT_ALT),
|
||||||
|
(0x11, 1) => Some(LEFT_CTRL),
|
||||||
|
(0x11, 2) => Some(RIGHT_CTRL),
|
||||||
|
(0x08, _) => Some(BACKSPACE),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
126
src/event/mod.rs
126
src/event/mod.rs
|
@ -57,6 +57,7 @@ pub enum KeyEvent {
|
||||||
Other
|
Other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[warn(non_camel_case_types)]
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum KeyModifier {
|
pub enum KeyModifier {
|
||||||
CTRL,
|
CTRL,
|
||||||
|
@ -65,6 +66,70 @@ pub enum KeyModifier {
|
||||||
META,
|
META,
|
||||||
BACKSPACE,
|
BACKSPACE,
|
||||||
OFF,
|
OFF,
|
||||||
|
|
||||||
|
// These are specific variants of the ones above. See issue: #117
|
||||||
|
// https://github.com/federico-terzi/espanso/issues/117
|
||||||
|
LEFT_CTRL,
|
||||||
|
RIGHT_CTRL,
|
||||||
|
LEFT_ALT,
|
||||||
|
RIGHT_ALT,
|
||||||
|
LEFT_META,
|
||||||
|
RIGHT_META,
|
||||||
|
LEFT_SHIFT,
|
||||||
|
RIGHT_SHIFT,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyModifier {
|
||||||
|
/// This function is used to compare KeyModifiers, considering the relations between
|
||||||
|
/// the generic modifier and the specific left/right variant
|
||||||
|
/// For example, CTRL will match with CTRL, LEFT_CTRL and RIGHT_CTRL;
|
||||||
|
/// but LEFT_CTRL will only match will LEFT_CTRL
|
||||||
|
pub fn shallow_equals(current: &KeyModifier, config: &KeyModifier) -> bool {
|
||||||
|
use KeyModifier::*;
|
||||||
|
|
||||||
|
match config {
|
||||||
|
KeyModifier::CTRL => {
|
||||||
|
current == &LEFT_CTRL || current == &RIGHT_CTRL || current == &CTRL
|
||||||
|
},
|
||||||
|
KeyModifier::SHIFT => {
|
||||||
|
current == &LEFT_SHIFT || current == &RIGHT_SHIFT || current == &SHIFT
|
||||||
|
},
|
||||||
|
KeyModifier::ALT => {
|
||||||
|
current == &LEFT_ALT || current == &RIGHT_ALT || current == &ALT
|
||||||
|
},
|
||||||
|
KeyModifier::META => {
|
||||||
|
current == &LEFT_META || current == &RIGHT_META || current == &META
|
||||||
|
},
|
||||||
|
KeyModifier::BACKSPACE => {
|
||||||
|
current == &BACKSPACE
|
||||||
|
},
|
||||||
|
KeyModifier::LEFT_CTRL => {
|
||||||
|
current == &LEFT_CTRL
|
||||||
|
},
|
||||||
|
KeyModifier::RIGHT_CTRL => {
|
||||||
|
current == &RIGHT_CTRL
|
||||||
|
},
|
||||||
|
KeyModifier::LEFT_ALT => {
|
||||||
|
current == &LEFT_ALT
|
||||||
|
},
|
||||||
|
KeyModifier::RIGHT_ALT => {
|
||||||
|
current == &RIGHT_ALT
|
||||||
|
},
|
||||||
|
KeyModifier::LEFT_META => {
|
||||||
|
current == &LEFT_META
|
||||||
|
},
|
||||||
|
KeyModifier::RIGHT_META => {
|
||||||
|
current == &RIGHT_META
|
||||||
|
},
|
||||||
|
KeyModifier::LEFT_SHIFT => {
|
||||||
|
current == &LEFT_SHIFT
|
||||||
|
},
|
||||||
|
KeyModifier::RIGHT_SHIFT => {
|
||||||
|
current == &RIGHT_SHIFT
|
||||||
|
},
|
||||||
|
_ => {false},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receivers
|
// Receivers
|
||||||
|
@ -75,4 +140,65 @@ pub trait KeyEventReceiver {
|
||||||
|
|
||||||
pub trait ActionEventReceiver {
|
pub trait ActionEventReceiver {
|
||||||
fn on_action_event(&self, e: ActionType);
|
fn on_action_event(&self, e: ActionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TESTS
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use super::KeyModifier::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shallow_equals_ctrl() {
|
||||||
|
assert!(KeyModifier::shallow_equals(&CTRL, &CTRL));
|
||||||
|
assert!(KeyModifier::shallow_equals(&LEFT_CTRL, &CTRL));
|
||||||
|
assert!(KeyModifier::shallow_equals(&RIGHT_CTRL, &CTRL));
|
||||||
|
|
||||||
|
assert!(!KeyModifier::shallow_equals(&CTRL, &LEFT_CTRL));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&CTRL, &RIGHT_CTRL));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shallow_equals_shift() {
|
||||||
|
assert!(KeyModifier::shallow_equals(&SHIFT, &SHIFT));
|
||||||
|
assert!(KeyModifier::shallow_equals(&LEFT_SHIFT, &SHIFT));
|
||||||
|
assert!(KeyModifier::shallow_equals(&RIGHT_SHIFT, &SHIFT));
|
||||||
|
|
||||||
|
assert!(!KeyModifier::shallow_equals(&SHIFT, &LEFT_SHIFT));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&SHIFT, &RIGHT_SHIFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shallow_equals_alt() {
|
||||||
|
assert!(KeyModifier::shallow_equals(&ALT, &ALT));
|
||||||
|
assert!(KeyModifier::shallow_equals(&LEFT_ALT, &ALT));
|
||||||
|
assert!(KeyModifier::shallow_equals(&RIGHT_ALT, &ALT));
|
||||||
|
|
||||||
|
assert!(!KeyModifier::shallow_equals(&ALT, &LEFT_ALT));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&ALT, &RIGHT_ALT));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shallow_equals_meta() {
|
||||||
|
assert!(KeyModifier::shallow_equals(&META, &META));
|
||||||
|
assert!(KeyModifier::shallow_equals(&LEFT_META, &META));
|
||||||
|
assert!(KeyModifier::shallow_equals(&RIGHT_META, &META));
|
||||||
|
|
||||||
|
assert!(!KeyModifier::shallow_equals(&META, &LEFT_META));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&META, &RIGHT_META));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shallow_equals_backspace() {
|
||||||
|
assert!(KeyModifier::shallow_equals(&BACKSPACE, &BACKSPACE));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shallow_equals_off() {
|
||||||
|
assert!(!KeyModifier::shallow_equals(&OFF, &CTRL));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&OFF, &ALT));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&OFF, &META));
|
||||||
|
assert!(!KeyModifier::shallow_equals(&OFF, &SHIFT));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -212,7 +212,7 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMa
|
||||||
// TODO: at the moment, activating the passive key triggers the toggle key
|
// TODO: at the moment, activating the passive key triggers the toggle key
|
||||||
// study a mechanism to avoid this problem
|
// study a mechanism to avoid this problem
|
||||||
|
|
||||||
if m == config.toggle_key {
|
if KeyModifier::shallow_equals(&m, &config.toggle_key) {
|
||||||
check_interval(&self.toggle_press_time,
|
check_interval(&self.toggle_press_time,
|
||||||
u128::from(config.toggle_interval), || {
|
u128::from(config.toggle_interval), || {
|
||||||
self.toggle();
|
self.toggle();
|
||||||
|
@ -223,7 +223,7 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMa
|
||||||
self.current_set_queue.borrow_mut().clear();
|
self.current_set_queue.borrow_mut().clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else if m == config.passive_key {
|
}else if KeyModifier::shallow_equals(&m, &config.passive_key) {
|
||||||
check_interval(&self.passive_press_time,
|
check_interval(&self.passive_press_time,
|
||||||
u128::from(config.toggle_interval), || {
|
u128::from(config.toggle_interval), || {
|
||||||
self.receiver.on_passive();
|
self.receiver.on_passive();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user