Skip to main content

winit/platform_impl/windows/
raw_input.rs

1use std::mem::{self, size_of};
2use std::ptr;
3
4use windows_sys::Win32::Devices::HumanInterfaceDevice::{
5    HID_USAGE_GENERIC_KEYBOARD, HID_USAGE_GENERIC_MOUSE, HID_USAGE_PAGE_GENERIC,
6};
7use windows_sys::Win32::Foundation::{HANDLE, HWND};
8use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
9    MapVirtualKeyW, MAPVK_VK_TO_VSC_EX, VK_NUMLOCK, VK_SHIFT,
10};
11use windows_sys::Win32::UI::Input::{
12    GetRawInputData, GetRawInputDeviceInfoW, GetRawInputDeviceList, RegisterRawInputDevices,
13    HRAWINPUT, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST, RAWINPUTHEADER, RAWKEYBOARD,
14    RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDEV_REMOVE, RIDI_DEVICEINFO, RIDI_DEVICENAME,
15    RID_DEVICE_INFO, RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD, RID_DEVICE_INFO_MOUSE,
16    RID_INPUT, RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
17};
18use windows_sys::Win32::UI::WindowsAndMessaging::{
19    RI_KEY_E0, RI_KEY_E1, RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, RI_MOUSE_BUTTON_2_DOWN,
20    RI_MOUSE_BUTTON_2_UP, RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, RI_MOUSE_BUTTON_4_DOWN,
21    RI_MOUSE_BUTTON_4_UP, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP,
22};
23
24use super::scancode_to_physicalkey;
25use crate::event::ElementState;
26use crate::event_loop::DeviceEvents;
27use crate::keyboard::{KeyCode, PhysicalKey};
28use crate::platform_impl::platform::util;
29
30#[allow(dead_code)]
31pub fn get_raw_input_device_list() -> Option<Vec<RAWINPUTDEVICELIST>> {
32    let list_size = size_of::<RAWINPUTDEVICELIST>() as u32;
33
34    let mut num_devices = 0;
35    let status = unsafe { GetRawInputDeviceList(ptr::null_mut(), &mut num_devices, list_size) };
36
37    if status == u32::MAX {
38        return None;
39    }
40
41    let mut buffer = Vec::with_capacity(num_devices as _);
42
43    let num_stored =
44        unsafe { GetRawInputDeviceList(buffer.as_mut_ptr(), &mut num_devices, list_size) };
45
46    if num_stored == u32::MAX {
47        return None;
48    }
49
50    debug_assert_eq!(num_devices, num_stored);
51
52    unsafe { buffer.set_len(num_devices as _) };
53
54    Some(buffer)
55}
56
57#[allow(dead_code)]
58pub enum RawDeviceInfo {
59    Mouse(RID_DEVICE_INFO_MOUSE),
60    Keyboard(RID_DEVICE_INFO_KEYBOARD),
61    Hid(RID_DEVICE_INFO_HID),
62}
63
64impl From<RID_DEVICE_INFO> for RawDeviceInfo {
65    fn from(info: RID_DEVICE_INFO) -> Self {
66        unsafe {
67            match info.dwType {
68                RIM_TYPEMOUSE => RawDeviceInfo::Mouse(info.Anonymous.mouse),
69                RIM_TYPEKEYBOARD => RawDeviceInfo::Keyboard(info.Anonymous.keyboard),
70                RIM_TYPEHID => RawDeviceInfo::Hid(info.Anonymous.hid),
71                _ => unreachable!(),
72            }
73        }
74    }
75}
76
77#[allow(dead_code)]
78pub fn get_raw_input_device_info(handle: HANDLE) -> Option<RawDeviceInfo> {
79    let mut info: RID_DEVICE_INFO = unsafe { mem::zeroed() };
80    let info_size = size_of::<RID_DEVICE_INFO>() as u32;
81
82    info.cbSize = info_size;
83
84    let mut minimum_size = 0;
85    let status = unsafe {
86        GetRawInputDeviceInfoW(handle, RIDI_DEVICEINFO, &mut info as *mut _ as _, &mut minimum_size)
87    };
88
89    if status == u32::MAX || status == 0 {
90        return None;
91    }
92
93    debug_assert_eq!(info_size, status);
94
95    Some(info.into())
96}
97
98pub fn get_raw_input_device_name(handle: HANDLE) -> Option<String> {
99    let mut minimum_size = 0;
100    let status = unsafe {
101        GetRawInputDeviceInfoW(handle, RIDI_DEVICENAME, ptr::null_mut(), &mut minimum_size)
102    };
103
104    if status != 0 {
105        return None;
106    }
107
108    let mut name: Vec<u16> = Vec::with_capacity(minimum_size as _);
109
110    let status = unsafe {
111        GetRawInputDeviceInfoW(handle, RIDI_DEVICENAME, name.as_ptr() as _, &mut minimum_size)
112    };
113
114    if status == u32::MAX || status == 0 {
115        return None;
116    }
117
118    debug_assert_eq!(minimum_size, status);
119
120    unsafe { name.set_len(minimum_size as _) };
121
122    util::decode_wide(&name).into_string().ok()
123}
124
125pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool {
126    let device_size = size_of::<RAWINPUTDEVICE>() as u32;
127
128    unsafe {
129        RegisterRawInputDevices(devices.as_ptr(), devices.len() as u32, device_size) == true.into()
130    }
131}
132
133pub fn register_all_mice_and_keyboards_for_raw_input(
134    mut window_handle: HWND,
135    filter: DeviceEvents,
136) -> bool {
137    // RIDEV_DEVNOTIFY: receive hotplug events
138    // RIDEV_INPUTSINK: receive events even if we're not in the foreground
139    // RIDEV_REMOVE: don't receive device events (requires NULL hwndTarget)
140    let flags = match filter {
141        DeviceEvents::Never => {
142            window_handle = 0;
143            RIDEV_REMOVE
144        },
145        DeviceEvents::WhenFocused => RIDEV_DEVNOTIFY,
146        DeviceEvents::Always => RIDEV_DEVNOTIFY | RIDEV_INPUTSINK,
147    };
148
149    let devices: [RAWINPUTDEVICE; 2] = [
150        RAWINPUTDEVICE {
151            usUsagePage: HID_USAGE_PAGE_GENERIC,
152            usUsage: HID_USAGE_GENERIC_MOUSE,
153            dwFlags: flags,
154            hwndTarget: window_handle,
155        },
156        RAWINPUTDEVICE {
157            usUsagePage: HID_USAGE_PAGE_GENERIC,
158            usUsage: HID_USAGE_GENERIC_KEYBOARD,
159            dwFlags: flags,
160            hwndTarget: window_handle,
161        },
162    ];
163
164    register_raw_input_devices(&devices)
165}
166
167pub fn get_raw_input_data(handle: HRAWINPUT) -> Option<RAWINPUT> {
168    let mut data: RAWINPUT = unsafe { mem::zeroed() };
169    let mut data_size = size_of::<RAWINPUT>() as u32;
170    let header_size = size_of::<RAWINPUTHEADER>() as u32;
171
172    let status = unsafe {
173        GetRawInputData(handle, RID_INPUT, &mut data as *mut _ as _, &mut data_size, header_size)
174    };
175
176    if status == u32::MAX || status == 0 {
177        return None;
178    }
179
180    Some(data)
181}
182
183fn button_flags_to_element_state(
184    button_flags: u32,
185    down_flag: u32,
186    up_flag: u32,
187) -> Option<ElementState> {
188    // We assume the same button won't be simultaneously pressed and released.
189    if util::has_flag(button_flags, down_flag) {
190        Some(ElementState::Pressed)
191    } else if util::has_flag(button_flags, up_flag) {
192        Some(ElementState::Released)
193    } else {
194        None
195    }
196}
197
198pub fn get_raw_mouse_button_state(button_flags: u32) -> [Option<ElementState>; 5] {
199    [
200        button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP),
201        button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP),
202        button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP),
203        button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP),
204        button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP),
205    ]
206}
207
208pub fn get_keyboard_physical_key(keyboard: RAWKEYBOARD) -> Option<PhysicalKey> {
209    let extension = {
210        if util::has_flag(keyboard.Flags, RI_KEY_E0 as _) {
211            0xe000
212        } else if util::has_flag(keyboard.Flags, RI_KEY_E1 as _) {
213            0xe100
214        } else {
215            0x0000
216        }
217    };
218    let scancode = if keyboard.MakeCode == 0 {
219        // In some cases (often with media keys) the device reports a scancode of 0 but a
220        // valid virtual key. In these cases we obtain the scancode from the virtual key.
221        unsafe { MapVirtualKeyW(keyboard.VKey as u32, MAPVK_VK_TO_VSC_EX) as u16 }
222    } else {
223        keyboard.MakeCode | extension
224    };
225    if scancode == 0xe11d || scancode == 0xe02a {
226        // At the hardware (or driver?) level, pressing the Pause key is equivalent to pressing
227        // Ctrl+NumLock.
228        // This equivalence means that if the user presses Pause, the keyboard will emit two
229        // subsequent keypresses:
230        // 1, 0xE11D - Which is a left Ctrl (0x1D) with an extension flag (0xE100)
231        // 2, 0x0045 - Which on its own can be interpreted as Pause
232        //
233        // There's another combination which isn't quite an equivalence:
234        // PrtSc used to be Shift+Asterisk. This means that on some keyboards, pressing
235        // PrtSc (print screen) produces the following sequence:
236        // 1, 0xE02A - Which is a left shift (0x2A) with an extension flag (0xE000)
237        // 2, 0xE037 - Which is a numpad multiply (0x37) with an extension flag (0xE000). This on
238        //             its own it can be interpreted as PrtSc
239        //
240        // For this reason, if we encounter the first keypress, we simply ignore it, trusting
241        // that there's going to be another event coming, from which we can extract the
242        // appropriate key.
243        // For more on this, read the article by Raymond Chen, titled:
244        // "Why does Ctrl+ScrollLock cancel dialogs?"
245        // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503
246        return None;
247    }
248    let physical_key = if keyboard.VKey == VK_NUMLOCK {
249        // Historically, the NumLock and the Pause key were one and the same physical key.
250        // The user could trigger Pause by pressing Ctrl+NumLock.
251        // Now these are often physically separate and the two keys can be differentiated by
252        // checking the extension flag of the scancode. NumLock is 0xE045, Pause is 0x0045.
253        //
254        // However in this event, both keys are reported as 0x0045 even on modern hardware.
255        // Therefore we use the virtual key instead to determine whether it's a NumLock and
256        // set the KeyCode accordingly.
257        //
258        // For more on this, read the article by Raymond Chen, titled:
259        // "Why does Ctrl+ScrollLock cancel dialogs?"
260        // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503
261        PhysicalKey::Code(KeyCode::NumLock)
262    } else {
263        scancode_to_physicalkey(scancode as u32)
264    };
265    if keyboard.VKey == VK_SHIFT {
266        if let PhysicalKey::Code(
267            KeyCode::NumpadDecimal
268            | KeyCode::Numpad0
269            | KeyCode::Numpad1
270            | KeyCode::Numpad2
271            | KeyCode::Numpad3
272            | KeyCode::Numpad4
273            | KeyCode::Numpad5
274            | KeyCode::Numpad6
275            | KeyCode::Numpad7
276            | KeyCode::Numpad8
277            | KeyCode::Numpad9,
278        ) = physical_key
279        {
280            // On Windows, holding the Shift key makes numpad keys behave as if NumLock
281            // wasn't active. The way this is exposed to applications by the system is that
282            // the application receives a fake key release event for the shift key at the
283            // moment when the numpad key is pressed, just before receiving the numpad key
284            // as well.
285            //
286            // The issue is that in the raw device event (here), the fake shift release
287            // event reports the numpad key as the scancode. Unfortunately, the event
288            // doesn't have any information to tell whether it's the
289            // left shift or the right shift that needs to get the fake
290            // release (or press) event so we don't forward this
291            // event to the application at all.
292            //
293            // For more on this, read the article by Raymond Chen, titled:
294            // "The shift key overrides NumLock"
295            // https://devblogs.microsoft.com/oldnewthing/20040906-00/?p=37953
296            return None;
297        }
298    }
299
300    Some(physical_key)
301}