Windows: Fix detection of Pause and Scroll keys (#525)

Fixes #524
This commit is contained in:
Francesca Frangipane 2018-05-18 18:48:19 -04:00 committed by GitHub
parent dec728cfa2
commit fddfb2e2d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 65 deletions

View file

@ -16,6 +16,7 @@
- macOS keyboard handling has been overhauled, allowing for the use of dead keys, IME, etc. Right modifier keys are also no longer reported as being left.
- Added the `Window::set_ime_spot(x: i32, y: i32)` method, which is implemented on X11 and macOS.
- **Breaking**: `os::unix::WindowExt::send_xim_spot(x: i16, y: i16)` no longer exists. Switch to the new `Window::set_ime_spot(x: i32, y: i32)`, which has equivalent functionality.
- Fixed detection of `Pause` and `Scroll` keys on Windows.
# Version 0.14.0 (2018-05-09)

View file

@ -205,31 +205,49 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
}
}
pub fn vkey_left_right(vkey: c_int, scancode: UINT, extended: bool) -> c_int {
match vkey {
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
scancode,
winuser::MAPVK_VSC_TO_VK_EX,
) as _ },
winuser::VK_CONTROL => if extended {
winuser::VK_RCONTROL
} else {
winuser::VK_LCONTROL
},
winuser::VK_MENU => if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
},
_ => vkey,
}
pub fn handle_extended_keys(vkey: c_int, mut scancode: UINT, extended: bool) -> Option<(c_int, UINT)> {
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
let vkey = match vkey {
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
scancode,
winuser::MAPVK_VSC_TO_VK_EX,
) as _ },
winuser::VK_CONTROL => if extended {
winuser::VK_RCONTROL
} else {
winuser::VK_LCONTROL
},
winuser::VK_MENU => if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
},
_ => match scancode {
// This is only triggered when using raw input. Without this check, we get two events whenever VK_PAUSE is
// pressed, the first one having scancode 0x1D but vkey VK_PAUSE...
0x1D if vkey == winuser::VK_PAUSE => return None,
// ...and the second having scancode 0x45 but an unmatched vkey!
0x45 => winuser::VK_PAUSE,
// VK_PAUSE and VK_SCROLL have the same scancode when using modifiers, alongside incorrect vkey values.
0x46 => {
if extended {
scancode = 0x45;
winuser::VK_PAUSE
} else {
winuser::VK_SCROLL
}
},
_ => vkey,
},
};
Some((vkey, scancode))
}
pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
pub fn process_key_params(wparam: WPARAM, lparam: LPARAM) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
let scancode = ((lparam >> 16) & 0xff) as UINT;
let extended = (lparam & 0x01000000) != 0;
let vkey = vkey_left_right(wparam as _, scancode, extended);
(scancode, vkey_to_winit_vkey(vkey))
handle_extended_keys(wparam as _, scancode, extended)
.map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey)))
}
// This is needed as windows doesn't properly distinguish

View file

@ -34,7 +34,7 @@ use winapi::um::winnt::{LONG, SHORT};
use events::DeviceEvent;
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
use platform::platform::event::{vkey_to_winit_vkey, vkey_left_right};
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
use platform::platform::raw_input::*;
use platform::platform::window::adjust_size;
@ -586,26 +586,27 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 {
winuser::DefWindowProcW(window, msg, wparam, lparam)
} else {
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Pressed,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
}
}
});
// Windows doesn't emit a delete character by default, but in order to make it
// consistent with the other platforms we'll emit a delete character here.
if vkey == Some(VirtualKeyCode::Delete) {
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::ReceivedCharacter('\u{7F}'),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Pressed,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
}
}
});
// Windows doesn't emit a delete character by default, but in order to make it
// consistent with the other platforms we'll emit a delete character here.
if vkey == Some(VirtualKeyCode::Delete) {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::ReceivedCharacter('\u{7F}'),
});
}
}
0
}
@ -613,19 +614,20 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
winuser::WM_KEYUP | winuser::WM_SYSKEYUP => {
use events::ElementState::Released;
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Released,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
}
});
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Released,
scancode: scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
}
});
}
0
},
@ -836,23 +838,25 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
};
let scancode = keyboard.MakeCode as _;
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _);
let vkey = vkey_left_right(
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _)
| util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _);
if let Some((vkey, scancode)) = handle_extended_keys(
keyboard.VKey as _,
scancode,
extended,
);
let virtual_keycode = vkey_to_winit_vkey(vkey);
) {
let virtual_keycode = vkey_to_winit_vkey(vkey);
send_event(Event::DeviceEvent {
device_id,
event: Key(KeyboardInput {
scancode,
state,
virtual_keycode,
modifiers: event::get_key_mods(),
}),
});
send_event(Event::DeviceEvent {
device_id,
event: Key(KeyboardInput {
scancode,
state,
virtual_keycode,
modifiers: event::get_key_mods(),
}),
});
}
}
}
}