From fe2d37fcdcff31306c5115023b3a29fd752c7cc6 Mon Sep 17 00:00:00 2001 From: Francesca Frangipane Date: Sat, 28 Apr 2018 12:42:33 -0400 Subject: [PATCH] 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 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. --- CHANGELOG.md | 2 + src/os/windows.rs | 16 ++ src/platform/windows/event.rs | 67 +++++--- src/platform/windows/events_loop.rs | 147 ++++++++++++----- src/platform/windows/mod.rs | 23 ++- src/platform/windows/monitor.rs | 12 +- src/platform/windows/raw_input.rs | 235 ++++++++++++++++++++++++++++ src/platform/windows/util.rs | 16 ++ src/platform/windows/window.rs | 14 +- 9 files changed, 445 insertions(+), 87 deletions(-) create mode 100644 src/platform/windows/raw_input.rs create mode 100644 src/platform/windows/util.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ff09d6a54..5a624693d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar. - Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`). - Implemented `Moved` event on macOS. +- On Windows, implemented all variants of `DeviceEvent` other than `Text`. Mouse `DeviceEvent`s are now received even if the window isn't in the foreground. +- `DeviceId` on Windows is no longer a unit struct, and now contains a `u32`. For `WindowEvent`s, this will always be 0, but on `DeviceEvent`s it will be the handle to that device. `DeviceIdExt::get_persistent_identifier` can be used to acquire a unique identifier for that device that persists across replugs/reboots/etc. # Version 0.13.1 (2018-04-26) diff --git a/src/os/windows.rs b/src/os/windows.rs index d474cafa44..1526d3b790 100644 --- a/src/os/windows.rs +++ b/src/os/windows.rs @@ -3,6 +3,7 @@ use std::os::raw::c_void; use libc; use MonitorId; +use DeviceId; use Window; use WindowBuilder; use winapi::shared::windef::HWND; @@ -56,3 +57,18 @@ impl MonitorIdExt for MonitorId { self.inner.get_hmonitor() as *mut _ } } + +/// Additional methods on `DeviceId` that are specific to Windows. +pub trait DeviceIdExt { + /// Returns an identifier that persistently refers to this specific device. + /// + /// Will return `None` if the device is no longer available. + fn get_persistent_identifier(&self) -> Option; +} + +impl DeviceIdExt for DeviceId { + #[inline] + fn get_persistent_identifier(&self) -> Option { + self.0.get_persistent_identifier() + } +} diff --git a/src/platform/windows/event.rs b/src/platform/windows/event.rs index 5abdf309eb..21a14cc628 100644 --- a/src/platform/windows/event.rs +++ b/src/platform/windows/event.rs @@ -1,14 +1,13 @@ +use std::char; +use std::os::raw::c_int; + use events::VirtualKeyCode; use events::ModifiersState; -use winapi::shared::minwindef::{WPARAM, LPARAM}; +use winapi::shared::minwindef::{WPARAM, LPARAM, UINT}; use winapi::um::winuser; use ScanCode; -use std::char; - -const MAPVK_VK_TO_CHAR: u32 = 2; -const MAPVK_VSC_TO_VK_EX: u32 = 3; pub fn get_key_mods() -> ModifiersState { let mut mods = ModifiersState::default(); @@ -29,18 +28,9 @@ pub fn get_key_mods() -> ModifiersState { mods } -pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option) { - let scancode = ((lparam >> 16) & 0xff) as u32; - let extended = (lparam & 0x01000000) != 0; - let vk = match wparam as i32 { - winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) as i32 }, - winuser::VK_CONTROL => if extended { winuser::VK_RCONTROL } else { winuser::VK_LCONTROL }, - winuser::VK_MENU => if extended { winuser::VK_RMENU } else { winuser::VK_LMENU }, - other => other - }; - +pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { // VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx - (scancode, match vk { + match vkey { //winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton), //winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton), //winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel), @@ -191,13 +181,13 @@ pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option< winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma), winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus), winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period), - winuser::VK_OEM_1 => map_text_keys(vk), - winuser::VK_OEM_2 => map_text_keys(vk), - winuser::VK_OEM_3 => map_text_keys(vk), - winuser::VK_OEM_4 => map_text_keys(vk), - winuser::VK_OEM_5 => map_text_keys(vk), - winuser::VK_OEM_6 => map_text_keys(vk), - winuser::VK_OEM_7 => map_text_keys(vk), + winuser::VK_OEM_1 => map_text_keys(vkey), + winuser::VK_OEM_2 => map_text_keys(vkey), + winuser::VK_OEM_3 => map_text_keys(vkey), + winuser::VK_OEM_4 => map_text_keys(vkey), + winuser::VK_OEM_5 => map_text_keys(vkey), + winuser::VK_OEM_6 => map_text_keys(vkey), + winuser::VK_OEM_7 => map_text_keys(vkey), /*winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */ winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102), /*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey), @@ -212,13 +202,40 @@ pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option< winuser::VK_PA1 => Some(VirtualKeyCode::Pa1), winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/ _ => None - }) + } +} + +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 vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (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)) } // This is needed as windows doesn't properly distinguish // some virtual key codes for different keyboard layouts fn map_text_keys(win_virtual_key: i32) -> Option { - let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF; + let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, winuser::MAPVK_VK_TO_CHAR) } & 0x7FFF; match char::from_u32(char_key) { Some(';') => Some(VirtualKeyCode::Semicolon), Some('/') => Some(VirtualKeyCode::Slash), diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 3ca51802ec..91af29f1b1 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -30,13 +30,13 @@ use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::shared::windowsx; use winapi::um::{winuser, shellapi, processthreadsapi}; -use winapi::um::winnt::LONG; +use winapi::um::winnt::{LONG, SHORT}; -use platform::platform::event; +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::raw_input::*; use platform::platform::window::adjust_size; -use platform::platform::Cursor; -use platform::platform::WindowId; -use platform::platform::DEVICE_ID; use ControlFlow; use CursorState; @@ -567,7 +567,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, }, winuser::WM_MOUSEWHEEL => { - use events::{DeviceEvent, WindowEvent}; use events::MouseScrollDelta::LineDelta; use events::TouchPhase; @@ -580,11 +579,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() }, }); - send_event(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::MouseWheel { delta: LineDelta(0.0, value) }, - }); - 0 }, @@ -751,46 +745,121 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 0 }, + winuser::WM_INPUT_DEVICE_CHANGE => { + let event = match wparam as _ { + winuser::GIDC_ARRIVAL => DeviceEvent::Added, + winuser::GIDC_REMOVAL => DeviceEvent::Removed, + _ => unreachable!(), + }; + + send_event(Event::DeviceEvent { + device_id: wrap_device_id(lparam as _), + event, + }); + + winuser::DefWindowProcW(window, msg, wparam, lparam) + }, + winuser::WM_INPUT => { - use events::DeviceEvent::{Motion, MouseMotion}; - let mut data: winuser::RAWINPUT = mem::uninitialized(); - let mut data_size = mem::size_of::() as UINT; - winuser::GetRawInputData(mem::transmute(lparam), winuser::RID_INPUT, - mem::transmute(&mut data), &mut data_size, - mem::size_of::() as UINT); - - if data.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = data.data.mouse(); - if mouse.usFlags & winuser::MOUSE_MOVE_RELATIVE == winuser::MOUSE_MOVE_RELATIVE { - let x = mouse.lLastX as f64; - let y = mouse.lLastY as f64; - - if x != 0.0 { - send_event(Event::DeviceEvent { - device_id: DEVICE_ID, - event: Motion { axis: 0, value: x } - }); + use events::DeviceEvent::{Motion, MouseMotion, MouseWheel, Button, Key}; + use events::MouseScrollDelta::LineDelta; + use events::ElementState::{Pressed, Released}; + + if let Some(data) = get_raw_input_data(lparam as _) { + let device_id = wrap_device_id(data.header.hDevice as _); + + if data.header.dwType == winuser::RIM_TYPEMOUSE { + let mouse = data.data.mouse(); + + if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + let x = mouse.lLastX as f64; + let y = mouse.lLastY as f64; + + if x != 0.0 { + send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 0, value: x } + }); + } + + if y != 0.0 { + send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 1, value: y } + }); + } + + if x != 0.0 || y != 0.0 { + send_event(Event::DeviceEvent { + device_id, + event: MouseMotion { delta: (x, y) } + }); + } } - if y != 0.0 { + if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; send_event(Event::DeviceEvent { - device_id: DEVICE_ID, - event: Motion { axis: 1, value: y } + device_id, + event: MouseWheel { delta: LineDelta(0.0, delta as f32) } }); } - if x != 0.0 || y != 0.0 { + let button_state = get_raw_mouse_button_state(mouse.usButtonFlags); + // Left, middle, and right, respectively. + for (index, state) in button_state.iter().enumerate() { + if let Some(state) = *state { + // This gives us consistency with X11, since there doesn't + // seem to be anything else reasonable to do for a mouse + // button ID. + let button = (index + 1) as _; + send_event(Event::DeviceEvent { + device_id, + event: Button { + button, + state, + } + }); + } + } + } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard = data.data.keyboard(); + + let pressed = keyboard.Message == winuser::WM_KEYDOWN + || keyboard.Message == winuser::WM_SYSKEYDOWN; + let released = keyboard.Message == winuser::WM_KEYUP + || keyboard.Message == winuser::WM_SYSKEYUP; + + if pressed || released { + let state = if pressed { + Pressed + } else { + Released + }; + + let scancode = keyboard.MakeCode as _; + let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _); + let vkey = vkey_left_right( + keyboard.VKey as _, + scancode, + extended, + ); + let virtual_keycode = vkey_to_winit_vkey(vkey); + send_event(Event::DeviceEvent { - device_id: DEVICE_ID, - event: MouseMotion { delta: (x, y) } + device_id, + event: Key(KeyboardInput { + scancode, + state, + virtual_keycode, + modifiers: event::get_key_mods(), + }), }); } } - - 0 - } else { - winuser::DefWindowProcW(window, msg, wparam, lparam) } + + winuser::DefWindowProcW(window, msg, wparam, lparam) }, winuser::WM_TOUCH => { diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 19f2c02149..0ea6ab886b 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -18,10 +18,25 @@ unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {} // TODO: document what this means pub type Cursor = *const winapi::ctypes::wchar_t; -// Constant device ID, to be removed when this backend is updated to report real device IDs. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId; -const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId); +pub struct DeviceId(u32); + +impl DeviceId { + pub fn get_persistent_identifier(&self) -> Option { + if self.0 != 0 { + raw_input::get_raw_input_device_name(self.0 as _) + } else { + None + } + } +} + +// Constant device ID, to be removed when this backend is updated to report real device IDs. +const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId(0)); + +fn wrap_device_id(id: u32) -> ::DeviceId { + ::DeviceId(DeviceId(id)) +} #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WindowId(HWND); @@ -31,4 +46,6 @@ unsafe impl Sync for WindowId {} mod event; mod events_loop; mod monitor; +mod raw_input; +mod util; mod window; diff --git a/src/platform/windows/monitor.rs b/src/platform/windows/monitor.rs index 4f9eaadd41..92e9dd3046 100644 --- a/src/platform/windows/monitor.rs +++ b/src/platform/windows/monitor.rs @@ -6,7 +6,7 @@ use winapi::um::winuser; use std::collections::VecDeque; use std::{mem, ptr}; -use super::EventsLoop; +use super::{EventsLoop, util}; /// Win32 implementation of the main `MonitorId` object. #[derive(Clone)] @@ -44,12 +44,6 @@ struct HMonitor(HMONITOR); unsafe impl Send for HMonitor {} -fn wchar_as_string(wchar: &[wchar_t]) -> String { - String::from_utf16_lossy(wchar) - .trim_right_matches(0 as char) - .to_string() -} - unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL { let monitors = data as *mut VecDeque; @@ -67,7 +61,7 @@ unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: L (*monitors).push_back(MonitorId { adapter_name: monitor_info.szDevice, hmonitor: HMonitor(hmonitor), - monitor_name: wchar_as_string(&monitor_info.szDevice), + monitor_name: util::wchar_to_string(&monitor_info.szDevice), primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0, position, dimensions, @@ -109,7 +103,7 @@ impl EventsLoop { MonitorId { adapter_name: monitor_info.szDevice, hmonitor: super::monitor::HMonitor(hmonitor), - monitor_name: wchar_as_string(&monitor_info.szDevice), + monitor_name: util::wchar_to_string(&monitor_info.szDevice), primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0, position, dimensions, diff --git a/src/platform/windows/raw_input.rs b/src/platform/windows/raw_input.rs new file mode 100644 index 0000000000..2f6e90594f --- /dev/null +++ b/src/platform/windows/raw_input.rs @@ -0,0 +1,235 @@ +use std::mem::{self, size_of}; +use std::ptr; + +use winapi::ctypes::wchar_t; +use winapi::shared::minwindef::{UINT, USHORT, TRUE}; +use winapi::shared::hidusage::{ + HID_USAGE_PAGE_GENERIC, + HID_USAGE_GENERIC_MOUSE, + HID_USAGE_GENERIC_KEYBOARD, +}; +use winapi::shared::windef::HWND; +use winapi::um::winnt::HANDLE; +use winapi::um::winuser::{ + self, + RAWINPUTDEVICELIST, + RID_DEVICE_INFO, + RID_DEVICE_INFO_MOUSE, + RID_DEVICE_INFO_KEYBOARD, + RID_DEVICE_INFO_HID, + RIM_TYPEMOUSE, + RIM_TYPEKEYBOARD, + RIM_TYPEHID, + RIDI_DEVICEINFO, + RIDI_DEVICENAME, + RAWINPUTDEVICE, + RIDEV_DEVNOTIFY, + RIDEV_INPUTSINK, + HRAWINPUT, + RAWINPUT, + RAWINPUTHEADER, + RID_INPUT, +}; + +use platform::platform::util; +use events::ElementState; + +#[allow(dead_code)] +pub fn get_raw_input_device_list() -> Option> { + let list_size = size_of::() as UINT; + + let mut num_devices = 0; + let status = unsafe { winuser::GetRawInputDeviceList( + ptr::null_mut(), + &mut num_devices, + list_size, + ) }; + + if status == UINT::max_value() { + return None; + } + + let mut buffer = Vec::with_capacity(num_devices as _); + + let num_stored = unsafe { winuser::GetRawInputDeviceList( + buffer.as_ptr() as _, + &mut num_devices, + list_size, + ) }; + + if num_stored == UINT::max_value() { + return None; + } + + debug_assert_eq!(num_devices, num_stored); + + unsafe { buffer.set_len(num_devices as _) }; + + Some(buffer) +} + +#[allow(dead_code)] +pub enum RawDeviceInfo { + Mouse(RID_DEVICE_INFO_MOUSE), + Keyboard(RID_DEVICE_INFO_KEYBOARD), + Hid(RID_DEVICE_INFO_HID), +} + +impl From for RawDeviceInfo { + fn from(info: RID_DEVICE_INFO) -> Self { + unsafe { + match info.dwType { + RIM_TYPEMOUSE => RawDeviceInfo::Mouse(*info.u.mouse()), + RIM_TYPEKEYBOARD => RawDeviceInfo::Keyboard(*info.u.keyboard()), + RIM_TYPEHID => RawDeviceInfo::Hid(*info.u.hid()), + _ => unreachable!(), + } + } + } +} + +#[allow(dead_code)] +pub fn get_raw_input_device_info(handle: HANDLE) -> Option { + let mut info: RID_DEVICE_INFO = unsafe { mem::uninitialized() }; + let info_size = size_of::() as UINT; + + info.cbSize = info_size; + + let mut minimum_size = 0; + let status = unsafe { winuser::GetRawInputDeviceInfoW( + handle, + RIDI_DEVICEINFO, + &mut info as *mut _ as _, + &mut minimum_size, + ) }; + + if status == UINT::max_value() || status == 0 { + return None; + } + + debug_assert_eq!(info_size, status); + + Some(info.into()) +} + +pub fn get_raw_input_device_name(handle: HANDLE) -> Option { + let mut minimum_size = 0; + let status = unsafe { winuser::GetRawInputDeviceInfoW( + handle, + RIDI_DEVICENAME, + ptr::null_mut(), + &mut minimum_size, + ) }; + + if status != 0 { + return None; + } + + let mut name: Vec = Vec::with_capacity(minimum_size as _); + + let status = unsafe { winuser::GetRawInputDeviceInfoW( + handle, + RIDI_DEVICENAME, + name.as_ptr() as _, + &mut minimum_size, + ) }; + + if status == UINT::max_value() || status == 0 { + return None; + } + + debug_assert_eq!(minimum_size, status); + + unsafe { name.set_len(minimum_size as _) }; + + Some(util::wchar_to_string(&name)) +} + +pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool { + let device_size = size_of::() as UINT; + + let success = unsafe { winuser::RegisterRawInputDevices( + devices.as_ptr() as _, + devices.len() as _, + device_size, + ) }; + + success == TRUE +} + +pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> bool { + // RIDEV_DEVNOTIFY: receive hotplug events + // RIDEV_INPUTSINK: receive events even if we're not in the foreground + let flags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + + let devices: [RAWINPUTDEVICE; 2] = [ + RAWINPUTDEVICE { + usUsagePage: HID_USAGE_PAGE_GENERIC, + usUsage: HID_USAGE_GENERIC_MOUSE, + dwFlags: flags, + hwndTarget: window_handle, + }, + RAWINPUTDEVICE { + usUsagePage: HID_USAGE_PAGE_GENERIC, + usUsage: HID_USAGE_GENERIC_KEYBOARD, + dwFlags: flags, + hwndTarget: window_handle, + }, + ]; + + register_raw_input_devices(&devices) +} + +pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { + let mut data: RAWINPUT = unsafe { mem::uninitialized() }; + let mut data_size = size_of::() as UINT; + let header_size = size_of::() as UINT; + + let status = unsafe { winuser::GetRawInputData( + handle, + RID_INPUT, + &mut data as *mut _ as _, + &mut data_size, + header_size, + ) }; + + if status == UINT::max_value() || status == 0 { + return None; + } + + Some(data) +} + + +fn button_flags_to_element_state(button_flags: USHORT, down_flag: USHORT, up_flag: USHORT) + -> Option +{ + // We assume the same button won't be simultaneously pressed and released. + if util::has_flag(button_flags, down_flag) { + Some(ElementState::Pressed) + } else if util::has_flag(button_flags, up_flag) { + Some(ElementState::Released) + } else { + None + } +} + +pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option; 3] { + [ + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_LEFT_BUTTON_DOWN, + winuser::RI_MOUSE_LEFT_BUTTON_UP, + ), + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_MIDDLE_BUTTON_DOWN, + winuser::RI_MOUSE_MIDDLE_BUTTON_UP, + ), + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_RIGHT_BUTTON_DOWN, + winuser::RI_MOUSE_RIGHT_BUTTON_UP, + ), + ] +} diff --git a/src/platform/windows/util.rs b/src/platform/windows/util.rs new file mode 100644 index 0000000000..3377a54292 --- /dev/null +++ b/src/platform/windows/util.rs @@ -0,0 +1,16 @@ +use std::ops::BitAnd; + +use winapi::ctypes::wchar_t; + +pub fn has_flag(bitset: T, flag: T) -> bool +where T: + Copy + PartialEq + BitAnd +{ + bitset & flag == flag +} + +pub fn wchar_to_string(wchar: &[wchar_t]) -> String { + String::from_utf16_lossy(wchar) + .trim_right_matches(0 as char) + .to_string() +} diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index e0a37a0ce1..2e71f88ba0 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -14,6 +14,7 @@ use std::cell::Cell; use platform::platform::events_loop::{self, DESTROY_MSG_ID}; use platform::platform::EventsLoop; use platform::platform::PlatformSpecificWindowBuilderAttributes; +use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input; use platform::platform::WindowId; use CreationError; @@ -24,7 +25,6 @@ use MonitorId as RootMonitorId; use winapi::shared::minwindef::{UINT, DWORD, BOOL}; use winapi::shared::windef::{HWND, HDC, RECT, POINT}; -use winapi::shared::hidusage; use winapi::um::{winuser, dwmapi, libloaderapi, processthreadsapi}; use winapi::um::winnt::{LPCWSTR, LONG, HRESULT}; use winapi::um::combaseapi; @@ -727,16 +727,8 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild WindowWrapper(handle, hdc) }; - // Set up raw mouse input - { - let mut rid: winuser::RAWINPUTDEVICE = mem::uninitialized(); - rid.usUsagePage = hidusage::HID_USAGE_PAGE_GENERIC; - rid.usUsage = hidusage::HID_USAGE_GENERIC_MOUSE; - rid.dwFlags = 0; - rid.hwndTarget = real_window.0; - - winuser::RegisterRawInputDevices(&rid, 1, mem::size_of::() as u32); - } + // Set up raw input + register_all_mice_and_keyboards_for_raw_input(real_window.0); // Register for touch events if applicable {