winit/src/platform_impl/windows/event.rs

437 lines
18 KiB
Rust
Raw Normal View History

use std::{
char,
2022-03-07 22:58:12 +01:00
sync::atomic::{AtomicBool, AtomicIsize, Ordering},
};
use windows_sys::Win32::{
Foundation::{LPARAM, WPARAM},
UI::{
Input::KeyboardAndMouse::{
GetKeyState, GetKeyboardLayout, GetKeyboardState, MapVirtualKeyA, ToUnicodeEx,
VIRTUAL_KEY, VK_0, VK_1, VK_2, VK_3, VK_4, VK_5, VK_6, VK_7, VK_8, VK_9, VK_A, VK_ADD,
VK_APPS, VK_B, VK_BACK, VK_BROWSER_BACK, VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD,
VK_BROWSER_HOME, VK_BROWSER_REFRESH, VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_C,
VK_CAPITAL, VK_CONTROL, VK_CONVERT, VK_D, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN,
VK_E, VK_END, VK_ESCAPE, VK_F, VK_F1, VK_F10, VK_F11, VK_F12, VK_F13, VK_F14, VK_F15,
VK_F16, VK_F17, VK_F18, VK_F19, VK_F2, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, VK_F3,
VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_G, VK_H, VK_HOME, VK_I, VK_INSERT, VK_J,
VK_K, VK_KANA, VK_KANJI, VK_L, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL,
VK_LEFT, VK_LMENU, VK_LSHIFT, VK_LWIN, VK_M, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE,
VK_MEDIA_PREV_TRACK, VK_MEDIA_STOP, VK_MENU, VK_MULTIPLY, VK_N, VK_NEXT, VK_NONCONVERT,
VK_NUMLOCK, VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5,
VK_NUMPAD6, VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_O, VK_OEM_1, VK_OEM_102, VK_OEM_2,
VK_OEM_3, VK_OEM_4, VK_OEM_5, VK_OEM_6, VK_OEM_7, VK_OEM_COMMA, VK_OEM_MINUS,
VK_OEM_PERIOD, VK_OEM_PLUS, VK_P, VK_PAUSE, VK_PRIOR, VK_Q, VK_R, VK_RCONTROL,
VK_RETURN, VK_RIGHT, VK_RMENU, VK_RSHIFT, VK_RWIN, VK_S, VK_SCROLL, VK_SHIFT, VK_SLEEP,
VK_SNAPSHOT, VK_SPACE, VK_SUBTRACT, VK_T, VK_TAB, VK_U, VK_UP, VK_V, VK_VOLUME_DOWN,
VK_VOLUME_MUTE, VK_VOLUME_UP, VK_W, VK_X, VK_Y, VK_Z,
},
TextServices::HKL,
WindowsAndMessaging::{MAPVK_VK_TO_CHAR, MAPVK_VSC_TO_VK_EX},
},
};
Windows: Implement DeviceEvents (#482) Fixes #467 All variants other than Text have been implemented. While Text can be implemented using ToUnicode, that doesn't play nice with dead keys, IME, etc. Most of the mouse DeviceEvents were already implemented, but due to the flags that were used when registering for raw input events, they only worked when the window was in the foreground. This is also a step forward for #338, as DeviceIds are no longer useless on Windows. On DeviceEvents, the DeviceId contains that device's handle. While that handle could ostensibly be used by developers to query device information, my actual reason for choosing it is because it's simply a very easy way to handle this. As a fun bonus, this enabled me to create this method: DevideIdExt::get_persistent_identifier() -> Option<String> Using this gives you a unique identifier for the device that persists across replugs/reboots/etc., so it's ideal for something like device-specific configuration. There's a notable caveat to the new DeviceIds, which is that the value will always be 0 for a WindowEvent. There doesn't seem to be any straightforward way around this limitation. I was concerned that multi-window applications would receive n copies of every DeviceEvent, but Windows only sends them to one window per application. Lastly, there's a chance that these additions will cause antivirus/etc. software to detect winit applications as keyloggers. I don't know how likely that is to actually happen to people, but if it does become an issue, the raw input code is neatly sequestered and would be easy to make optional during compilation.
2018-04-28 12:42:33 -04:00
use crate::event::{ModifiersState, ScanCode, VirtualKeyCode};
2022-03-07 22:58:12 +01:00
use super::util::has_flag;
2022-03-07 22:58:12 +01:00
fn key_pressed(vkey: VIRTUAL_KEY) -> bool {
unsafe { has_flag(GetKeyState(vkey as i32), 1 << 15) }
}
pub fn get_key_mods() -> ModifiersState {
2022-03-07 22:58:12 +01:00
let filter_out_altgr = layout_uses_altgr() && key_pressed(VK_RMENU);
let mut mods = ModifiersState::empty();
2022-03-07 22:58:12 +01:00
mods.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT));
mods.set(
ModifiersState::CTRL,
2022-03-07 22:58:12 +01:00
key_pressed(VK_CONTROL) && !filter_out_altgr,
);
mods.set(
ModifiersState::ALT,
2022-03-07 22:58:12 +01:00
key_pressed(VK_MENU) && !filter_out_altgr,
);
mods.set(
ModifiersState::LOGO,
2022-03-07 22:58:12 +01:00
key_pressed(VK_LWIN) || key_pressed(VK_RWIN),
);
mods
}
bitflags! {
#[derive(Default)]
pub struct ModifiersStateSide: u32 {
const LSHIFT = 0b010;
const RSHIFT = 0b001;
const LCTRL = 0b010 << 3;
const RCTRL = 0b001 << 3;
const LALT = 0b010 << 6;
const RALT = 0b001 << 6;
const LLOGO = 0b010 << 9;
const RLOGO = 0b001 << 9;
}
}
impl ModifiersStateSide {
pub fn filter_out_altgr(&self) -> ModifiersStateSide {
match layout_uses_altgr() && self.contains(Self::RALT) {
false => *self,
true => *self & !(Self::LCTRL | Self::RCTRL | Self::LALT | Self::RALT),
}
}
}
impl From<ModifiersStateSide> for ModifiersState {
fn from(side: ModifiersStateSide) -> Self {
let mut state = ModifiersState::default();
state.set(
Self::SHIFT,
side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT),
);
state.set(
Self::CTRL,
side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL),
);
state.set(
Self::ALT,
side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT),
);
state.set(
Self::LOGO,
side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO),
);
state
}
}
2022-03-07 22:58:12 +01:00
pub fn get_pressed_keys() -> impl Iterator<Item = VIRTUAL_KEY> {
let mut keyboard_state = vec![0u8; 256];
2022-03-07 22:58:12 +01:00
unsafe { GetKeyboardState(keyboard_state.as_mut_ptr()) };
keyboard_state
.into_iter()
.enumerate()
.filter(|(_, p)| (*p & (1 << 7)) != 0) // whether or not a key is pressed is communicated via the high-order bit
2022-03-07 22:58:12 +01:00
.map(|(i, _)| i as u16)
}
unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<char> {
let mut unicode_bytes = [0u16; 5];
2022-03-07 22:58:12 +01:00
let len = ToUnicodeEx(
v_key,
0,
keyboard_state.as_ptr(),
unicode_bytes.as_mut_ptr(),
unicode_bytes.len() as _,
0,
hkl,
);
if len >= 1 {
char::decode_utf16(unicode_bytes.iter().cloned())
.next()
.and_then(|c| c.ok())
} else {
None
}
}
/// Figures out if the keyboard layout has an AltGr key instead of an Alt key.
///
/// Unfortunately, the Windows API doesn't give a way for us to conveniently figure that out. So,
/// we use a technique blatantly stolen from [the Firefox source code][source]: iterate over every
/// possible virtual key and compare the `char` output when AltGr is pressed vs when it isn't. If
/// pressing AltGr outputs characters that are different from the standard characters, the layout
/// uses AltGr. Otherwise, it doesn't.
///
/// [source]: https://github.com/mozilla/gecko-dev/blob/265e6721798a455604328ed5262f430cfcc37c2f/widget/windows/KeyboardLayout.cpp#L4356-L4416
fn layout_uses_altgr() -> bool {
unsafe {
2022-03-07 22:58:12 +01:00
static ACTIVE_LAYOUT: AtomicIsize = AtomicIsize::new(0);
static USES_ALTGR: AtomicBool = AtomicBool::new(false);
2022-03-07 22:58:12 +01:00
let hkl = GetKeyboardLayout(0);
let old_hkl = ACTIVE_LAYOUT.swap(hkl, Ordering::SeqCst);
if hkl == old_hkl {
return USES_ALTGR.load(Ordering::SeqCst);
}
let mut keyboard_state_altgr = [0u8; 256];
// AltGr is an alias for Ctrl+Alt for... some reason. Whatever it is, those are the keypresses
// we have to emulate to do an AltGr test.
2022-03-07 22:58:12 +01:00
keyboard_state_altgr[VK_MENU as usize] = 0x80;
keyboard_state_altgr[VK_CONTROL as usize] = 0x80;
let keyboard_state_empty = [0u8; 256];
for v_key in 0..=255 {
let key_noaltgr = get_char(&keyboard_state_empty, v_key, hkl);
let key_altgr = get_char(&keyboard_state_altgr, v_key, hkl);
if let (Some(noaltgr), Some(altgr)) = (key_noaltgr, key_altgr) {
if noaltgr != altgr {
USES_ALTGR.store(true, Ordering::SeqCst);
return true;
}
}
}
USES_ALTGR.store(false, Ordering::SeqCst);
false
}
}
2022-03-07 22:58:12 +01:00
pub fn vkey_to_winit_vkey(vkey: VIRTUAL_KEY) -> Option<VirtualKeyCode> {
2017-07-24 20:21:04 +10:00
// VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
Windows: Implement DeviceEvents (#482) Fixes #467 All variants other than Text have been implemented. While Text can be implemented using ToUnicode, that doesn't play nice with dead keys, IME, etc. Most of the mouse DeviceEvents were already implemented, but due to the flags that were used when registering for raw input events, they only worked when the window was in the foreground. This is also a step forward for #338, as DeviceIds are no longer useless on Windows. On DeviceEvents, the DeviceId contains that device's handle. While that handle could ostensibly be used by developers to query device information, my actual reason for choosing it is because it's simply a very easy way to handle this. As a fun bonus, this enabled me to create this method: DevideIdExt::get_persistent_identifier() -> Option<String> Using this gives you a unique identifier for the device that persists across replugs/reboots/etc., so it's ideal for something like device-specific configuration. There's a notable caveat to the new DeviceIds, which is that the value will always be 0 for a WindowEvent. There doesn't seem to be any straightforward way around this limitation. I was concerned that multi-window applications would receive n copies of every DeviceEvent, but Windows only sends them to one window per application. Lastly, there's a chance that these additions will cause antivirus/etc. software to detect winit applications as keyloggers. I don't know how likely that is to actually happen to people, but if it does become an issue, the raw input code is neatly sequestered and would be easy to make optional during compilation.
2018-04-28 12:42:33 -04:00
match vkey {
2022-03-07 22:58:12 +01:00
//VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
//VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
//VK_CANCEL => Some(VirtualKeyCode::Cancel),
//VK_MBUTTON => Some(VirtualKeyCode::Mbutton),
//VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1),
//VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2),
VK_BACK => Some(VirtualKeyCode::Back),
VK_TAB => Some(VirtualKeyCode::Tab),
//VK_CLEAR => Some(VirtualKeyCode::Clear),
VK_RETURN => Some(VirtualKeyCode::Return),
VK_LSHIFT => Some(VirtualKeyCode::LShift),
VK_RSHIFT => Some(VirtualKeyCode::RShift),
VK_LCONTROL => Some(VirtualKeyCode::LControl),
VK_RCONTROL => Some(VirtualKeyCode::RControl),
VK_LMENU => Some(VirtualKeyCode::LAlt),
VK_RMENU => Some(VirtualKeyCode::RAlt),
VK_PAUSE => Some(VirtualKeyCode::Pause),
VK_CAPITAL => Some(VirtualKeyCode::Capital),
VK_KANA => Some(VirtualKeyCode::Kana),
//VK_HANGUEL => Some(VirtualKeyCode::Hanguel),
//VK_HANGUL => Some(VirtualKeyCode::Hangul),
//VK_JUNJA => Some(VirtualKeyCode::Junja),
//VK_FINAL => Some(VirtualKeyCode::Final),
//VK_HANJA => Some(VirtualKeyCode::Hanja),
VK_KANJI => Some(VirtualKeyCode::Kanji),
VK_ESCAPE => Some(VirtualKeyCode::Escape),
VK_CONVERT => Some(VirtualKeyCode::Convert),
VK_NONCONVERT => Some(VirtualKeyCode::NoConvert),
//VK_ACCEPT => Some(VirtualKeyCode::Accept),
//VK_MODECHANGE => Some(VirtualKeyCode::Modechange),
VK_SPACE => Some(VirtualKeyCode::Space),
VK_PRIOR => Some(VirtualKeyCode::PageUp),
VK_NEXT => Some(VirtualKeyCode::PageDown),
VK_END => Some(VirtualKeyCode::End),
VK_HOME => Some(VirtualKeyCode::Home),
VK_LEFT => Some(VirtualKeyCode::Left),
VK_UP => Some(VirtualKeyCode::Up),
VK_RIGHT => Some(VirtualKeyCode::Right),
VK_DOWN => Some(VirtualKeyCode::Down),
//VK_SELECT => Some(VirtualKeyCode::Select),
//VK_PRINT => Some(VirtualKeyCode::Print),
//VK_EXECUTE => Some(VirtualKeyCode::Execute),
VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot),
VK_INSERT => Some(VirtualKeyCode::Insert),
VK_DELETE => Some(VirtualKeyCode::Delete),
//VK_HELP => Some(VirtualKeyCode::Help),
VK_0 => Some(VirtualKeyCode::Key0),
VK_1 => Some(VirtualKeyCode::Key1),
VK_2 => Some(VirtualKeyCode::Key2),
VK_3 => Some(VirtualKeyCode::Key3),
VK_4 => Some(VirtualKeyCode::Key4),
VK_5 => Some(VirtualKeyCode::Key5),
VK_6 => Some(VirtualKeyCode::Key6),
VK_7 => Some(VirtualKeyCode::Key7),
VK_8 => Some(VirtualKeyCode::Key8),
VK_9 => Some(VirtualKeyCode::Key9),
VK_A => Some(VirtualKeyCode::A),
VK_B => Some(VirtualKeyCode::B),
VK_C => Some(VirtualKeyCode::C),
VK_D => Some(VirtualKeyCode::D),
VK_E => Some(VirtualKeyCode::E),
VK_F => Some(VirtualKeyCode::F),
VK_G => Some(VirtualKeyCode::G),
VK_H => Some(VirtualKeyCode::H),
VK_I => Some(VirtualKeyCode::I),
VK_J => Some(VirtualKeyCode::J),
VK_K => Some(VirtualKeyCode::K),
VK_L => Some(VirtualKeyCode::L),
VK_M => Some(VirtualKeyCode::M),
VK_N => Some(VirtualKeyCode::N),
VK_O => Some(VirtualKeyCode::O),
VK_P => Some(VirtualKeyCode::P),
VK_Q => Some(VirtualKeyCode::Q),
VK_R => Some(VirtualKeyCode::R),
VK_S => Some(VirtualKeyCode::S),
VK_T => Some(VirtualKeyCode::T),
VK_U => Some(VirtualKeyCode::U),
VK_V => Some(VirtualKeyCode::V),
VK_W => Some(VirtualKeyCode::W),
VK_X => Some(VirtualKeyCode::X),
VK_Y => Some(VirtualKeyCode::Y),
VK_Z => Some(VirtualKeyCode::Z),
VK_LWIN => Some(VirtualKeyCode::LWin),
VK_RWIN => Some(VirtualKeyCode::RWin),
VK_APPS => Some(VirtualKeyCode::Apps),
VK_SLEEP => Some(VirtualKeyCode::Sleep),
VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1),
VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2),
VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3),
VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4),
VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5),
VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6),
VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7),
VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8),
VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9),
VK_MULTIPLY => Some(VirtualKeyCode::NumpadMultiply),
VK_ADD => Some(VirtualKeyCode::NumpadAdd),
//VK_SEPARATOR => Some(VirtualKeyCode::Separator),
VK_SUBTRACT => Some(VirtualKeyCode::NumpadSubtract),
VK_DECIMAL => Some(VirtualKeyCode::NumpadDecimal),
VK_DIVIDE => Some(VirtualKeyCode::NumpadDivide),
VK_F1 => Some(VirtualKeyCode::F1),
VK_F2 => Some(VirtualKeyCode::F2),
VK_F3 => Some(VirtualKeyCode::F3),
VK_F4 => Some(VirtualKeyCode::F4),
VK_F5 => Some(VirtualKeyCode::F5),
VK_F6 => Some(VirtualKeyCode::F6),
VK_F7 => Some(VirtualKeyCode::F7),
VK_F8 => Some(VirtualKeyCode::F8),
VK_F9 => Some(VirtualKeyCode::F9),
VK_F10 => Some(VirtualKeyCode::F10),
VK_F11 => Some(VirtualKeyCode::F11),
VK_F12 => Some(VirtualKeyCode::F12),
VK_F13 => Some(VirtualKeyCode::F13),
VK_F14 => Some(VirtualKeyCode::F14),
VK_F15 => Some(VirtualKeyCode::F15),
VK_F16 => Some(VirtualKeyCode::F16),
VK_F17 => Some(VirtualKeyCode::F17),
VK_F18 => Some(VirtualKeyCode::F18),
VK_F19 => Some(VirtualKeyCode::F19),
VK_F20 => Some(VirtualKeyCode::F20),
VK_F21 => Some(VirtualKeyCode::F21),
VK_F22 => Some(VirtualKeyCode::F22),
VK_F23 => Some(VirtualKeyCode::F23),
VK_F24 => Some(VirtualKeyCode::F24),
VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
VK_SCROLL => Some(VirtualKeyCode::Scroll),
VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward),
VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh),
VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop),
VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch),
VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites),
VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome),
VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute),
VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown),
VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp),
VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack),
VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack),
VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop),
VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause),
VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail),
VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect),
/*VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/
VK_OEM_PLUS => Some(VirtualKeyCode::Equals),
VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
VK_OEM_1 => map_text_keys(vkey),
VK_OEM_2 => map_text_keys(vkey),
VK_OEM_3 => map_text_keys(vkey),
VK_OEM_4 => map_text_keys(vkey),
VK_OEM_5 => map_text_keys(vkey),
VK_OEM_6 => map_text_keys(vkey),
VK_OEM_7 => map_text_keys(vkey),
/* VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
VK_OEM_102 => Some(VirtualKeyCode::OEM102),
/*VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
VK_PACKET => Some(VirtualKeyCode::Packet),
VK_ATTN => Some(VirtualKeyCode::Attn),
VK_CRSEL => Some(VirtualKeyCode::Crsel),
VK_EXSEL => Some(VirtualKeyCode::Exsel),
VK_EREOF => Some(VirtualKeyCode::Ereof),
VK_PLAY => Some(VirtualKeyCode::Play),
VK_ZOOM => Some(VirtualKeyCode::Zoom),
VK_NONAME => Some(VirtualKeyCode::Noname),
VK_PA1 => Some(VirtualKeyCode::Pa1),
VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
_ => None,
Windows: Implement DeviceEvents (#482) Fixes #467 All variants other than Text have been implemented. While Text can be implemented using ToUnicode, that doesn't play nice with dead keys, IME, etc. Most of the mouse DeviceEvents were already implemented, but due to the flags that were used when registering for raw input events, they only worked when the window was in the foreground. This is also a step forward for #338, as DeviceIds are no longer useless on Windows. On DeviceEvents, the DeviceId contains that device's handle. While that handle could ostensibly be used by developers to query device information, my actual reason for choosing it is because it's simply a very easy way to handle this. As a fun bonus, this enabled me to create this method: DevideIdExt::get_persistent_identifier() -> Option<String> Using this gives you a unique identifier for the device that persists across replugs/reboots/etc., so it's ideal for something like device-specific configuration. There's a notable caveat to the new DeviceIds, which is that the value will always be 0 for a WindowEvent. There doesn't seem to be any straightforward way around this limitation. I was concerned that multi-window applications would receive n copies of every DeviceEvent, but Windows only sends them to one window per application. Lastly, there's a chance that these additions will cause antivirus/etc. software to detect winit applications as keyloggers. I don't know how likely that is to actually happen to people, but if it does become an issue, the raw input code is neatly sequestered and would be easy to make optional during compilation.
2018-04-28 12:42:33 -04:00
}
}
pub fn handle_extended_keys(
2022-03-07 22:58:12 +01:00
vkey: VIRTUAL_KEY,
mut scancode: u32,
extended: bool,
2022-03-07 22:58:12 +01:00
) -> Option<(VIRTUAL_KEY, u32)> {
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
scancode |= if extended { 0xE000 } else { 0x0000 };
let vkey = match vkey {
2022-03-07 22:58:12 +01:00
VK_SHIFT => (unsafe { MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) } as u16),
VK_CONTROL => {
if extended {
2022-03-07 22:58:12 +01:00
VK_RCONTROL
} else {
2022-03-07 22:58:12 +01:00
VK_LCONTROL
}
2019-06-24 12:14:55 -04:00
}
2022-03-07 22:58:12 +01:00
VK_MENU => {
if extended {
2022-03-07 22:58:12 +01:00
VK_RMENU
} else {
2022-03-07 22:58:12 +01:00
VK_LMENU
}
2019-06-24 12:14:55 -04:00
}
_ => {
match scancode {
// When VK_PAUSE is pressed it emits a LeftControl + NumLock scancode event sequence, but reports VK_PAUSE
// as the virtual key on both events, or VK_PAUSE on the first event or 0xFF when using raw input.
// Don't emit anything for the LeftControl event in the pair...
2022-03-07 22:58:12 +01:00
0xE01D if vkey == VK_PAUSE => return None,
// ...and emit the Pause event for the second event in the pair.
2022-03-07 22:58:12 +01:00
0x45 if vkey == VK_PAUSE || vkey == 0xFF => {
scancode = 0xE059;
2022-03-07 22:58:12 +01:00
VK_PAUSE
2019-06-24 12:14:55 -04:00
}
// VK_PAUSE has an incorrect vkey value when used with modifiers. VK_PAUSE also reports a different
// scancode when used with modifiers than when used without
0xE046 => {
scancode = 0xE059;
2022-03-07 22:58:12 +01:00
VK_PAUSE
}
// VK_SCROLL has an incorrect vkey value when used with modifiers.
2022-03-07 22:58:12 +01:00
0x46 => VK_SCROLL,
_ => vkey,
}
2019-06-24 12:14:55 -04:00
}
};
Some((vkey, scancode))
Windows: Implement DeviceEvents (#482) Fixes #467 All variants other than Text have been implemented. While Text can be implemented using ToUnicode, that doesn't play nice with dead keys, IME, etc. Most of the mouse DeviceEvents were already implemented, but due to the flags that were used when registering for raw input events, they only worked when the window was in the foreground. This is also a step forward for #338, as DeviceIds are no longer useless on Windows. On DeviceEvents, the DeviceId contains that device's handle. While that handle could ostensibly be used by developers to query device information, my actual reason for choosing it is because it's simply a very easy way to handle this. As a fun bonus, this enabled me to create this method: DevideIdExt::get_persistent_identifier() -> Option<String> Using this gives you a unique identifier for the device that persists across replugs/reboots/etc., so it's ideal for something like device-specific configuration. There's a notable caveat to the new DeviceIds, which is that the value will always be 0 for a WindowEvent. There doesn't seem to be any straightforward way around this limitation. I was concerned that multi-window applications would receive n copies of every DeviceEvent, but Windows only sends them to one window per application. Lastly, there's a chance that these additions will cause antivirus/etc. software to detect winit applications as keyloggers. I don't know how likely that is to actually happen to people, but if it does become an issue, the raw input code is neatly sequestered and would be easy to make optional during compilation.
2018-04-28 12:42:33 -04:00
}
pub fn process_key_params(
wparam: WPARAM,
lparam: LPARAM,
) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
2022-03-07 22:58:12 +01:00
let scancode = ((lparam >> 16) & 0xff) as u32;
Windows: Implement DeviceEvents (#482) Fixes #467 All variants other than Text have been implemented. While Text can be implemented using ToUnicode, that doesn't play nice with dead keys, IME, etc. Most of the mouse DeviceEvents were already implemented, but due to the flags that were used when registering for raw input events, they only worked when the window was in the foreground. This is also a step forward for #338, as DeviceIds are no longer useless on Windows. On DeviceEvents, the DeviceId contains that device's handle. While that handle could ostensibly be used by developers to query device information, my actual reason for choosing it is because it's simply a very easy way to handle this. As a fun bonus, this enabled me to create this method: DevideIdExt::get_persistent_identifier() -> Option<String> Using this gives you a unique identifier for the device that persists across replugs/reboots/etc., so it's ideal for something like device-specific configuration. There's a notable caveat to the new DeviceIds, which is that the value will always be 0 for a WindowEvent. There doesn't seem to be any straightforward way around this limitation. I was concerned that multi-window applications would receive n copies of every DeviceEvent, but Windows only sends them to one window per application. Lastly, there's a chance that these additions will cause antivirus/etc. software to detect winit applications as keyloggers. I don't know how likely that is to actually happen to people, but if it does become an issue, the raw input code is neatly sequestered and would be easy to make optional during compilation.
2018-04-28 12:42:33 -04:00
let extended = (lparam & 0x01000000) != 0;
2022-03-07 22:58:12 +01:00
handle_extended_keys(wparam as u16, scancode, extended)
.map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey)))
2014-07-27 18:55:14 +02:00
}
2017-07-24 20:21:04 +10:00
// This is needed as windows doesn't properly distinguish
// some virtual key codes for different keyboard layouts
2022-03-07 22:58:12 +01:00
fn map_text_keys(win_virtual_key: VIRTUAL_KEY) -> Option<VirtualKeyCode> {
let char_key = unsafe { MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF;
2017-07-24 20:21:04 +10:00
match char::from_u32(char_key) {
Some(';') => Some(VirtualKeyCode::Semicolon),
Some('/') => Some(VirtualKeyCode::Slash),
Some('`') => Some(VirtualKeyCode::Grave),
Some('[') => Some(VirtualKeyCode::LBracket),
Some(']') => Some(VirtualKeyCode::RBracket),
Some('\'') => Some(VirtualKeyCode::Apostrophe),
Some('\\') => Some(VirtualKeyCode::Backslash),
_ => None,
2017-07-24 20:21:04 +10:00
}
}