diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e39147b..ff0768e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/platform/windows/event.rs b/src/platform/windows/event.rs index 21a14cc6..3be475a0 100644 --- a/src/platform/windows/event.rs +++ b/src/platform/windows/event.rs @@ -205,31 +205,49 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { } } -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) { +pub fn process_key_params(wparam: WPARAM, lparam: LPARAM) -> Option<(ScanCode, Option)> { 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 diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 23652252..1eafe1e4 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -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(), + }), + }); + } } } }