From 700e65e1397f5e168d77c0d8e3a500d2670c646a Mon Sep 17 00:00:00 2001 From: Francesca Plebani Date: Sun, 4 Nov 2018 15:28:44 -0500 Subject: [PATCH 01/34] Initial implementation --- Cargo.toml | 1 + examples/gamepad.rs | 31 +++ src/lib.rs | 4 +- src/platform/windows/events_loop.rs | 64 +++++- src/platform/windows/raw_input.rs | 313 ++++++++++++++++++++++++++-- src/platform/windows/util.rs | 2 + src/platform/windows/window.rs | 4 +- src/util.rs | 20 ++ 8 files changed, 415 insertions(+), 24 deletions(-) create mode 100644 examples/gamepad.rs create mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index 371d576ac3..3a85d73c86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ features = [ "combaseapi", "dwmapi", "errhandlingapi", + "hidpi", "hidusage", "libloaderapi", "objbase", diff --git a/examples/gamepad.rs b/examples/gamepad.rs new file mode 100644 index 0000000000..26a82bab04 --- /dev/null +++ b/examples/gamepad.rs @@ -0,0 +1,31 @@ +extern crate winit; + +fn main() { + let mut events_loop = winit::EventsLoop::new(); + + let _window = winit::WindowBuilder::new() + .with_title("The world's worst video game") + .build(&events_loop) + .unwrap(); + + events_loop.run_forever(|event| { + match event { + winit::Event::DeviceEvent { device_id, event } => match event { + winit::DeviceEvent::Button { button, state } => { + println!("[{:?}] {:#?}", device_id, event); + winit::ControlFlow::Continue + }, + winit::DeviceEvent::Motion { axis, value } => { + println!("[{:?}] {:#?}", device_id, event); + winit::ControlFlow::Continue + }, + _ => winit::ControlFlow::Continue, + }, + winit::Event::WindowEvent { + event: winit::WindowEvent::CloseRequested, + .. + } => winit::ControlFlow::Break, + _ => winit::ControlFlow::Continue, + } + }); +} diff --git a/src/lib.rs b/src/lib.rs index 0d2b59fa41..4e91fb461d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,11 +124,11 @@ pub use icon::*; pub mod dpi; mod events; mod icon; +pub mod os; mod platform; +mod util; mod window; -pub mod os; - /// Represents a window. /// /// # Example diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index b14faa1075..060b3d9b8a 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -59,7 +59,11 @@ use platform::platform::dpi::{ use platform::platform::drop_handler::FileDropHandler; use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey}; use platform::platform::icon::WinIcon; -use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state}; +use platform::platform::raw_input::{ + Gamepad, + get_raw_input_data, + get_raw_mouse_button_state, +}; use platform::platform::window::adjust_size; /// Contains saved window info for switching between fullscreen @@ -140,6 +144,12 @@ pub struct EventsLoop { receiver: mpsc::Receiver, } +lazy_static! { + pub static ref GAMEPADS: Arc>> = Arc::new(Mutex::new( + HashMap::with_capacity(4) + )); +} + impl EventsLoop { pub fn new() -> EventsLoop { Self::with_dpi_awareness(true) @@ -824,7 +834,7 @@ pub unsafe extern "system" fn callback( use events::MouseScrollDelta::LineDelta; use events::ElementState::{Pressed, Released}; - if let Some(data) = get_raw_input_data(lparam as _) { + if let Some(mut data) = get_raw_input_data(lparam as _) { let device_id = wrap_device_id(data.header.hDevice as _); if data.header.dwType == winuser::RIM_TYPEMOUSE { @@ -917,6 +927,56 @@ pub unsafe extern "system" fn callback( }); } } + } else if data.header.dwType == winuser::RIM_TYPEHID { + let handle = data.header.hDevice; + let key = handle as isize; + let mut gamepad_mutex = GAMEPADS.lock().unwrap(); + let gamepad_registered = gamepad_mutex.contains_key(&key); + if !gamepad_registered { + if let Some(gamepad) = Gamepad::new(handle) { + gamepad_mutex.insert(key, gamepad); + } + } + gamepad_mutex + .get_mut(&key) + .and_then(|gamepad| gamepad + .update_state(data) + .map(|_| gamepad)) + .map(|gamepad| { + for (index, (button, prev_button)) in gamepad.button_state + .iter() + .zip(gamepad.prev_button_state.iter()) + .enumerate() + { + if button != prev_button { + let state = if *button { Pressed } else { Released }; + send_event(Event::DeviceEvent { + device_id, + event: Button { + button: index as _, + state, + }, + }); + } + } + + for (index, axis) in gamepad.axis_state + .iter() + .enumerate() + { + if axis.value != axis.prev_value { + send_event(Event::DeviceEvent { + device_id, + event: Motion { + axis: index as _, + value: axis.value, + }, + }); + } + } + }); + } else { + unreachable!(); } } diff --git a/src/platform/windows/raw_input.rs b/src/platform/windows/raw_input.rs index 2f6e90594f..123848334b 100644 --- a/src/platform/windows/raw_input.rs +++ b/src/platform/windows/raw_input.rs @@ -1,34 +1,56 @@ +use std::{fmt, ptr}; +use std::cmp::max; use std::mem::{self, size_of}; -use std::ptr; use winapi::ctypes::wchar_t; -use winapi::shared::minwindef::{UINT, USHORT, TRUE}; +use winapi::shared::minwindef::{TRUE, UINT, USHORT}; +use winapi::shared::hidpi::{ + HidP_GetButtonCaps, + HidP_GetCaps, + HidP_GetScaledUsageValue, + HidP_GetUsagesEx, + HidP_GetUsageValue, + HidP_GetValueCaps, + HidP_Input, + /*HIDP_STATUS_BUFFER_TOO_SMALL, + HIDP_STATUS_INCOMPATIBLE_REPORT_ID, + HIDP_STATUS_INVALID_PREPARSED_DATA, + HIDP_STATUS_INVALID_REPORT_LENGTH, + HIDP_STATUS_INVALID_REPORT_TYPE,*/ + HIDP_STATUS_SUCCESS, + HIDP_VALUE_CAPS, + PHIDP_PREPARSED_DATA, +}; use winapi::shared::hidusage::{ HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE, HID_USAGE_GENERIC_KEYBOARD, + HID_USAGE_GENERIC_JOYSTICK, + HID_USAGE_GENERIC_GAMEPAD, }; use winapi::shared::windef::HWND; -use winapi::um::winnt::HANDLE; +use winapi::um::winnt::{HANDLE, PCHAR}; use winapi::um::winuser::{ self, + HRAWINPUT, + RAWHID, + RAWINPUT, + RAWINPUTDEVICE, RAWINPUTDEVICELIST, + RAWINPUTHEADER, + RID_INPUT, RID_DEVICE_INFO, - RID_DEVICE_INFO_MOUSE, - RID_DEVICE_INFO_KEYBOARD, RID_DEVICE_INFO_HID, + RID_DEVICE_INFO_KEYBOARD, + RID_DEVICE_INFO_MOUSE, + RIDEV_DEVNOTIFY, + RIDEV_INPUTSINK, + RIDI_DEVICEINFO, + RIDI_DEVICENAME, + RIDI_PREPARSEDDATA, RIM_TYPEMOUSE, RIM_TYPEKEYBOARD, RIM_TYPEHID, - RIDI_DEVICEINFO, - RIDI_DEVICENAME, - RAWINPUTDEVICE, - RIDEV_DEVNOTIFY, - RIDEV_INPUTSINK, - HRAWINPUT, - RAWINPUT, - RAWINPUTHEADER, - RID_INPUT, }; use platform::platform::util; @@ -88,7 +110,14 @@ impl From for RawDeviceInfo { } } -#[allow(dead_code)] +#[derive(Debug)] +#[repr(usize)] +pub enum RawDeviceInfoComamnd { + Info = RIDI_DEVICEINFO as _, + Name = RIDI_DEVICENAME as _, + PreParseData = RIDI_PREPARSEDDATA as _, +} + 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; @@ -145,6 +174,39 @@ pub fn get_raw_input_device_name(handle: HANDLE) -> Option { Some(util::wchar_to_string(&name)) } +pub fn get_raw_input_pre_parse_info(handle: HANDLE) -> Option> { + let mut minimum_size = 0; + let status = unsafe { winuser::GetRawInputDeviceInfoW( + handle, + RIDI_PREPARSEDDATA, + ptr::null_mut(), + &mut minimum_size, + ) }; + + if status != 0 { + return None; + } + + let mut buf: Vec = Vec::with_capacity(minimum_size as _); + + let status = unsafe { winuser::GetRawInputDeviceInfoW( + handle, + RIDI_PREPARSEDDATA, + buf.as_ptr() as _, + &mut minimum_size, + ) }; + + if status == UINT::max_value() || status == 0 { + return None; + } + + debug_assert_eq!(minimum_size, status); + + unsafe { buf.set_len(minimum_size as _) }; + + Some(buf) +} + pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool { let device_size = size_of::() as UINT; @@ -157,12 +219,12 @@ pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool { success == TRUE } -pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> bool { +pub fn register_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] = [ + let devices: [RAWINPUTDEVICE; 5] = [ RAWINPUTDEVICE { usUsagePage: HID_USAGE_PAGE_GENERIC, usUsage: HID_USAGE_GENERIC_MOUSE, @@ -175,6 +237,24 @@ pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> boo dwFlags: flags, hwndTarget: window_handle, }, + RAWINPUTDEVICE { + usUsagePage: HID_USAGE_PAGE_GENERIC, + usUsage: HID_USAGE_GENERIC_JOYSTICK, + dwFlags: flags, + hwndTarget: window_handle, + }, + RAWINPUTDEVICE { + usUsagePage: HID_USAGE_PAGE_GENERIC, + usUsage: HID_USAGE_GENERIC_GAMEPAD, + dwFlags: flags, + hwndTarget: window_handle, + }, + RAWINPUTDEVICE { + usUsagePage: HID_USAGE_PAGE_GENERIC, + usUsage: 0x08, // multi-axis + dwFlags: flags, + hwndTarget: window_handle, + }, ]; register_raw_input_devices(&devices) @@ -200,7 +280,6 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { Some(data) } - fn button_flags_to_element_state(button_flags: USHORT, down_flag: USHORT, up_flag: USHORT) -> Option { @@ -233,3 +312,201 @@ pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option ), ] } + +pub struct Axis { + caps: HIDP_VALUE_CAPS, + pub value: f64, + pub prev_value: f64, + //active: bool, +} + +impl fmt::Debug for Axis { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[derive(Debug)] + struct Axis { + value: f64, + prev_value: f64, + } + + let axis_proxy = Axis { + value: self.value, + prev_value: self.prev_value, + }; + + axis_proxy.fmt(f) + } +} + +#[derive(Debug)] +pub struct Gamepad { + handle: HANDLE, + pre_parsed_data: Vec, + button_count: usize, + pub button_state: Vec, + pub prev_button_state: Vec, + axis_count: usize, + pub axis_state: Vec, +} + +impl Gamepad { + pub fn new(handle: HANDLE) -> Option { + let pre_parsed_data = get_raw_input_pre_parse_info(handle)?; + let data_ptr = pre_parsed_data.as_ptr() as PHIDP_PREPARSED_DATA; + let mut caps = unsafe { mem::uninitialized() }; + let status = unsafe { HidP_GetCaps(data_ptr, &mut caps) }; + if status != HIDP_STATUS_SUCCESS { + return None; + } + let mut button_caps_len = caps.NumberInputButtonCaps; + let mut button_caps = Vec::with_capacity(button_caps_len as _); + let status = unsafe { HidP_GetButtonCaps( + HidP_Input, + button_caps.as_mut_ptr(), + &mut button_caps_len, + data_ptr, + ) }; + if status != HIDP_STATUS_SUCCESS { + return None; + } + unsafe { button_caps.set_len(button_caps_len as _) }; + let mut button_count = 0; + for button_cap in button_caps { + let range = unsafe { button_cap.u.Range() }; + button_count = max(button_count, range.UsageMax); + } + let button_state = vec![false; button_count as usize]; + let mut axis_caps_len = caps.NumberInputValueCaps; + let mut axis_caps = Vec::with_capacity(axis_caps_len as _); + let status = unsafe { HidP_GetValueCaps( + HidP_Input, + axis_caps.as_mut_ptr(), + &mut axis_caps_len, + data_ptr, + ) }; + if status != HIDP_STATUS_SUCCESS { + return None; + } + unsafe { axis_caps.set_len(axis_caps_len as _) }; + let mut axis_state = Vec::with_capacity(axis_caps_len as _); + let mut axis_count = 0; + for (axis_index, axis_cap) in axis_caps.drain(0..).enumerate() { + axis_state.push(Axis { + caps: axis_cap, + value: 0.0, + prev_value: 0.0, + //active: true, + }); + axis_count = max(axis_count, axis_index + 1); + } + Some(Gamepad { + handle, + pre_parsed_data, + button_count: button_count as usize, + button_state: button_state.clone(), + prev_button_state: button_state, + axis_count, + axis_state, + }) + } + + fn pre_parsed_data_ptr(&mut self) -> PHIDP_PREPARSED_DATA { + self.pre_parsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA + } + + fn update_button_state(&mut self, hid: &mut RAWHID) -> Option<()> { + let pre_parsed_data_ptr = self.pre_parsed_data_ptr(); + self.prev_button_state = mem::replace( + &mut self.button_state, + vec![false; self.button_count], + ); + let mut usages_len = 0; + // This is the officially documented way to get the required length, but it nonetheless returns + // `HIDP_STATUS_BUFFER_TOO_SMALL`... + unsafe { HidP_GetUsagesEx( + HidP_Input, + 0, + ptr::null_mut(), + &mut usages_len, + pre_parsed_data_ptr, + hid.bRawData.as_mut_ptr() as PCHAR, + hid.dwSizeHid, + ) }; + let mut usages = Vec::with_capacity(usages_len as _); + let status = unsafe { HidP_GetUsagesEx( + HidP_Input, + 0, + usages.as_mut_ptr(), + &mut usages_len, + pre_parsed_data_ptr, + hid.bRawData.as_mut_ptr() as PCHAR, + hid.dwSizeHid, + ) }; + if status != HIDP_STATUS_SUCCESS { + return None; + } + unsafe { usages.set_len(usages_len as _) }; + for usage in usages { + if usage.UsagePage != 0xFF << 8 { + let button_index = (usage.Usage - 1) as usize; + self.button_state[button_index] = true; + } + } + Some(()) + } + + fn update_axis_state(&mut self, hid: &mut RAWHID) -> Option<()> { + let pre_parsed_data_ptr = self.pre_parsed_data_ptr(); + for axis in &mut self.axis_state { + let (status, axis_value) = if axis.caps.LogicalMin < 0 { + let mut scaled_axis_value = 0; + let status = unsafe { HidP_GetScaledUsageValue( + HidP_Input, + axis.caps.UsagePage, + 0, + axis.caps.u.Range().UsageMin, + &mut scaled_axis_value, + pre_parsed_data_ptr, + hid.bRawData.as_mut_ptr() as PCHAR, + hid.dwSizeHid, + ) }; + (status, scaled_axis_value as f64) + } else { + let mut axis_value = 0; + let status = unsafe { HidP_GetUsageValue( + HidP_Input, + axis.caps.UsagePage, + 0, + axis.caps.u.Range().UsageMin, + &mut axis_value, + pre_parsed_data_ptr, + hid.bRawData.as_mut_ptr() as PCHAR, + hid.dwSizeHid, + ) }; + (status, axis_value as f64) + }; + if status != HIDP_STATUS_SUCCESS { + return None; + } + axis.prev_value = axis.value; + axis.value = util::normalize_symmetric( + axis_value, + axis.caps.LogicalMin as f64, + axis.caps.LogicalMax as f64, + ); + } + Some(()) + } + + pub fn update_state(&mut self, mut input: RAWINPUT) -> Option<()> { + if input.header.dwType != winuser::RIM_TYPEHID { + return None; + } + let hid = unsafe { input.data.hid_mut() }; + self.update_button_state(hid)?; + self.update_axis_state(hid)?; + Some(()) + } +} + +unsafe impl Send for Gamepad {} +unsafe impl Sync for Gamepad {} diff --git a/src/platform/windows/util.rs b/src/platform/windows/util.rs index 0d0a2efcab..cf03f52b8f 100644 --- a/src/platform/windows/util.rs +++ b/src/platform/windows/util.rs @@ -21,6 +21,8 @@ use winapi::um::winnt::{ }; use winapi::um::winuser; +pub use util::*; + pub fn has_flag(bitset: T, flag: T) -> bool where T: Copy + PartialEq + BitAnd diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index d7d789a13a..6594a3efec 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -32,7 +32,7 @@ use platform::platform::events_loop::{self, EventsLoop, DESTROY_MSG_ID, INITIAL_ use platform::platform::events_loop::WindowState; use platform::platform::icon::{self, IconType, WinIcon}; use platform::platform::monitor::get_available_monitors; -use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input; +use platform::platform::raw_input::register_for_raw_input; use platform::platform::util; const WS_RESIZABLE: DWORD = winuser::WS_SIZEBOX | winuser::WS_MAXIMIZEBOX; @@ -966,7 +966,7 @@ unsafe fn init( }; // Set up raw input - register_all_mice_and_keyboards_for_raw_input(real_window.0); + register_for_raw_input(real_window.0); // Register for touch events if applicable { diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000000..480841561f --- /dev/null +++ b/src/util.rs @@ -0,0 +1,20 @@ +pub fn clamp(value: f64, min: f64, max: f64) -> f64 { + if value > max { + max + } else if value < min { + min + } else { + value + } +} + +pub fn normalize_asymmetric(value: f64, min: f64, max: f64) -> f64 { + let range = max - min; + let translated = value - min; + let scaled = translated / range; + clamp(scaled, 0.0, 1.0) +} + +pub fn normalize_symmetric(value: f64, min: f64, max: f64) -> f64 { + (2.0 * normalize_asymmetric(value, min, max)) - 1.0 +} From b5523700e2236f3ab2a3010d10996db90c44d4f3 Mon Sep 17 00:00:00 2001 From: Francesca Plebani Date: Sun, 4 Nov 2018 15:29:09 -0500 Subject: [PATCH 02/34] Corrected RAWINPUT buffer sizing --- examples/gamepad.rs | 7 ++--- src/platform/windows/events_loop.rs | 18 ++++++----- src/platform/windows/raw_input.rs | 48 +++++++++++++++++------------ 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 26a82bab04..e907ce6c4e 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -11,11 +11,8 @@ fn main() { events_loop.run_forever(|event| { match event { winit::Event::DeviceEvent { device_id, event } => match event { - winit::DeviceEvent::Button { button, state } => { - println!("[{:?}] {:#?}", device_id, event); - winit::ControlFlow::Continue - }, - winit::DeviceEvent::Motion { axis, value } => { + winit::DeviceEvent::Button { .. } + | winit::DeviceEvent::Motion { .. } => { println!("[{:?}] {:#?}", device_id, event); winit::ControlFlow::Continue }, diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 060b3d9b8a..a52a448438 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -835,10 +835,12 @@ pub unsafe extern "system" fn callback( use events::ElementState::{Pressed, Released}; if let Some(mut data) = get_raw_input_data(lparam as _) { - let device_id = wrap_device_id(data.header.hDevice as _); + let input = data.as_mut_ptr() as *mut winuser::RAWINPUT; - if data.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = data.data.mouse(); + let device_id = wrap_device_id((*input).header.hDevice as _); + + if (*input).header.dwType == winuser::RIM_TYPEMOUSE { + let mouse = (*input).data.mouse(); if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { let x = mouse.lLastX as f64; @@ -891,8 +893,8 @@ pub unsafe extern "system" fn callback( }); } } - } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { - let keyboard = data.data.keyboard(); + } else if (*input).header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard = (*input).data.keyboard(); let pressed = keyboard.Message == winuser::WM_KEYDOWN || keyboard.Message == winuser::WM_SYSKEYDOWN; @@ -927,8 +929,8 @@ pub unsafe extern "system" fn callback( }); } } - } else if data.header.dwType == winuser::RIM_TYPEHID { - let handle = data.header.hDevice; + } else if (*input).header.dwType == winuser::RIM_TYPEHID { + let handle = (*input).header.hDevice; let key = handle as isize; let mut gamepad_mutex = GAMEPADS.lock().unwrap(); let gamepad_registered = gamepad_mutex.contains_key(&key); @@ -940,7 +942,7 @@ pub unsafe extern "system" fn callback( gamepad_mutex .get_mut(&key) .and_then(|gamepad| gamepad - .update_state(data) + .update_state(input) .map(|_| gamepad)) .map(|gamepad| { for (index, (button, prev_button)) in gamepad.button_state diff --git a/src/platform/windows/raw_input.rs b/src/platform/windows/raw_input.rs index 123848334b..009ef6540e 100644 --- a/src/platform/windows/raw_input.rs +++ b/src/platform/windows/raw_input.rs @@ -3,7 +3,7 @@ use std::cmp::max; use std::mem::{self, size_of}; use winapi::ctypes::wchar_t; -use winapi::shared::minwindef::{TRUE, UINT, USHORT}; +use winapi::shared::minwindef::{BYTE, TRUE, UINT, USHORT}; use winapi::shared::hidpi::{ HidP_GetButtonCaps, HidP_GetCaps, @@ -53,8 +53,8 @@ use winapi::um::winuser::{ RIM_TYPEHID, }; -use platform::platform::util; use events::ElementState; +use platform::platform::util; #[allow(dead_code)] pub fn get_raw_input_device_list() -> Option> { @@ -110,14 +110,7 @@ impl From for RawDeviceInfo { } } -#[derive(Debug)] -#[repr(usize)] -pub enum RawDeviceInfoComamnd { - Info = RIDI_DEVICEINFO as _, - Name = RIDI_DEVICENAME as _, - PreParseData = RIDI_PREPARSEDDATA as _, -} - +#[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; @@ -220,8 +213,8 @@ pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool { } pub fn register_for_raw_input(window_handle: HWND) -> bool { - // RIDEV_DEVNOTIFY: receive hotplug events - // RIDEV_INPUTSINK: receive events even if we're not in the foreground + // `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; 5] = [ @@ -260,15 +253,29 @@ pub fn register_for_raw_input(window_handle: HWND) -> bool { 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; +pub fn get_raw_input_data(handle: HRAWINPUT) -> Option> { + let mut data_size = 0; let header_size = size_of::() as UINT; + unsafe { winuser::GetRawInputData( + handle, + RID_INPUT, + ptr::null_mut(), + &mut data_size, + header_size, + ) }; + + let alignment_remainder = data_size % 8; + if alignment_remainder != 0 { + data_size += 8 - alignment_remainder; + } + + let mut data = Vec::with_capacity(data_size as _); + let status = unsafe { winuser::GetRawInputData( handle, RID_INPUT, - &mut data as *mut _ as _, + data.as_mut_ptr() as _, &mut data_size, header_size, ) }; @@ -277,6 +284,8 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { return None; } + unsafe { data.set_len(data_size as _) }; + Some(data) } @@ -348,6 +357,7 @@ pub struct Gamepad { pub axis_state: Vec, } +// Reference: https://chromium.googlesource.com/chromium/chromium/+/trunk/content/browser/gamepad/raw_input_data_fetcher_win.cc impl Gamepad { pub fn new(handle: HANDLE) -> Option { let pre_parsed_data = get_raw_input_pre_parse_info(handle)?; @@ -497,11 +507,11 @@ impl Gamepad { Some(()) } - pub fn update_state(&mut self, mut input: RAWINPUT) -> Option<()> { - if input.header.dwType != winuser::RIM_TYPEHID { + pub unsafe fn update_state(&mut self, input: *mut RAWINPUT) -> Option<()> { + if (*input).header.dwType != winuser::RIM_TYPEHID { return None; } - let hid = unsafe { input.data.hid_mut() }; + let hid = (*input).data.hid_mut(); self.update_button_state(hid)?; self.update_axis_state(hid)?; Some(()) From 8c7f7205e7eda56844aad38a50c4048ee740aa75 Mon Sep 17 00:00:00 2001 From: Francesca Plebani Date: Sun, 4 Nov 2018 15:29:15 -0500 Subject: [PATCH 03/34] Mostly complete XInput implementation --- Cargo.toml | 5 + src/events.rs | 110 +++++++++++++++++- src/lib.rs | 4 + src/platform/windows/events_loop.rs | 105 +++++++++-------- src/platform/windows/gamepad.rs | 101 ++++++++++++++++ src/platform/windows/mod.rs | 2 + src/platform/windows/raw_input.rs | 42 +++++-- src/platform/windows/util.rs | 8 -- src/platform/windows/xinput.rs | 172 ++++++++++++++++++++++++++++ src/util.rs | 9 ++ 10 files changed, 486 insertions(+), 72 deletions(-) create mode 100644 src/platform/windows/gamepad.rs create mode 100644 src/platform/windows/xinput.rs diff --git a/Cargo.toml b/Cargo.toml index 3a85d73c86..25031823d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,10 @@ cocoa = "0.17" core-foundation = "0.6" core-graphics = "0.16" +[target.'cfg(target_os = "windows")'.dependencies] +regex = "1.0" +rusty-xinput = "1.0" + [target.'cfg(target_os = "windows")'.dependencies.winapi] version = "0.3.6" features = [ @@ -57,6 +61,7 @@ features = [ "wingdi", "winnt", "winuser", + "xinput", ] [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] diff --git a/src/events.rs b/src/events.rs index 6725b19a0d..f9eeb868d9 100644 --- a/src/events.rs +++ b/src/events.rs @@ -108,6 +108,104 @@ pub enum WindowEvent { HiDpiFactorChanged(f64), } +/// A hint suggesting the type of button that was pressed. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ButtonHint { + LeftMouse, + MiddleMouse, + RightMouse, + + Start, + Select, + + /// The north face button. + /// + /// * Nintendo: X + /// * Playstation: Triangle + /// * XBox: Y + North, + /// The south face button. + /// + /// * Nintendo: B + /// * Playstation: X + /// * XBox: A + South, + /// The east face button. + /// + /// * Nintendo: A + /// * Playstation: Circle + /// * XBox: B + East, + /// The west face button. + /// + /// * Nintendo: Y + /// * Playstation: Square + /// * XBox: X + West, + + LeftStick, + RightStick, + + LeftTrigger, + RightTrigger, + + LeftShoulder, + RightShoulder, + + DPadUp, + DPadDown, + DPadLeft, + DPadRight, +} + +impl ButtonHint { + #[inline] + pub fn is_mouse_button(&self) -> bool { + use ButtonHint::*; + match *self { + LeftMouse | MiddleMouse | RightMouse => true, + _ => false, + } + } + + #[inline] + pub fn is_gamepad_button(&self) -> bool { + !self.is_mouse_button() + } +} + +/// A hint suggesting the type of axis that moved. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum AxisHint { + MouseX, + MouseY, + + LeftStickX, + LeftStickY, + + RightStickX, + RightStickY, + + LeftTrigger, + RightTrigger, +} + +impl AxisHint { + #[inline] + pub fn is_mouse_axis(&self) -> bool { + use AxisHint::*; + match *self { + MouseX | MouseY => true, + _ => false, + } + } + + #[inline] + pub fn is_gamepad_axis(&self) -> bool { + !self.is_mouse_axis() + } +} + /// Represents raw hardware events that are not associated with any particular window. /// /// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person @@ -139,9 +237,17 @@ pub enum DeviceEvent { /// Motion on some analog axis. This event will be reported for all arbitrary input devices /// that winit supports on this platform, including mouse devices. If the device is a mouse /// device then this will be reported alongside the MouseMotion event. - Motion { axis: AxisId, value: f64 }, + Motion { + axis: AxisId, + hint: Option, + value: f64, + }, - Button { button: ButtonId, state: ElementState }, + Button { + button: ButtonId, + hint: Option, + state: ElementState, + }, Key(KeyboardInput), Text { codepoint: char }, } diff --git a/src/lib.rs b/src/lib.rs index 4e91fb461d..4b2fbf1fc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,10 @@ extern crate image; #[macro_use] extern crate serde; +#[cfg(target_os = "windows")] +extern crate regex; +#[cfg(target_os = "windows")] +extern crate rusty_xinput; #[cfg(target_os = "windows")] extern crate winapi; #[cfg(any(target_os = "macos", target_os = "ios"))] diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index a52a448438..dbaed61084 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -48,7 +48,7 @@ use { WindowEvent, WindowId as SuperWindowId, }; -use events::{DeviceEvent, Touch, TouchPhase}; +use events::{AxisHint, ButtonHint, DeviceEvent, Touch, TouchPhase}; use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util}; use platform::platform::dpi::{ become_dpi_aware, @@ -58,12 +58,9 @@ use platform::platform::dpi::{ }; use platform::platform::drop_handler::FileDropHandler; use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey}; +use platform::platform::gamepad::GAMEPADS; use platform::platform::icon::WinIcon; -use platform::platform::raw_input::{ - Gamepad, - get_raw_input_data, - get_raw_mouse_button_state, -}; +use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state}; use platform::platform::window::adjust_size; /// Contains saved window info for switching between fullscreen @@ -144,12 +141,6 @@ pub struct EventsLoop { receiver: mpsc::Receiver, } -lazy_static! { - pub static ref GAMEPADS: Arc>> = Arc::new(Mutex::new( - HashMap::with_capacity(4) - )); -} - impl EventsLoop { pub fn new() -> EventsLoop { Self::with_dpi_awareness(true) @@ -815,10 +806,19 @@ pub unsafe extern "system" fn callback( }, winuser::WM_INPUT_DEVICE_CHANGE => { - let event = match wparam as _ { - winuser::GIDC_ARRIVAL => DeviceEvent::Added, - winuser::GIDC_REMOVAL => DeviceEvent::Removed, - _ => unreachable!(), + let event = { + let mut gamepad_mutex = GAMEPADS.lock().unwrap(); + match wparam as _ { + winuser::GIDC_ARRIVAL => { + gamepad_mutex.get_or_add(lparam as _); + DeviceEvent::Added + }, + winuser::GIDC_REMOVAL => { + gamepad_mutex.remove(lparam as _); + DeviceEvent::Removed + }, + _ => unreachable!(), + } }; send_event(Event::DeviceEvent { @@ -849,14 +849,22 @@ pub unsafe extern "system" fn callback( if x != 0.0 { send_event(Event::DeviceEvent { device_id, - event: Motion { axis: 0, value: x } + event: Motion { + axis: 0, + hint: Some(AxisHint::MouseX), + value: x, + }, }); } if y != 0.0 { send_event(Event::DeviceEvent { device_id, - event: Motion { axis: 1, value: y } + event: Motion { + axis: 1, + hint: Some(AxisHint::MouseY), + value: y, + }, }); } @@ -888,6 +896,12 @@ pub unsafe extern "system" fn callback( device_id, event: Button { button, + hint: match button { + 1 => Some(ButtonHint::LeftMouse), + 2 => Some(ButtonHint::MiddleMouse), + 3 => Some(ButtonHint::RightMouse), + _ => None, + }, state, } }); @@ -931,50 +945,33 @@ pub unsafe extern "system" fn callback( } } else if (*input).header.dwType == winuser::RIM_TYPEHID { let handle = (*input).header.hDevice; - let key = handle as isize; let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - let gamepad_registered = gamepad_mutex.contains_key(&key); - if !gamepad_registered { - if let Some(gamepad) = Gamepad::new(handle) { - gamepad_mutex.insert(key, gamepad); - } - } gamepad_mutex - .get_mut(&key) + .get_or_add(handle) .and_then(|gamepad| gamepad .update_state(input) .map(|_| gamepad)) .map(|gamepad| { - for (index, (button, prev_button)) in gamepad.button_state - .iter() - .zip(gamepad.prev_button_state.iter()) - .enumerate() - { - if button != prev_button { - let state = if *button { Pressed } else { Released }; - send_event(Event::DeviceEvent { - device_id, - event: Button { - button: index as _, - state, - }, - }); - } + for (button, hint, state) in gamepad.get_changed_buttons() { + send_event(Event::DeviceEvent { + device_id, + event: Button { + button, + hint, + state, + }, + }); } - for (index, axis) in gamepad.axis_state - .iter() - .enumerate() - { - if axis.value != axis.prev_value { - send_event(Event::DeviceEvent { - device_id, - event: Motion { - axis: index as _, - value: axis.value, - }, - }); - } + for (axis, hint, value) in gamepad.get_changed_axes() { + send_event(Event::DeviceEvent { + device_id, + event: Motion { + axis, + hint, + value, + }, + }); } }); } else { diff --git a/src/platform/windows/gamepad.rs b/src/platform/windows/gamepad.rs new file mode 100644 index 0000000000..eea2de5161 --- /dev/null +++ b/src/platform/windows/gamepad.rs @@ -0,0 +1,101 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +use winapi::um::winnt::HANDLE; +use winapi::um::winuser::RAWINPUT; + +use events::{AxisHint, ButtonHint, ElementState}; +use platform::platform::raw_input::{get_raw_input_device_name, RawGamepad}; +use platform::platform::xinput::{self, XInputGamepad}; + +lazy_static! { + pub static ref GAMEPADS: Arc> = Default::default(); +} + +#[derive(Debug)] +pub struct GamepadStore { + gamepads: HashMap, +} + +impl GamepadStore { + pub fn get_or_add(&mut self, handle: HANDLE) -> Option<&mut Gamepad> { + let key = handle as isize; + let gamepad_registered = self.gamepads.contains_key(&key); + if !gamepad_registered { + self.gamepads.insert(key, Gamepad::new(handle)?); + } + self.gamepads.get_mut(&key) + } + + pub fn remove(&mut self, handle: HANDLE) { + let key = handle as isize; + self.gamepads.remove(&key); + } +} + +impl Default for GamepadStore { + fn default() -> Self { + GamepadStore { + gamepads: HashMap::with_capacity(4), + } + } +} + +unsafe impl Send for GamepadStore {} +unsafe impl Sync for GamepadStore {} + +#[derive(Debug)] +pub enum GamepadType { + Raw(RawGamepad), + XInput(XInputGamepad), +} + +#[derive(Debug)] +pub struct Gamepad { + handle: HANDLE, + backend: GamepadType, +} + +impl Gamepad { + pub fn new(handle: HANDLE) -> Option { + // TODO: Verify that this is an HID device + let name = get_raw_input_device_name(handle)?; + xinput::id_from_name(&name) + .and_then(XInputGamepad::new) + .map(GamepadType::XInput) + .or_else(|| RawGamepad::new(handle) + .map(GamepadType::Raw)) + .map(|backend| Gamepad { + handle, + backend, + }) + } + + pub unsafe fn update_state(&mut self, input: *mut RAWINPUT) -> Option<()> { + match self.backend { + GamepadType::Raw(ref mut gamepad) => gamepad.update_state(input), + GamepadType::XInput(ref mut gamepad) => gamepad.update_state(), + } + } + + pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { + match self.backend { + GamepadType::Raw(ref gamepad) => gamepad.get_changed_buttons(), + GamepadType::XInput(ref gamepad) => gamepad.get_changed_buttons(), + } + } + + pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { + match self.backend { + GamepadType::Raw(ref gamepad) => gamepad.get_changed_axes(), + GamepadType::XInput(ref gamepad) => gamepad.get_changed_axes(), + } + } + + pub fn rumble(&mut self, left_speed: u16, right_speed: u16) { + match self.backend { + GamepadType::Raw(ref mut gamepad) => gamepad.rumble(left_speed, right_speed), + GamepadType::XInput(ref mut gamepad) => gamepad.rumble(left_speed, right_speed), + } + } +} diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index f7a576eb4d..840f67474c 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -52,8 +52,10 @@ mod dpi; mod drop_handler; mod event; mod events_loop; +mod gamepad; mod icon; mod monitor; mod raw_input; mod util; mod window; +mod xinput; diff --git a/src/platform/windows/raw_input.rs b/src/platform/windows/raw_input.rs index 009ef6540e..f241357c70 100644 --- a/src/platform/windows/raw_input.rs +++ b/src/platform/windows/raw_input.rs @@ -53,7 +53,7 @@ use winapi::um::winuser::{ RIM_TYPEHID, }; -use events::ElementState; +use events::{AxisHint, ButtonHint, ElementState}; use platform::platform::util; #[allow(dead_code)] @@ -347,7 +347,7 @@ impl fmt::Debug for Axis { } #[derive(Debug)] -pub struct Gamepad { +pub struct RawGamepad { handle: HANDLE, pre_parsed_data: Vec, button_count: usize, @@ -358,8 +358,8 @@ pub struct Gamepad { } // Reference: https://chromium.googlesource.com/chromium/chromium/+/trunk/content/browser/gamepad/raw_input_data_fetcher_win.cc -impl Gamepad { - pub fn new(handle: HANDLE) -> Option { +impl RawGamepad { + pub fn new(handle: HANDLE) -> Option { let pre_parsed_data = get_raw_input_pre_parse_info(handle)?; let data_ptr = pre_parsed_data.as_ptr() as PHIDP_PREPARSED_DATA; let mut caps = unsafe { mem::uninitialized() }; @@ -408,7 +408,7 @@ impl Gamepad { }); axis_count = max(axis_count, axis_index + 1); } - Some(Gamepad { + Some(RawGamepad { handle, pre_parsed_data, button_count: button_count as usize, @@ -516,7 +516,33 @@ impl Gamepad { self.update_axis_state(hid)?; Some(()) } -} -unsafe impl Send for Gamepad {} -unsafe impl Sync for Gamepad {} + pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { + self.button_state + .iter() + .zip(self.prev_button_state.iter()) + .enumerate() + .filter(|&(_, (button, prev_button))| button != prev_button) + .map(|(index, (button, _))| { + let state = if *button { ElementState::Pressed } else { ElementState::Released }; + (index as _, None, state) + }) + .collect() + } + + pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { + // TODO: hints + self.axis_state + .iter() + .enumerate() + .filter(|&(_, axis)| axis.value != axis.prev_value) + .map(|(index, axis)| (index as _, None, axis.value)) + .collect() + } + + pub fn rumble(&mut self, _left_speed: u16, _right_speed: u16) { + // Even though I can't read German, this is still the most useful resource I found: + // https://zfx.info/viewtopic.php?t=3574&f=7 + // I'm not optimistic about it being possible to implement this. + } +} diff --git a/src/platform/windows/util.rs b/src/platform/windows/util.rs index cf03f52b8f..789ba9654a 100644 --- a/src/platform/windows/util.rs +++ b/src/platform/windows/util.rs @@ -1,5 +1,4 @@ use std::{self, mem, ptr, slice}; -use std::ops::BitAnd; use winapi::ctypes::wchar_t; use winapi::shared::minwindef::{BOOL, DWORD}; @@ -23,13 +22,6 @@ use winapi::um::winuser; pub use util::*; -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).to_string() } diff --git a/src/platform/windows/xinput.rs b/src/platform/windows/xinput.rs new file mode 100644 index 0000000000..3269f18955 --- /dev/null +++ b/src/platform/windows/xinput.rs @@ -0,0 +1,172 @@ +use std::mem; + +use regex::Regex; +use rusty_xinput::*; +use winapi::shared::minwindef::{DWORD, WORD}; +use winapi::um::xinput::*; + +use events::{AxisHint, ButtonHint, ElementState}; +use platform::platform::util; + +lazy_static! { + static ref XINPUT_GUARD: Option<()> = dynamic_load_xinput().ok(); + static ref ID_REGEX: Regex = Regex::new(r"(?m)IG_0([0-3])").unwrap(); +} + +static BUTTONS: &[(WORD , u32, ButtonHint)] = &[ + (XINPUT_GAMEPAD_DPAD_UP, 12, ButtonHint::DPadUp), + (XINPUT_GAMEPAD_DPAD_DOWN, 13, ButtonHint::DPadDown), + (XINPUT_GAMEPAD_DPAD_LEFT, 14, ButtonHint::DPadLeft), + (XINPUT_GAMEPAD_DPAD_RIGHT, 15, ButtonHint::DPadRight), + (XINPUT_GAMEPAD_START, 9, ButtonHint::Start), + (XINPUT_GAMEPAD_BACK, 8, ButtonHint::Select), + (XINPUT_GAMEPAD_LEFT_THUMB, 10, ButtonHint::LeftStick), + (XINPUT_GAMEPAD_RIGHT_THUMB, 11, ButtonHint::RightStick), + (XINPUT_GAMEPAD_LEFT_SHOULDER, 4, ButtonHint::LeftShoulder), + (XINPUT_GAMEPAD_RIGHT_SHOULDER, 5, ButtonHint::RightShoulder), + (XINPUT_GAMEPAD_A, 0, ButtonHint::South), + (XINPUT_GAMEPAD_B, 1, ButtonHint::East), + (XINPUT_GAMEPAD_X, 2, ButtonHint::West), + (XINPUT_GAMEPAD_Y, 3, ButtonHint::North), +]; + +pub fn id_from_name(name: &str) -> Option { + // A device name looks like \\?\HID#VID_046D&PID_C21D&IG_00#8&6daf3b6&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} + // The IG_00 substring indicates that this is an XInput gamepad, and that the ID is 00 + // So we use this regex to extract the ID, 0 + ID_REGEX + .captures_iter(name) + .next() + .and_then(|id| id + .get(1) + .unwrap() + .as_str() + .parse() + .ok()) +} + +#[derive(Debug)] +enum Stick { + Left, + Right, +} + +#[derive(Debug)] +pub struct XInputGamepad { + id: DWORD, + prev_state: Option, + state: Option, +} + +impl XInputGamepad { + pub fn new(id: DWORD) -> Option { + XINPUT_GUARD.map(|_| XInputGamepad { + id, + prev_state: None, + state: None, + }) + } + + pub fn update_state(&mut self) -> Option<()> { + let state = xinput_get_state(self.id).ok(); + if state.is_some() { + self.prev_state = mem::replace(&mut self.state, state); + Some(()) + } else { + None + } + } + + pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { + let buttons = match self.state + .as_ref() + .map(|state| state.raw.Gamepad.wButtons) + { + Some(buttons) => buttons, + None => return Vec::with_capacity(0), + }; + let prev_buttons = self.prev_state + .as_ref() + .map(|state| state.raw.Gamepad.wButtons) + .unwrap_or(0); + /* + A = buttons + B = prev_buttons + C = changed + P = pressed + R = released + A B C C A P C B R + (0 0) 0 (0 0) 0 (0 0) 0 + (0 1) 1 (1 1) 1 (1 0) 0 + (1 0) 1 (1 0) 0 (1 1) 1 + (1 1) 0 (0 1) 0 (0 1) 0 + */ + let changed = buttons ^ prev_buttons; + let pressed = changed & buttons; + let released = changed & prev_buttons; + let mut events = Vec::with_capacity(BUTTONS.len()); + for &(flag, id, hint) in BUTTONS { + if util::has_flag(pressed, flag) { + events.push((id, Some(hint), ElementState::Pressed)); + } else if util::has_flag(released, flag) { + events.push((id, Some(hint), ElementState::Released)); + } + } + events + } + + fn check_axis( + events: &mut Vec<(u32, Option, f64)>, + value: f32, + prev_value: Option, + id: u32, + hint: AxisHint, + ) { + if Some(value) != prev_value { + events.push((id, Some(hint), f64::from(value))); + } + } + + fn check_stick( + events: &mut Vec<(u32, Option, f64)>, + value: (f32, f32), + prev_value: Option<(f32, f32)>, + stick: Stick, + ) { + let (id, hint) = match stick { + Stick::Left => ((0, 1), (AxisHint::LeftStickX, AxisHint::LeftStickY)), + Stick::Right => ((2, 3), (AxisHint::RightStickX, AxisHint::RightStickY)), + }; + let prev_x = prev_value.map(|prev| prev.0); + let prev_y = prev_value.map(|prev| prev.1); + Self::check_axis(events, value.0, prev_x, id.0, hint.0); + Self::check_axis(events, value.1, prev_y, id.1, hint.1); + } + + pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { + // TODO: triggers + let (left_stick, right_stick) = match self.state { + Some(ref state) => ( + state.left_stick_normalized(), + state.right_stick_normalized(), + ), + None => return Vec::with_capacity(0), + }; + let mut events = Vec::with_capacity(4); + let prev_left_stick = self.prev_state + .as_ref() + .map(|prev| prev.left_stick_normalized()); + let prev_right_stick = self.prev_state + .as_ref() + .map(|prev| prev.right_stick_normalized()); + Self::check_stick(&mut events, left_stick, prev_left_stick, Stick::Left); + Self::check_stick(&mut events, right_stick, prev_right_stick, Stick::Right); + events + } + + pub fn rumble(&mut self, left_speed: u16, right_speed: u16) { + // TODO: We should probably return the status + let _ = xinput_set_state(self.id, left_speed, right_speed); + } +} + diff --git a/src/util.rs b/src/util.rs index 480841561f..c371990f11 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,12 @@ +use std::ops::BitAnd; + +pub fn has_flag(bitset: T, flag: T) -> bool +where T: + Copy + PartialEq + BitAnd +{ + bitset & flag == flag +} + pub fn clamp(value: f64, min: f64, max: f64) -> f64 { if value > max { max From 666a391df9feb88a44e9e8023143630fad454809 Mon Sep 17 00:00:00 2001 From: Francesca Plebani Date: Sun, 4 Nov 2018 15:29:20 -0500 Subject: [PATCH 04/34] XInput triggers --- src/events.rs | 9 +++ src/platform/windows/raw_input.rs | 26 +++++-- src/platform/windows/xinput.rs | 115 +++++++++++++++++++++++------- 3 files changed, 119 insertions(+), 31 deletions(-) diff --git a/src/events.rs b/src/events.rs index f9eeb868d9..d64ad1a56f 100644 --- a/src/events.rs +++ b/src/events.rs @@ -188,6 +188,15 @@ pub enum AxisHint { LeftTrigger, RightTrigger, + + /// This is supposed to have a specialized meaning, referring to a point-of-view switch present on joysticks used + /// for flight simulation. However, Xbox 360 controllers (and their derivatives) use a hat switch for the D-pad. + HatSwitch, + + DPadUp, + DPadDown, + DPadLeft, + DPadRight, } impl AxisHint { diff --git a/src/platform/windows/raw_input.rs b/src/platform/windows/raw_input.rs index f241357c70..b9b2c2940b 100644 --- a/src/platform/windows/raw_input.rs +++ b/src/platform/windows/raw_input.rs @@ -324,9 +324,9 @@ pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option pub struct Axis { caps: HIDP_VALUE_CAPS, - pub value: f64, - pub prev_value: f64, - //active: bool, + value: f64, + prev_value: f64, + hint: Option, } impl fmt::Debug for Axis { @@ -335,11 +335,13 @@ impl fmt::Debug for Axis { struct Axis { value: f64, prev_value: f64, + hint: Option, } let axis_proxy = Axis { value: self.value, prev_value: self.prev_value, + hint: self.hint, }; axis_proxy.fmt(f) @@ -404,7 +406,20 @@ impl RawGamepad { caps: axis_cap, value: 0.0, prev_value: 0.0, - //active: true, + hint: match unsafe { axis_cap.u.Range().UsageMin } { + 0x30 => Some(AxisHint::LeftStickX), + 0x31 => Some(AxisHint::LeftStickY), + 0x32 => Some(AxisHint::RightStickX), + 0x33 => Some(AxisHint::LeftTrigger), + 0x34 => Some(AxisHint::RightTrigger), + 0x35 => Some(AxisHint::RightStickY), + 0x39 => Some(AxisHint::HatSwitch), + 0x90 => Some(AxisHint::DPadUp), + 0x91 => Some(AxisHint::DPadDown), + 0x92 => Some(AxisHint::DPadRight), + 0x93 => Some(AxisHint::DPadLeft), + _ => None, + }, }); axis_count = max(axis_count, axis_index + 1); } @@ -531,12 +546,11 @@ impl RawGamepad { } pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { - // TODO: hints self.axis_state .iter() .enumerate() .filter(|&(_, axis)| axis.value != axis.prev_value) - .map(|(index, axis)| (index as _, None, axis.value)) + .map(|(index, axis)| (index as _, axis.hint, axis.value)) .collect() } diff --git a/src/platform/windows/xinput.rs b/src/platform/windows/xinput.rs index 3269f18955..adf8a3b37b 100644 --- a/src/platform/windows/xinput.rs +++ b/src/platform/windows/xinput.rs @@ -46,7 +46,7 @@ pub fn id_from_name(name: &str) -> Option { } #[derive(Debug)] -enum Stick { +enum Side { Left, Right, } @@ -77,18 +77,41 @@ impl XInputGamepad { } } + fn check_trigger_digital( + events: &mut Vec<(u32, Option, ElementState)>, + value: bool, + prev_value: Option, + side: Side, + ) { + const LEFT_TRIGGER_ID: u32 = /*BUTTONS.len() as _*/ 16; + const RIGHT_TRIGGER_ID: u32 = LEFT_TRIGGER_ID + 1; + if Some(value) != prev_value { + let state = if value { ElementState::Pressed } else { ElementState::Released }; + let (id, hint) = match side { + Side::Left => (LEFT_TRIGGER_ID, ButtonHint::LeftTrigger), + Side::Right => (RIGHT_TRIGGER_ID, ButtonHint::RightTrigger), + }; + events.push((id, Some(hint), state)); + } + } + pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { - let buttons = match self.state - .as_ref() - .map(|state| state.raw.Gamepad.wButtons) - { - Some(buttons) => buttons, + let (buttons, left_trigger, right_trigger) = match self.state.as_ref() { + Some(state) => ( + state.raw.Gamepad.wButtons, + state.left_trigger_bool(), + state.right_trigger_bool(), + ), None => return Vec::with_capacity(0), }; - let prev_buttons = self.prev_state + let (prev_buttons, prev_left, prev_right) = self.prev_state .as_ref() - .map(|state| state.raw.Gamepad.wButtons) - .unwrap_or(0); + .map(|state| ( + state.raw.Gamepad.wButtons, + Some(state.left_trigger_bool()), + Some(state.right_trigger_bool()), + )) + .unwrap_or_else(|| (0, None, None)); /* A = buttons B = prev_buttons @@ -104,7 +127,7 @@ impl XInputGamepad { let changed = buttons ^ prev_buttons; let pressed = changed & buttons; let released = changed & prev_buttons; - let mut events = Vec::with_capacity(BUTTONS.len()); + let mut events = Vec::with_capacity(BUTTONS.len() + 2); for &(flag, id, hint) in BUTTONS { if util::has_flag(pressed, flag) { events.push((id, Some(hint), ElementState::Pressed)); @@ -112,17 +135,43 @@ impl XInputGamepad { events.push((id, Some(hint), ElementState::Released)); } } + Self::check_trigger_digital(&mut events, left_trigger, prev_left, Side::Left); + Self::check_trigger_digital(&mut events, right_trigger, prev_right, Side::Right); events } + fn check_trigger( + events: &mut Vec<(u32, Option, f64)>, + value: u8, + prev_value: Option, + side: Side, + ) { + const LEFT_TRIGGER_ID: u32 = 4; + const RIGHT_TRIGGER_ID: u32 = LEFT_TRIGGER_ID + 1; + if Some(value) != prev_value { + let (id, hint) = match side { + Side::Left => (LEFT_TRIGGER_ID, AxisHint::LeftTrigger), + Side::Right => (RIGHT_TRIGGER_ID, AxisHint::RightTrigger), + }; + events.push((id, Some(hint), value as f64 / u8::max_value() as f64)); + } + } + fn check_axis( events: &mut Vec<(u32, Option, f64)>, value: f32, prev_value: Option, id: u32, hint: AxisHint, + flip: bool, ) { if Some(value) != prev_value { + let value = f64::from(if flip && value != 0.0 { + // Flip the Y axis so the top left is (-1, -1) + -value + } else { + value + }); events.push((id, Some(hint), f64::from(value))); } } @@ -131,36 +180,52 @@ impl XInputGamepad { events: &mut Vec<(u32, Option, f64)>, value: (f32, f32), prev_value: Option<(f32, f32)>, - stick: Stick, + stick: Side, ) { let (id, hint) = match stick { - Stick::Left => ((0, 1), (AxisHint::LeftStickX, AxisHint::LeftStickY)), - Stick::Right => ((2, 3), (AxisHint::RightStickX, AxisHint::RightStickY)), + Side::Left => ((0, 1), (AxisHint::LeftStickX, AxisHint::LeftStickY)), + Side::Right => ((2, 3), (AxisHint::RightStickX, AxisHint::RightStickY)), }; let prev_x = prev_value.map(|prev| prev.0); let prev_y = prev_value.map(|prev| prev.1); - Self::check_axis(events, value.0, prev_x, id.0, hint.0); - Self::check_axis(events, value.1, prev_y, id.1, hint.1); + Self::check_axis(events, value.0, prev_x, id.0, hint.0, false); + Self::check_axis(events, value.1, prev_y, id.1, hint.1, true); } pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { - // TODO: triggers - let (left_stick, right_stick) = match self.state { + let ( + left_stick, + right_stick, + left_trigger, + right_trigger, + ) = match self.state { Some(ref state) => ( state.left_stick_normalized(), state.right_stick_normalized(), + state.left_trigger(), + state.right_trigger(), ), None => return Vec::with_capacity(0), }; - let mut events = Vec::with_capacity(4); - let prev_left_stick = self.prev_state - .as_ref() - .map(|prev| prev.left_stick_normalized()); - let prev_right_stick = self.prev_state + let mut events = Vec::with_capacity(6); + let ( + prev_left_stick, + prev_right_stick, + prev_left_trigger, + prev_right_trigger, + ) = self.prev_state .as_ref() - .map(|prev| prev.right_stick_normalized()); - Self::check_stick(&mut events, left_stick, prev_left_stick, Stick::Left); - Self::check_stick(&mut events, right_stick, prev_right_stick, Stick::Right); + .map(|state| ( + Some(state.left_stick_normalized()), + Some(state.right_stick_normalized()), + Some(state.left_trigger()), + Some(state.right_trigger()), + )) + .unwrap_or_else(|| (None, None, None, None)); + Self::check_stick(&mut events, left_stick, prev_left_stick, Side::Left); + Self::check_stick(&mut events, right_stick, prev_right_stick, Side::Right); + Self::check_trigger(&mut events, left_trigger, prev_left_trigger, Side::Left); + Self::check_trigger(&mut events, right_trigger, prev_right_trigger, Side::Right); events } From da57daac6a6af1ec544bc57f6b0dc360ee824ad9 Mon Sep 17 00:00:00 2001 From: Francesca Plebani Date: Sun, 4 Nov 2018 15:29:26 -0500 Subject: [PATCH 05/34] Add preliminary CHANGELOG entry. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0016098a3e..da36298553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types. - Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them. - On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze. +- Added gamepad support on Windows via raw input and XInput. # Version 0.17.2 (2018-08-19) From 89b6cb7f9fe9796268faa623fa0b97236986a0ca Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 15 Nov 2018 12:43:13 +0100 Subject: [PATCH 06/34] match unix common API to evl 2.0 --- src/platform/unix.rs | 14 +- src/platform_impl/linux/mod.rs | 328 ++++++++++++++++----------------- 2 files changed, 165 insertions(+), 177 deletions(-) diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 983ccf0d08..f96664df2a 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -6,13 +6,11 @@ use std::sync::Arc; use sctk::window::{ButtonState, Theme}; -use { - EventLoop, - LogicalSize, - MonitorHandle, - Window, - WindowBuilder, -}; +use dpi::LogicalSize; +use event_loop::EventLoop; +use monitor::MonitorHandle; +use window::{Window, WindowBuilder}; + use platform_impl::{ EventLoop as LinuxEventLoop, Window as LinuxWindow, @@ -115,7 +113,7 @@ pub trait EventLoopExtUnix { fn get_xlib_xconnection(&self) -> Option>; } -impl EventLoopExtUnix for EventLoop { +impl EventLoopExtUnix for EventLoop { #[inline] fn new_x11() -> Result { LinuxEventLoop::new_x11().map(|ev| diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index a1bdbe5f02..d94c4463c3 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -9,23 +9,18 @@ use std::sync::Arc; use parking_lot::Mutex; use sctk::reexports::client::ConnectError; -use { - CreationError, - EventLoopClosed, - Icon, - MouseCursor, - ControlFlow, - WindowAttributes, -}; use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use window::MonitorHandle as RootMonitorHandle; -use self::x11::{XConnection, XError}; -use self::x11::ffi::XVisualInfo; -pub use self::x11::XNotSupported; +use icon::Icon; +use event_loop::{EventLoopClosed, ControlFlow}; +use monitor::MonitorHandle as RootMonitorHandle; +use window::{WindowAttributes, CreationError, MouseCursor}; +//use self::x11::{XConnection, XError}; +//use self::x11::ffi::XVisualInfo; +//pub use self::x11::XNotSupported; mod dlopen; -pub mod wayland; -pub mod x11; +//pub mod wayland; +//pub mod x11; /// Environment variable specifying which backend should be used on unix platform. /// @@ -38,32 +33,32 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { - pub visual_infos: Option, + //pub visual_infos: Option, pub screen_id: Option, pub resize_increments: Option<(u32, u32)>, pub base_size: Option<(u32, u32)>, pub class: Option<(String, String)>, pub override_redirect: bool, - pub x11_window_type: x11::util::WindowType, + //pub x11_window_type: x11::util::WindowType, pub gtk_theme_variant: Option, pub app_id: Option } -lazy_static!( - pub static ref X11_BACKEND: Mutex, XNotSupported>> = { - Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) - }; -); +//lazy_static!( +// pub static ref X11_BACKEND: Mutex, XNotSupported>> = { +// Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) +// }; +//); pub enum Window { - X(x11::Window), - Wayland(wayland::Window), + //X(x11::Window), + //Wayland(wayland::Window), } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum WindowId { - X(x11::WindowId), - Wayland(wayland::WindowId), + //X(x11::WindowId), + //Wayland(wayland::WindowId), } impl WindowId { @@ -74,8 +69,8 @@ impl WindowId { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DeviceId { - X(x11::DeviceId), - Wayland(wayland::DeviceId), + //X(x11::DeviceId), + //Wayland(wayland::DeviceId), } impl DeviceId { @@ -86,292 +81,293 @@ impl DeviceId { #[derive(Debug, Clone)] pub enum MonitorHandle { - X(x11::MonitorHandle), - Wayland(wayland::MonitorHandle), + //X(x11::MonitorHandle), + //Wayland(wayland::MonitorHandle), } impl MonitorHandle { #[inline] pub fn get_name(&self) -> Option { match self { - &MonitorHandle::X(ref m) => m.get_name(), - &MonitorHandle::Wayland(ref m) => m.get_name(), + //&MonitorHandle::X(ref m) => m.get_name(), + //&MonitorHandle::Wayland(ref m) => m.get_name(), } } #[inline] pub fn get_native_identifier(&self) -> u32 { match self { - &MonitorHandle::X(ref m) => m.get_native_identifier(), - &MonitorHandle::Wayland(ref m) => m.get_native_identifier(), + //&MonitorHandle::X(ref m) => m.get_native_identifier(), + //&MonitorHandle::Wayland(ref m) => m.get_native_identifier(), } } #[inline] pub fn get_dimensions(&self) -> PhysicalSize { match self { - &MonitorHandle::X(ref m) => m.get_dimensions(), - &MonitorHandle::Wayland(ref m) => m.get_dimensions(), + //&MonitorHandle::X(ref m) => m.get_dimensions(), + //&MonitorHandle::Wayland(ref m) => m.get_dimensions(), } } #[inline] pub fn get_position(&self) -> PhysicalPosition { match self { - &MonitorHandle::X(ref m) => m.get_position(), - &MonitorHandle::Wayland(ref m) => m.get_position(), + //&MonitorHandle::X(ref m) => m.get_position(), + //&MonitorHandle::Wayland(ref m) => m.get_position(), } } #[inline] pub fn get_hidpi_factor(&self) -> f64 { match self { - &MonitorHandle::X(ref m) => m.get_hidpi_factor(), - &MonitorHandle::Wayland(ref m) => m.get_hidpi_factor() as f64, + //&MonitorHandle::X(ref m) => m.get_hidpi_factor(), + //&MonitorHandle::Wayland(ref m) => m.get_hidpi_factor() as f64, } } } impl Window { #[inline] - pub fn new( - event_loop: &EventLoop, + pub fn new( + event_loop: &EventLoop, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result { match *event_loop { - EventsLoop::Wayland(ref event_loop) => { - wayland::Window::new(event_loop, attribs, pl_attribs).map(Window::Wayland) - }, - EventLoop::X(ref event_loop) => { - x11::Window::new(event_loop, attribs, pl_attribs).map(Window::X) - }, + //EventLoop::Wayland(ref event_loop) => { + // wayland::Window::new(event_loop, attribs).map(Window::Wayland) + //}, + //EventLoop::X(ref event_loop) => { + // x11::Window::new(event_loop, attribs, pl_attribs).map(Window::X) + //}, } } #[inline] pub fn id(&self) -> WindowId { match self { - &Window::X(ref w) => WindowId::X(w.id()), - &Window::Wayland(ref w) => WindowId::Wayland(w.id()), + //&Window::X(ref w) => WindowId::X(w.id()), + //&Window::Wayland(ref w) => WindowId::Wayland(w.id()), } } #[inline] pub fn set_title(&self, title: &str) { match self { - &Window::X(ref w) => w.set_title(title), - &Window::Wayland(ref w) => w.set_title(title), + //&Window::X(ref w) => w.set_title(title), + //&Window::Wayland(ref w) => w.set_title(title), } } #[inline] pub fn show(&self) { match self { - &Window::X(ref w) => w.show(), - &Window::Wayland(ref w) => w.show(), + //&Window::X(ref w) => w.show(), + //&Window::Wayland(ref w) => w.show(), } } #[inline] pub fn hide(&self) { match self { - &Window::X(ref w) => w.hide(), - &Window::Wayland(ref w) => w.hide(), + //&Window::X(ref w) => w.hide(), + //&Window::Wayland(ref w) => w.hide(), } } #[inline] pub fn get_position(&self) -> Option { match self { - &Window::X(ref w) => w.get_position(), - &Window::Wayland(ref w) => w.get_position(), + //&Window::X(ref w) => w.get_position(), + //&Window::Wayland(ref w) => w.get_position(), } } #[inline] pub fn get_inner_position(&self) -> Option { match self { - &Window::X(ref m) => m.get_inner_position(), - &Window::Wayland(ref m) => m.get_inner_position(), + //&Window::X(ref m) => m.get_inner_position(), + //&Window::Wayland(ref m) => m.get_inner_position(), } } #[inline] pub fn set_position(&self, position: LogicalPosition) { match self { - &Window::X(ref w) => w.set_position(position), - &Window::Wayland(ref w) => w.set_position(position), + //&Window::X(ref w) => w.set_position(position), + //&Window::Wayland(ref w) => w.set_position(position), } } #[inline] pub fn get_inner_size(&self) -> Option { match self { - &Window::X(ref w) => w.get_inner_size(), - &Window::Wayland(ref w) => w.get_inner_size(), + //&Window::X(ref w) => w.get_inner_size(), + //&Window::Wayland(ref w) => w.get_inner_size(), } } #[inline] pub fn get_outer_size(&self) -> Option { match self { - &Window::X(ref w) => w.get_outer_size(), - &Window::Wayland(ref w) => w.get_outer_size(), + //&Window::X(ref w) => w.get_outer_size(), + //&Window::Wayland(ref w) => w.get_outer_size(), } } #[inline] pub fn set_inner_size(&self, size: LogicalSize) { match self { - &Window::X(ref w) => w.set_inner_size(size), - &Window::Wayland(ref w) => w.set_inner_size(size), + //&Window::X(ref w) => w.set_inner_size(size), + //&Window::Wayland(ref w) => w.set_inner_size(size), } } #[inline] pub fn set_min_dimensions(&self, dimensions: Option) { match self { - &Window::X(ref w) => w.set_min_dimensions(dimensions), - &Window::Wayland(ref w) => w.set_min_dimensions(dimensions), + //&Window::X(ref w) => w.set_min_dimensions(dimensions), + //&Window::Wayland(ref w) => w.set_min_dimensions(dimensions), } } #[inline] pub fn set_max_dimensions(&self, dimensions: Option) { match self { - &Window::X(ref w) => w.set_max_dimensions(dimensions), - &Window::Wayland(ref w) => w.set_max_dimensions(dimensions), + //&Window::X(ref w) => w.set_max_dimensions(dimensions), + //&Window::Wayland(ref w) => w.set_max_dimensions(dimensions), } } #[inline] pub fn set_resizable(&self, resizable: bool) { match self { - &Window::X(ref w) => w.set_resizable(resizable), - &Window::Wayland(ref w) => w.set_resizable(resizable), + //&Window::X(ref w) => w.set_resizable(resizable), + //&Window::Wayland(ref w) => w.set_resizable(resizable), } } #[inline] pub fn set_cursor(&self, cursor: MouseCursor) { match self { - &Window::X(ref w) => w.set_cursor(cursor), - &Window::Wayland(ref w) => w.set_cursor(cursor) + //&Window::X(ref w) => w.set_cursor(cursor), + //&Window::Wayland(ref w) => w.set_cursor(cursor) } } #[inline] pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { match self { - &Window::X(ref window) => window.grab_cursor(grab), - &Window::Wayland(ref window) => window.grab_cursor(grab), + //&Window::X(ref window) => window.grab_cursor(grab), + //&Window::Wayland(ref window) => window.grab_cursor(grab), } } #[inline] pub fn hide_cursor(&self, hide: bool) { match self { - &Window::X(ref window) => window.hide_cursor(hide), - &Window::Wayland(ref window) => window.hide_cursor(hide), + //&Window::X(ref window) => window.hide_cursor(hide), + //&Window::Wayland(ref window) => window.hide_cursor(hide), } } #[inline] pub fn get_hidpi_factor(&self) -> f64 { match self { - &Window::X(ref w) => w.get_hidpi_factor(), - &Window::Wayland(ref w) => w.hidpi_factor() as f64, + //&Window::X(ref w) => w.get_hidpi_factor(), + //&Window::Wayland(ref w) => w.hidpi_factor() as f64, } } #[inline] pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { match self { - &Window::X(ref w) => w.set_cursor_position(position), - &Window::Wayland(ref w) => w.set_cursor_position(position), + //&Window::X(ref w) => w.set_cursor_position(position), + //&Window::Wayland(ref w) => w.set_cursor_position(position), } } #[inline] pub fn set_maximized(&self, maximized: bool) { match self { - &Window::X(ref w) => w.set_maximized(maximized), - &Window::Wayland(ref w) => w.set_maximized(maximized), + //&Window::X(ref w) => w.set_maximized(maximized), + //&Window::Wayland(ref w) => w.set_maximized(maximized), } } #[inline] pub fn set_fullscreen(&self, monitor: Option) { match self { - &Window::X(ref w) => w.set_fullscreen(monitor), - &Window::Wayland(ref w) => w.set_fullscreen(monitor) + //&Window::X(ref w) => w.set_fullscreen(monitor), + //&Window::Wayland(ref w) => w.set_fullscreen(monitor) } } #[inline] pub fn set_decorations(&self, decorations: bool) { match self { - &Window::X(ref w) => w.set_decorations(decorations), - &Window::Wayland(ref w) => w.set_decorations(decorations) + //&Window::X(ref w) => w.set_decorations(decorations), + //&Window::Wayland(ref w) => w.set_decorations(decorations) } } #[inline] pub fn set_always_on_top(&self, always_on_top: bool) { match self { - &Window::X(ref w) => w.set_always_on_top(always_on_top), - &Window::Wayland(_) => (), + //&Window::X(ref w) => w.set_always_on_top(always_on_top), + //&Window::Wayland(_) => (), } } #[inline] pub fn set_window_icon(&self, window_icon: Option) { match self { - &Window::X(ref w) => w.set_window_icon(window_icon), - &Window::Wayland(_) => (), + //&Window::X(ref w) => w.set_window_icon(window_icon), + //&Window::Wayland(_) => (), } } #[inline] pub fn set_ime_spot(&self, position: LogicalPosition) { match self { - &Window::X(ref w) => w.set_ime_spot(position), - &Window::Wayland(_) => (), + //&Window::X(ref w) => w.set_ime_spot(position), + //&Window::Wayland(_) => (), } } #[inline] pub fn get_current_monitor(&self) -> RootMonitorHandle { match self { - &Window::X(ref window) => RootMonitorHandle { inner: MonitorHandle::X(window.get_current_monitor()) }, - &Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.get_current_monitor()) }, + //&Window::X(ref window) => RootMonitorHandle { inner: MonitorHandle::X(window.get_current_monitor()) }, + //&Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.get_current_monitor()) }, } } #[inline] pub fn get_available_monitors(&self) -> VecDeque { match self { - &Window::X(ref window) => window.get_available_monitors() - .into_iter() - .map(MonitorHandle::X) - .collect(), - &Window::Wayland(ref window) => window.get_available_monitors() - .into_iter() - .map(MonitorHandle::Wayland) - .collect(), + //&Window::X(ref window) => window.get_available_monitors() + // .into_iter() + // .map(MonitorHandle::X) + // .collect(), + //&Window::Wayland(ref window) => window.get_available_monitors() + // .into_iter() + // .map(MonitorHandle::Wayland) + // .collect(), } } #[inline] pub fn get_primary_monitor(&self) -> MonitorHandle { match self { - &Window::X(ref window) => MonitorHandle::X(window.get_primary_monitor()), - &Window::Wayland(ref window) => MonitorHandle::Wayland(window.get_primary_monitor()), + //&Window::X(ref window) => MonitorHandle::X(window.get_primary_monitor()), + //&Window::Wayland(ref window) => MonitorHandle::Wayland(window.get_primary_monitor()), } } } +/* unsafe extern "C" fn x_error_callback( display: *mut x11::ffi::Display, event: *mut x11::ffi::XErrorEvent, @@ -401,20 +397,21 @@ unsafe extern "C" fn x_error_callback( // Fun fact: this return value is completely ignored. 0 } +*/ -pub enum EventLoop { - Wayland(wayland::EventLoop), - X(x11::EventLoop) +pub enum EventLoop { + //Wayland(wayland::EventLoop), + //X(x11::EventLoop) } #[derive(Clone)] -pub enum EventLoopProxy { - X(x11::EventLoopProxy), - Wayland(wayland::EventLoopProxy), +pub enum EventLoopProxy { + //X(x11::EventLoopProxy), + //Wayland(wayland::EventLoopProxy), } -impl EventLoop { - pub fn new() -> EventLoop { +impl EventLoop { + pub fn new() -> EventLoop { if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { match env_var.as_str() { "x11" => { @@ -450,93 +447,86 @@ impl EventLoop { panic!(err_string); } - pub fn new_wayland() -> Result { - wayland::EventLoop::new() - .map(EventLoop::Wayland) + pub fn new_wayland() -> Result, ConnectError> { + //wayland::EventLoop::new() + // .map(EventLoop::Wayland) + unimplemented!() } - pub fn new_x11() -> Result { - X11_BACKEND - .lock() - .as_ref() - .map(Arc::clone) - .map(x11::EventLoop::new) - .map(EventLoop::X) - .map_err(|err| err.clone()) + pub fn new_x11() -> Result, () /*XNotSupported*/> { + //X11_BACKEND + // .lock() + // .as_ref() + // .map(Arc::clone) + // .map(x11::EventLoop::new) + // .map(EventLoop::X) + // .map_err(|err| err.clone()) + unimplemented!() } #[inline] pub fn get_available_monitors(&self) -> VecDeque { match *self { - EventLoop::Wayland(ref evlp) => evlp - .get_available_monitors() - .into_iter() - .map(MonitorHandle::Wayland) - .collect(), - EventLoop::X(ref evlp) => evlp - .x_connection() - .get_available_monitors() - .into_iter() - .map(MonitorHandle::X) - .collect(), + //EventLoop::Wayland(ref evlp) => evlp + // .get_available_monitors() + // .into_iter() + // .map(MonitorHandle::Wayland) + // .collect(), + //EventLoop::X(ref evlp) => evlp + // .x_connection() + // .get_available_monitors() + // .into_iter() + // .map(MonitorHandle::X) + // .collect(), } } #[inline] pub fn get_primary_monitor(&self) -> MonitorHandle { match *self { - EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.get_primary_monitor()), - EventLoop::X(ref evlp) => MonitorHandle::X(evlp.x_connection().get_primary_monitor()), + //EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.get_primary_monitor()), + //EventLoop::X(ref evlp) => MonitorHandle::X(evlp.x_connection().get_primary_monitor()), } } - pub fn create_proxy(&self) -> EventLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { match *self { - EventLoop::Wayland(ref evlp) => EventLoopProxy::Wayland(evlp.create_proxy()), - EventLoop::X(ref evlp) => EventLoopProxy::X(evlp.create_proxy()), + //EventLoop::Wayland(ref evlp) => EventLoopProxy::Wayland(evlp.create_proxy()), + //EventLoop::X(ref evlp) => EventLoopProxy::X(evlp.create_proxy()), } } - pub fn poll_events(&mut self, callback: F) - where F: FnMut(::Event) + pub fn run(self, callback: F) -> ! + where F: 'static + FnMut(::event::Event, &EventLoop, &mut ControlFlow) { - match *self { - EventLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), - EventLoop::X(ref mut evlp) => evlp.poll_events(callback) - } - } - - pub fn run_forever(&mut self, callback: F) - where F: FnMut(::Event) -> ControlFlow - { - match *self { - EventLoop::Wayland(ref mut evlp) => evlp.run_forever(callback), - EventLoop::X(ref mut evlp) => evlp.run_forever(callback) + match self { + //EventLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), + //EventLoop::X(ref mut evlp) => evlp.poll_events(callback) } } #[inline] pub fn is_wayland(&self) -> bool { match *self { - EventLoop::Wayland(_) => true, - EventLoop::X(_) => false, + //EventLoop::Wayland(_) => true, + //EventLoop::X(_) => false, } } - #[inline] - pub fn x_connection(&self) -> Option<&Arc> { - match *self { - EventLoop::Wayland(_) => None, - EventLoop::X(ref ev) => Some(ev.x_connection()), - } - } + //#[inline] + //pub fn x_connection(&self) -> Option<&Arc> { + // match *self { + // EventLoop::Wayland(_) => None, + // EventLoop::X(ref ev) => Some(ev.x_connection()), + // } + //} } -impl EventLoopProxy { - pub fn wakeup(&self) -> Result<(), EventLoopClosed> { +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { match *self { - EventLoopProxy::Wayland(ref proxy) => proxy.wakeup(), - EventLoopProxy::X(ref proxy) => proxy.wakeup(), + //EventLoopProxy::Wayland(ref proxy) => proxy.wakeup(), + //EventLoopProxy::X(ref proxy) => proxy.wakeup(), } } } From ae6ca4623a39d6cf361f64073c2a436f080478ba Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Thu, 15 Nov 2018 18:06:11 +0100 Subject: [PATCH 07/34] wayland: eventloop2.0 --- Cargo.toml | 5 +- src/lib.rs | 2 + src/platform/unix.rs | 110 ++--- src/platform_impl/linux/mod.rs | 162 ++++--- src/platform_impl/linux/wayland/event_loop.rs | 447 +++++++++--------- src/platform_impl/linux/wayland/keyboard.rs | 73 ++- src/platform_impl/linux/wayland/mod.rs | 7 +- src/platform_impl/linux/wayland/pointer.rs | 40 +- src/platform_impl/linux/wayland/touch.rs | 31 +- src/platform_impl/linux/wayland/window.rs | 117 ++--- 10 files changed, 468 insertions(+), 526 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7017dc9515..b37ea31777 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,8 +64,9 @@ features = [ ] [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] -wayland-client = { version = "0.21", features = [ "dlopen", "egl", "cursor"] } -smithay-client-toolkit = "0.4.3" +wayland-client = { version = "0.22.0", features = [ "dlopen", "egl", "cursor", "eventloop"] } +calloop = "0.4.2" +smithay-client-toolkit = "0.5" x11-dl = "2.18.3" parking_lot = "0.7" percent-encoding = "1.0" diff --git a/src/lib.rs b/src/lib.rs index ff374806ee..2066d06101 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,8 @@ extern crate parking_lot; extern crate percent_encoding; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate smithay_client_toolkit as sctk; +#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +extern crate calloop; pub mod dpi; pub mod event; diff --git a/src/platform/unix.rs b/src/platform/unix.rs index f96664df2a..5bde480d21 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -15,15 +15,15 @@ use platform_impl::{ EventLoop as LinuxEventLoop, Window as LinuxWindow, }; -use platform_impl::x11::XConnection; -use platform_impl::x11::ffi::XVisualInfo; - +//use platform_impl::x11::XConnection; +//use platform_impl::x11::ffi::XVisualInfo; +// // TODO: stupid hack so that glutin can do its work -#[doc(hidden)] -pub use platform_impl::x11; - -pub use platform_impl::XNotSupported; -pub use platform_impl::x11::util::WindowType as XWindowType; +//#[doc(hidden)] +//pub use platform_impl::x11; +// +//pub use platform_impl::XNotSupported; +//pub use platform_impl::x11::util::WindowType as XWindowType; /// Theme for wayland client side decorations /// @@ -95,9 +95,9 @@ impl Theme for WaylandThemeObject { /// Additional methods on `EventLoop` that are specific to Unix. pub trait EventLoopExtUnix { - /// Builds a new `EventsLoop` that is forced to use X11. - fn new_x11() -> Result - where Self: Sized; + /// Builds a new `EventLoops` that is forced to use X11. + //fn new_x11() -> Result + // where Self: Sized; /// Builds a new `EventLoop` that is forced to use Wayland. fn new_wayland() -> Self @@ -109,20 +109,20 @@ pub trait EventLoopExtUnix { /// True if the `EventLoop` uses X11. fn is_x11(&self) -> bool; - #[doc(hidden)] - fn get_xlib_xconnection(&self) -> Option>; + //#[doc(hidden)] + //fn get_xlib_xconnection(&self) -> Option>; } impl EventLoopExtUnix for EventLoop { - #[inline] - fn new_x11() -> Result { - LinuxEventLoop::new_x11().map(|ev| - EventLoop { - event_loop: ev, - _marker: ::std::marker::PhantomData, - } - ) - } + //#[inline] + //fn new_x11() -> Result { + // LinuxEventLoop::new_x11().map(|ev| + // EventLoop { + // event_loop: ev, + // _marker: ::std::marker::PhantomData, + // } + // ) + //} #[inline] fn new_wayland() -> Self { @@ -145,11 +145,11 @@ impl EventLoopExtUnix for EventLoop { !self.event_loop.is_wayland() } - #[inline] - #[doc(hidden)] - fn get_xlib_xconnection(&self) -> Option> { - self.event_loop.x_connection().cloned() - } + //#[inline] + //#[doc(hidden)] + //fn get_xlib_xconnection(&self) -> Option> { + // self.event_loop.x_connection().cloned() + //} } /// Additional methods on `Window` that are specific to Unix. @@ -168,8 +168,8 @@ pub trait WindowExtUnix { fn get_xlib_screen_id(&self) -> Option; - #[doc(hidden)] - fn get_xlib_xconnection(&self) -> Option>; + //#[doc(hidden)] + //fn get_xlib_xconnection(&self) -> Option>; /// Set window urgency hint (`XUrgencyHint`). Only relevant on X. fn set_urgent(&self, is_urgent: bool); @@ -212,7 +212,7 @@ impl WindowExtUnix for Window { #[inline] fn get_xlib_window(&self) -> Option { match self.window { - LinuxWindow::X(ref w) => Some(w.get_xlib_window()), + //LinuxWindow::X(ref w) => Some(w.get_xlib_window()), _ => None } } @@ -220,7 +220,7 @@ impl WindowExtUnix for Window { #[inline] fn get_xlib_display(&self) -> Option<*mut raw::c_void> { match self.window { - LinuxWindow::X(ref w) => Some(w.get_xlib_display()), + //LinuxWindow::X(ref w) => Some(w.get_xlib_display()), _ => None } } @@ -228,39 +228,39 @@ impl WindowExtUnix for Window { #[inline] fn get_xlib_screen_id(&self) -> Option { match self.window { - LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()), + //LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()), _ => None } } - #[inline] - #[doc(hidden)] - fn get_xlib_xconnection(&self) -> Option> { - match self.window { - LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()), - _ => None - } - } + //#[inline] + //#[doc(hidden)] + //fn get_xlib_xconnection(&self) -> Option> { + // match self.window { + // //LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()), + // _ => None + // } + //} #[inline] fn get_xcb_connection(&self) -> Option<*mut raw::c_void> { match self.window { - LinuxWindow::X(ref w) => Some(w.get_xcb_connection()), + //LinuxWindow::X(ref w) => Some(w.get_xcb_connection()), _ => None } } #[inline] fn set_urgent(&self, is_urgent: bool) { - if let LinuxWindow::X(ref w) = self.window { - w.set_urgent(is_urgent); - } + //if let LinuxWindow::X(ref w) = self.window { + // w.set_urgent(is_urgent); + //} } #[inline] fn get_wayland_surface(&self) -> Option<*mut raw::c_void> { match self.window { - LinuxWindow::Wayland(ref w) => Some(w.get_surface().c_ptr() as *mut _), + LinuxWindow::Wayland(ref w) => Some(w.get_surface().as_ref().c_ptr() as *mut _), _ => None } } @@ -268,7 +268,7 @@ impl WindowExtUnix for Window { #[inline] fn get_wayland_display(&self) -> Option<*mut raw::c_void> { match self.window { - LinuxWindow::Wayland(ref w) => Some(w.get_display().c_ptr() as *mut _), + LinuxWindow::Wayland(ref w) => Some(w.get_display().as_ref().c_ptr() as *mut _), _ => None } } @@ -297,7 +297,7 @@ pub trait WindowBuilderExtUnix { /// Build window with override-redirect flag; defaults to false. Only relevant on X11. fn with_override_redirect(self, override_redirect: bool) -> WindowBuilder; /// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11. - fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder; + //fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder; /// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11. fn with_gtk_theme_variant(self, variant: String) -> WindowBuilder; /// Build window with resize increment hint. Only implemented on X11. @@ -316,9 +316,9 @@ pub trait WindowBuilderExtUnix { impl WindowBuilderExtUnix for WindowBuilder { #[inline] fn with_x11_visual(mut self, visual_infos: *const T) -> WindowBuilder { - self.platform_specific.visual_infos = Some( - unsafe { ptr::read(visual_infos as *const XVisualInfo) } - ); + //self.platform_specific.visual_infos = Some( + // unsafe { ptr::read(visual_infos as *const XVisualInfo) } + //); self } @@ -340,11 +340,11 @@ impl WindowBuilderExtUnix for WindowBuilder { self } - #[inline] - fn with_x11_window_type(mut self, x11_window_type: XWindowType) -> WindowBuilder { - self.platform_specific.x11_window_type = x11_window_type; - self - } + //#[inline] + //fn with_x11_window_type(mut self, x11_window_type: XWindowType) -> WindowBuilder { + // self.platform_specific.x11_window_type = x11_window_type; + // self + //} #[inline] fn with_resize_increments(mut self, increments: LogicalSize) -> WindowBuilder { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index d94c4463c3..d05b3a1f48 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -11,7 +11,7 @@ use sctk::reexports::client::ConnectError; use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use icon::Icon; -use event_loop::{EventLoopClosed, ControlFlow}; +use event_loop::{EventLoopClosed, ControlFlow, EventLoopWindowTarget as RootELW}; use monitor::MonitorHandle as RootMonitorHandle; use window::{WindowAttributes, CreationError, MouseCursor}; //use self::x11::{XConnection, XError}; @@ -19,7 +19,7 @@ use window::{WindowAttributes, CreationError, MouseCursor}; //pub use self::x11::XNotSupported; mod dlopen; -//pub mod wayland; +pub mod wayland; //pub mod x11; /// Environment variable specifying which backend should be used on unix platform. @@ -52,37 +52,37 @@ pub struct PlatformSpecificWindowBuilderAttributes { pub enum Window { //X(x11::Window), - //Wayland(wayland::Window), + Wayland(wayland::Window), } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum WindowId { //X(x11::WindowId), - //Wayland(wayland::WindowId), + Wayland(wayland::WindowId), } impl WindowId { pub unsafe fn dummy() -> Self { - WindowId::X(x11::WindowId::dummy()) + WindowId::Wayland(wayland::WindowId::dummy()) } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DeviceId { //X(x11::DeviceId), - //Wayland(wayland::DeviceId), + Wayland(wayland::DeviceId), } impl DeviceId { pub unsafe fn dummy() -> Self { - DeviceId::X(x11::DeviceId::dummy()) + DeviceId::Wayland(wayland::DeviceId::dummy()) } } #[derive(Debug, Clone)] pub enum MonitorHandle { //X(x11::MonitorHandle), - //Wayland(wayland::MonitorHandle), + Wayland(wayland::MonitorHandle), } impl MonitorHandle { @@ -90,7 +90,7 @@ impl MonitorHandle { pub fn get_name(&self) -> Option { match self { //&MonitorHandle::X(ref m) => m.get_name(), - //&MonitorHandle::Wayland(ref m) => m.get_name(), + &MonitorHandle::Wayland(ref m) => m.get_name(), } } @@ -98,7 +98,7 @@ impl MonitorHandle { pub fn get_native_identifier(&self) -> u32 { match self { //&MonitorHandle::X(ref m) => m.get_native_identifier(), - //&MonitorHandle::Wayland(ref m) => m.get_native_identifier(), + &MonitorHandle::Wayland(ref m) => m.get_native_identifier(), } } @@ -106,7 +106,7 @@ impl MonitorHandle { pub fn get_dimensions(&self) -> PhysicalSize { match self { //&MonitorHandle::X(ref m) => m.get_dimensions(), - //&MonitorHandle::Wayland(ref m) => m.get_dimensions(), + &MonitorHandle::Wayland(ref m) => m.get_dimensions(), } } @@ -114,7 +114,7 @@ impl MonitorHandle { pub fn get_position(&self) -> PhysicalPosition { match self { //&MonitorHandle::X(ref m) => m.get_position(), - //&MonitorHandle::Wayland(ref m) => m.get_position(), + &MonitorHandle::Wayland(ref m) => m.get_position(), } } @@ -122,7 +122,7 @@ impl MonitorHandle { pub fn get_hidpi_factor(&self) -> f64 { match self { //&MonitorHandle::X(ref m) => m.get_hidpi_factor(), - //&MonitorHandle::Wayland(ref m) => m.get_hidpi_factor() as f64, + &MonitorHandle::Wayland(ref m) => m.get_hidpi_factor() as f64, } } } @@ -130,14 +130,14 @@ impl MonitorHandle { impl Window { #[inline] pub fn new( - event_loop: &EventLoop, + window_target: &EventLoopWindowTarget, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result { - match *event_loop { - //EventLoop::Wayland(ref event_loop) => { - // wayland::Window::new(event_loop, attribs).map(Window::Wayland) - //}, + match *window_target { + EventLoopWindowTarget::Wayland(ref window_target) => { + wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland) + }, //EventLoop::X(ref event_loop) => { // x11::Window::new(event_loop, attribs, pl_attribs).map(Window::X) //}, @@ -148,7 +148,7 @@ impl Window { pub fn id(&self) -> WindowId { match self { //&Window::X(ref w) => WindowId::X(w.id()), - //&Window::Wayland(ref w) => WindowId::Wayland(w.id()), + &Window::Wayland(ref w) => WindowId::Wayland(w.id()), } } @@ -156,7 +156,7 @@ impl Window { pub fn set_title(&self, title: &str) { match self { //&Window::X(ref w) => w.set_title(title), - //&Window::Wayland(ref w) => w.set_title(title), + &Window::Wayland(ref w) => w.set_title(title), } } @@ -164,7 +164,7 @@ impl Window { pub fn show(&self) { match self { //&Window::X(ref w) => w.show(), - //&Window::Wayland(ref w) => w.show(), + &Window::Wayland(ref w) => w.show(), } } @@ -172,7 +172,7 @@ impl Window { pub fn hide(&self) { match self { //&Window::X(ref w) => w.hide(), - //&Window::Wayland(ref w) => w.hide(), + &Window::Wayland(ref w) => w.hide(), } } @@ -180,7 +180,7 @@ impl Window { pub fn get_position(&self) -> Option { match self { //&Window::X(ref w) => w.get_position(), - //&Window::Wayland(ref w) => w.get_position(), + &Window::Wayland(ref w) => w.get_position(), } } @@ -188,7 +188,7 @@ impl Window { pub fn get_inner_position(&self) -> Option { match self { //&Window::X(ref m) => m.get_inner_position(), - //&Window::Wayland(ref m) => m.get_inner_position(), + &Window::Wayland(ref m) => m.get_inner_position(), } } @@ -196,7 +196,7 @@ impl Window { pub fn set_position(&self, position: LogicalPosition) { match self { //&Window::X(ref w) => w.set_position(position), - //&Window::Wayland(ref w) => w.set_position(position), + &Window::Wayland(ref w) => w.set_position(position), } } @@ -204,7 +204,7 @@ impl Window { pub fn get_inner_size(&self) -> Option { match self { //&Window::X(ref w) => w.get_inner_size(), - //&Window::Wayland(ref w) => w.get_inner_size(), + &Window::Wayland(ref w) => w.get_inner_size(), } } @@ -212,7 +212,7 @@ impl Window { pub fn get_outer_size(&self) -> Option { match self { //&Window::X(ref w) => w.get_outer_size(), - //&Window::Wayland(ref w) => w.get_outer_size(), + &Window::Wayland(ref w) => w.get_outer_size(), } } @@ -220,7 +220,7 @@ impl Window { pub fn set_inner_size(&self, size: LogicalSize) { match self { //&Window::X(ref w) => w.set_inner_size(size), - //&Window::Wayland(ref w) => w.set_inner_size(size), + &Window::Wayland(ref w) => w.set_inner_size(size), } } @@ -228,7 +228,7 @@ impl Window { pub fn set_min_dimensions(&self, dimensions: Option) { match self { //&Window::X(ref w) => w.set_min_dimensions(dimensions), - //&Window::Wayland(ref w) => w.set_min_dimensions(dimensions), + &Window::Wayland(ref w) => w.set_min_dimensions(dimensions), } } @@ -236,7 +236,7 @@ impl Window { pub fn set_max_dimensions(&self, dimensions: Option) { match self { //&Window::X(ref w) => w.set_max_dimensions(dimensions), - //&Window::Wayland(ref w) => w.set_max_dimensions(dimensions), + &Window::Wayland(ref w) => w.set_max_dimensions(dimensions), } } @@ -244,7 +244,7 @@ impl Window { pub fn set_resizable(&self, resizable: bool) { match self { //&Window::X(ref w) => w.set_resizable(resizable), - //&Window::Wayland(ref w) => w.set_resizable(resizable), + &Window::Wayland(ref w) => w.set_resizable(resizable), } } @@ -252,7 +252,7 @@ impl Window { pub fn set_cursor(&self, cursor: MouseCursor) { match self { //&Window::X(ref w) => w.set_cursor(cursor), - //&Window::Wayland(ref w) => w.set_cursor(cursor) + &Window::Wayland(ref w) => w.set_cursor(cursor) } } @@ -260,7 +260,7 @@ impl Window { pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { match self { //&Window::X(ref window) => window.grab_cursor(grab), - //&Window::Wayland(ref window) => window.grab_cursor(grab), + &Window::Wayland(ref window) => window.grab_cursor(grab), } } @@ -268,7 +268,7 @@ impl Window { pub fn hide_cursor(&self, hide: bool) { match self { //&Window::X(ref window) => window.hide_cursor(hide), - //&Window::Wayland(ref window) => window.hide_cursor(hide), + &Window::Wayland(ref window) => window.hide_cursor(hide), } } @@ -276,7 +276,7 @@ impl Window { pub fn get_hidpi_factor(&self) -> f64 { match self { //&Window::X(ref w) => w.get_hidpi_factor(), - //&Window::Wayland(ref w) => w.hidpi_factor() as f64, + &Window::Wayland(ref w) => w.hidpi_factor() as f64, } } @@ -284,7 +284,7 @@ impl Window { pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { match self { //&Window::X(ref w) => w.set_cursor_position(position), - //&Window::Wayland(ref w) => w.set_cursor_position(position), + &Window::Wayland(ref w) => w.set_cursor_position(position), } } @@ -292,7 +292,7 @@ impl Window { pub fn set_maximized(&self, maximized: bool) { match self { //&Window::X(ref w) => w.set_maximized(maximized), - //&Window::Wayland(ref w) => w.set_maximized(maximized), + &Window::Wayland(ref w) => w.set_maximized(maximized), } } @@ -300,7 +300,7 @@ impl Window { pub fn set_fullscreen(&self, monitor: Option) { match self { //&Window::X(ref w) => w.set_fullscreen(monitor), - //&Window::Wayland(ref w) => w.set_fullscreen(monitor) + &Window::Wayland(ref w) => w.set_fullscreen(monitor) } } @@ -308,7 +308,7 @@ impl Window { pub fn set_decorations(&self, decorations: bool) { match self { //&Window::X(ref w) => w.set_decorations(decorations), - //&Window::Wayland(ref w) => w.set_decorations(decorations) + &Window::Wayland(ref w) => w.set_decorations(decorations) } } @@ -316,7 +316,7 @@ impl Window { pub fn set_always_on_top(&self, always_on_top: bool) { match self { //&Window::X(ref w) => w.set_always_on_top(always_on_top), - //&Window::Wayland(_) => (), + &Window::Wayland(_) => (), } } @@ -324,7 +324,7 @@ impl Window { pub fn set_window_icon(&self, window_icon: Option) { match self { //&Window::X(ref w) => w.set_window_icon(window_icon), - //&Window::Wayland(_) => (), + &Window::Wayland(_) => (), } } @@ -332,7 +332,15 @@ impl Window { pub fn set_ime_spot(&self, position: LogicalPosition) { match self { //&Window::X(ref w) => w.set_ime_spot(position), - //&Window::Wayland(_) => (), + &Window::Wayland(_) => (), + } + } + + #[inline] + pub fn request_redraw(&self) { + match self { + //&Window::X(ref w) => w.request_redraw(), + &Window::Wayland(ref w) => w.request_redraw(), } } @@ -340,7 +348,7 @@ impl Window { pub fn get_current_monitor(&self) -> RootMonitorHandle { match self { //&Window::X(ref window) => RootMonitorHandle { inner: MonitorHandle::X(window.get_current_monitor()) }, - //&Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.get_current_monitor()) }, + &Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.get_current_monitor()) }, } } @@ -351,10 +359,10 @@ impl Window { // .into_iter() // .map(MonitorHandle::X) // .collect(), - //&Window::Wayland(ref window) => window.get_available_monitors() - // .into_iter() - // .map(MonitorHandle::Wayland) - // .collect(), + &Window::Wayland(ref window) => window.get_available_monitors() + .into_iter() + .map(MonitorHandle::Wayland) + .collect(), } } @@ -362,7 +370,7 @@ impl Window { pub fn get_primary_monitor(&self) -> MonitorHandle { match self { //&Window::X(ref window) => MonitorHandle::X(window.get_primary_monitor()), - //&Window::Wayland(ref window) => MonitorHandle::Wayland(window.get_primary_monitor()), + &Window::Wayland(ref window) => MonitorHandle::Wayland(window.get_primary_monitor()), } } } @@ -399,18 +407,18 @@ unsafe extern "C" fn x_error_callback( } */ -pub enum EventLoop { - //Wayland(wayland::EventLoop), +pub enum EventLoop { + Wayland(wayland::EventLoop), //X(x11::EventLoop) } #[derive(Clone)] pub enum EventLoopProxy { //X(x11::EventLoopProxy), - //Wayland(wayland::EventLoopProxy), + Wayland(wayland::EventLoopProxy), } -impl EventLoop { +impl EventLoop { pub fn new() -> EventLoop { if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { match env_var.as_str() { @@ -448,9 +456,8 @@ impl EventLoop { } pub fn new_wayland() -> Result, ConnectError> { - //wayland::EventLoop::new() - // .map(EventLoop::Wayland) - unimplemented!() + wayland::EventLoop::new() + .map(EventLoop::Wayland) } pub fn new_x11() -> Result, () /*XNotSupported*/> { @@ -467,11 +474,11 @@ impl EventLoop { #[inline] pub fn get_available_monitors(&self) -> VecDeque { match *self { - //EventLoop::Wayland(ref evlp) => evlp - // .get_available_monitors() - // .into_iter() - // .map(MonitorHandle::Wayland) - // .collect(), + EventLoop::Wayland(ref evlp) => evlp + .get_available_monitors() + .into_iter() + .map(MonitorHandle::Wayland) + .collect(), //EventLoop::X(ref evlp) => evlp // .x_connection() // .get_available_monitors() @@ -484,35 +491,51 @@ impl EventLoop { #[inline] pub fn get_primary_monitor(&self) -> MonitorHandle { match *self { - //EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.get_primary_monitor()), + EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.get_primary_monitor()), //EventLoop::X(ref evlp) => MonitorHandle::X(evlp.x_connection().get_primary_monitor()), } } pub fn create_proxy(&self) -> EventLoopProxy { match *self { - //EventLoop::Wayland(ref evlp) => EventLoopProxy::Wayland(evlp.create_proxy()), + EventLoop::Wayland(ref evlp) => EventLoopProxy::Wayland(evlp.create_proxy()), //EventLoop::X(ref evlp) => EventLoopProxy::X(evlp.create_proxy()), } } + pub fn run_return(&mut self, callback: F) + where F: FnMut(::event::Event, &RootELW, &mut ControlFlow) + { + match *self { + EventLoop::Wayland(ref mut evlp) => evlp.run_return(callback), + //EventLoop::X(ref mut evlp) => evlp.run_return(callback) + } + } + pub fn run(self, callback: F) -> ! - where F: 'static + FnMut(::event::Event, &EventLoop, &mut ControlFlow) + where F: 'static + FnMut(::event::Event, &RootELW, &mut ControlFlow) { match self { - //EventLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), - //EventLoop::X(ref mut evlp) => evlp.poll_events(callback) + EventLoop::Wayland(evlp) => evlp.run(callback), + //EventLoop::X(ref mut evlp) => evlp.run(callback) } } #[inline] pub fn is_wayland(&self) -> bool { match *self { - //EventLoop::Wayland(_) => true, + EventLoop::Wayland(_) => true, //EventLoop::X(_) => false, } } + pub fn window_target(&self) -> &::event_loop::EventLoopWindowTarget { + match *self { + EventLoop::Wayland(ref evl) => evl.window_target(), + //EventLoop::X(ref evl) => evl.window_target() + } + } + //#[inline] //pub fn x_connection(&self) -> Option<&Arc> { // match *self { @@ -525,8 +548,13 @@ impl EventLoop { impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { match *self { - //EventLoopProxy::Wayland(ref proxy) => proxy.wakeup(), + EventLoopProxy::Wayland(ref proxy) => proxy.send_event(event), //EventLoopProxy::X(ref proxy) => proxy.wakeup(), } } } + +pub enum EventLoopWindowTarget { + Wayland(wayland::EventLoopWindowTarget), + //X(x11::EventLoopWIndowTarget) +} diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index 25416233f0..ba5145de49 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -1,10 +1,13 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::fmt; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex, Weak}; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use std::time::Instant; -use {ControlFlow, EventLoopClosed, PhysicalPosition, PhysicalSize}; +use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}; +use event::ModifiersState; +use dpi::{PhysicalPosition, PhysicalSize}; use super::window::WindowStore; use super::WindowId; @@ -13,54 +16,61 @@ use sctk::output::OutputMgr; use sctk::reexports::client::protocol::{ wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch, }; -use sctk::reexports::client::{ConnectError, Display, EventQueue, GlobalEvent, Proxy}; +use sctk::reexports::client::{ConnectError, Display, EventQueue, GlobalEvent}; use sctk::Environment; -use sctk::reexports::client::protocol::wl_display::RequestsTrait as DisplayRequests; -use sctk::reexports::client::protocol::wl_surface::RequestsTrait; - -use ModifiersState; - -pub struct EventLoopSink { - buffer: VecDeque<::Event>, +pub struct WindowEventsSink { + buffer: VecDeque<(::event::WindowEvent, ::window::WindowId)>, } -impl EventLoopSink { - pub fn new() -> EventLoopSink { - EventLoopSink { +impl WindowEventsSink { + pub fn new() -> WindowEventsSink { + WindowEventsSink { buffer: VecDeque::new(), } } - pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) { - let evt = ::Event::WindowEvent { - event: evt, - window_id: ::WindowId(::platform::WindowId::Wayland(wid)), - }; - self.buffer.push_back(evt); - } - - pub fn send_raw_event(&mut self, evt: ::Event) { - self.buffer.push_back(evt); + pub fn send_event(&mut self, evt: ::event::WindowEvent, wid: WindowId) { + self.buffer.push_back((evt, ::window::WindowId(::platform_impl::WindowId::Wayland(wid)))); } - fn empty_with(&mut self, callback: &mut F) + fn empty_with(&mut self, mut callback: F) where - F: FnMut(::Event), + F: FnMut(::event::Event), { - for evt in self.buffer.drain(..) { - callback(evt) + for (evt, wid) in self.buffer.drain(..) { + callback(::event::Event::WindowEvent { event: evt, window_id: wid}) } } } -pub struct EventLoop { - // The Event Queue - pub evq: RefCell, +pub struct EventLoop { + // The loop + inner_loop: ::calloop::EventLoop<()>, + // The wayland display + pub display: Arc, + // the output manager + pub outputs: OutputMgr, // our sink, shared with some handlers, buffering the events - sink: Arc>, - // Whether or not there is a pending `Awakened` event to be emitted. - pending_wakeup: Arc, + sink: Arc>, + pending_user_events: Rc>>, + _user_source: ::calloop::Source<::calloop::channel::Channel>, + user_sender: ::calloop::channel::Sender, + _kbd_source: ::calloop::Source<::calloop::channel::Channel<(::event::WindowEvent, super::WindowId)>>, + window_target: RootELW +} + +// A handle that can be sent across threads and used to wake up the `EventLoop`. +// +// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. +#[derive(Clone)] +pub struct EventLoopProxy { + user_sender: ::calloop::channel::Sender +} + +pub struct EventLoopWindowTarget { + // the event queue + pub evq: RefCell<::calloop::Source>, // The window store pub store: Arc>, // the env @@ -70,57 +80,40 @@ pub struct EventLoop { // The wayland display pub display: Arc, // The list of seats - pub seats: Arc)>>>, + pub seats: Arc>>, + _marker: ::std::marker::PhantomData } -// A handle that can be sent across threads and used to wake up the `EventLoop`. -// -// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. -#[derive(Clone)] -pub struct EventLoopProxy { - display: Weak, - pending_wakeup: Weak, -} - -impl EventLoopProxy { - // Causes the `EventLoop` to stop blocking on `run_forever` and emit an `Awakened` event. - // - // Returns `Err` if the associated `EventLoop` no longer exists. - pub fn wakeup(&self) -> Result<(), EventLoopClosed> { - let display = self.display.upgrade(); - let wakeup = self.pending_wakeup.upgrade(); - match (display, wakeup) { - (Some(display), Some(wakeup)) => { - // Update the `EventLoop`'s `pending_wakeup` flag. - wakeup.store(true, Ordering::Relaxed); - // Cause the `EventLoop` to break from `dispatch` if it is currently blocked. - let _ = display.sync(|callback| callback.implement(|_, _| {}, ())); - display.flush().map_err(|_| EventLoopClosed)?; - Ok(()) - } - _ => Err(EventLoopClosed), - } +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + self.user_sender.send(event).map_err(|_| EventLoopClosed) } } -impl EventLoop { - pub fn new() -> Result { +impl EventLoop { + pub fn new() -> Result, ConnectError> { let (display, mut event_queue) = Display::connect_to_env()?; let display = Arc::new(display); - let pending_wakeup = Arc::new(AtomicBool::new(false)); - let sink = Arc::new(Mutex::new(EventLoopSink::new())); + let sink = Arc::new(Mutex::new(WindowEventsSink::new())); let store = Arc::new(Mutex::new(WindowStore::new())); let seats = Arc::new(Mutex::new(Vec::new())); + let inner_loop = ::calloop::EventLoop::new().unwrap(); + + let (kbd_sender, kbd_channel) = ::calloop::channel::channel(); + let kbd_sink = sink.clone(); + let kbd_source = inner_loop.handle().insert_source(kbd_channel, move |evt, &mut()| { + if let ::calloop::channel::Event::Msg((evt, wid)) = evt { + kbd_sink.lock().unwrap().send_event(evt, wid); + } + }).unwrap(); + let mut seat_manager = SeatManager { sink: sink.clone(), store: store.clone(), seats: seats.clone(), - events_loop_proxy: EventsLoopProxy { - display: Arc::downgrade(&display), - pending_wakeup: Arc::downgrade(&pending_wakeup), - }, + kbd_sender, }; let env = Environment::from_display_with_cb( @@ -142,92 +135,153 @@ impl EventLoop { }, ).unwrap(); + let source = inner_loop.handle().insert_source(event_queue, |(), &mut ()| {}).unwrap(); + + let pending_user_events = Rc::new(RefCell::new(VecDeque::new())); + let pending_user_events2 = pending_user_events.clone(); + + let (user_sender, user_channel) = ::calloop::channel::channel(); + + let user_source = inner_loop.handle().insert_source(user_channel, move |evt, &mut()| { + if let ::calloop::channel::Event::Msg(msg) = evt { + pending_user_events2.borrow_mut().push_back(msg); + } + }).unwrap(); + Ok(EventLoop { - display, - evq: RefCell::new(event_queue), + inner_loop, sink, - pending_wakeup, - store, - env, - cleanup_needed: Arc::new(Mutex::new(false)), - seats, + pending_user_events, + display: display.clone(), + outputs: env.outputs.clone(), + _user_source: user_source, + user_sender, + _kbd_source: kbd_source, + window_target: RootELW { + p: ::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget { + evq: RefCell::new(source), + store, + env, + cleanup_needed: Arc::new(Mutex::new(false)), + seats, + display, + _marker: ::std::marker::PhantomData + }), + _marker: ::std::marker::PhantomData + } }) } - pub fn create_proxy(&self) -> EventLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { - display: Arc::downgrade(&self.display), - pending_wakeup: Arc::downgrade(&self.pending_wakeup), + user_sender: self.user_sender.clone() } } - pub fn poll_events(&mut self, mut callback: F) - where - F: FnMut(::Event), + pub fn run(mut self, callback: F) -> ! + where F: 'static + FnMut(::event::Event, &RootELW, &mut ControlFlow) { - // send pending events to the server - self.display.flush().expect("Wayland connection lost."); - - // dispatch any pre-buffered events - self.sink.lock().unwrap().empty_with(&mut callback); - - // try to read pending events - if let Some(h) = self.evq.get_mut().prepare_read() { - h.read_events().expect("Wayland connection lost."); - } - // dispatch wayland events - self.evq - .get_mut() - .dispatch_pending() - .expect("Wayland connection lost."); - self.post_dispatch_triggers(); - - // dispatch buffered events to client - self.sink.lock().unwrap().empty_with(&mut callback); + self.run_return(callback); + ::std::process::exit(0); } - pub fn run_forever(&mut self, mut callback: F) - where - F: FnMut(::Event) -> ControlFlow, + pub fn run_return(&mut self, mut callback: F) + where F: FnMut(::event::Event, &RootELW, &mut ControlFlow) { // send pending events to the server self.display.flush().expect("Wayland connection lost."); - // Check for control flow by wrapping the callback. - let control_flow = ::std::cell::Cell::new(ControlFlow::Continue); - let mut callback = |event| { - if let ControlFlow::Break = callback(event) { - control_flow.set(ControlFlow::Break); - } - }; + let mut control_flow = ControlFlow::default(); + + let sink = self.sink.clone(); + let user_events = self.pending_user_events.clone(); - // dispatch any pre-buffered events - self.post_dispatch_triggers(); - self.sink.lock().unwrap().empty_with(&mut callback); + callback(::event::Event::NewEvents(::event::StartCause::Init), &self.window_target, &mut control_flow); loop { - // dispatch events blocking if needed - self.evq - .get_mut() - .dispatch() - .expect("Wayland connection lost."); self.post_dispatch_triggers(); // empty buffer of events - self.sink.lock().unwrap().empty_with(&mut callback); + { + let mut guard = sink.lock().unwrap(); + guard.empty_with(|evt| callback(evt, &self.window_target, &mut control_flow)); + } + // empty user events + { + let mut guard = user_events.borrow_mut(); + for evt in guard.drain(..) { + callback(::event::Event::UserEvent(evt), &self.window_target, &mut control_flow); + } + } - if let ControlFlow::Break = control_flow.get() { - break; + callback(::event::Event::EventsCleared, &self.window_target, &mut control_flow); + + // send pending events to the server + self.display.flush().expect("Wayland connection lost."); + + match control_flow { + ControlFlow::Exit => break, + ControlFlow::Poll => { + // non-blocking dispatch + self.inner_loop.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()).unwrap(); + control_flow = ControlFlow::default(); + callback(::event::Event::NewEvents(::event::StartCause::Poll), &self.window_target, &mut control_flow); + }, + ControlFlow::Wait => { + self.inner_loop.dispatch(None, &mut ()).unwrap(); + control_flow = ControlFlow::default(); + callback( + ::event::Event::NewEvents(::event::StartCause::WaitCancelled { + start: Instant::now(), + requested_resume: None + }), + &self.window_target, + &mut control_flow + ); + }, + ControlFlow::WaitUntil(deadline) => { + let start = Instant::now(); + // compute the blocking duration + let duration = deadline.duration_since(::std::cmp::max(deadline, start)); + self.inner_loop.dispatch(Some(duration), &mut ()).unwrap(); + control_flow = ControlFlow::default(); + let now = Instant::now(); + if now < deadline { + callback( + ::event::Event::NewEvents(::event::StartCause::WaitCancelled { + start, + requested_resume: Some(deadline) + }), + &self.window_target, + &mut control_flow + ); + } else { + callback( + ::event::Event::NewEvents(::event::StartCause::ResumeTimeReached { + start, + requested_resume: deadline + }), + &self.window_target, + &mut control_flow + ); + } + }, } } + + callback(::event::Event::LoopDestroyed, &self.window_target, &mut control_flow); } pub fn get_primary_monitor(&self) -> MonitorHandle { - get_primary_monitor(&self.env.outputs) + get_primary_monitor(&self.outputs) } pub fn get_available_monitors(&self) -> VecDeque { - get_available_monitors(&self.env.outputs) + get_available_monitors(&self.outputs) + } + + pub fn window_target(&self) -> &RootELW { + &self.window_target } } @@ -235,34 +289,33 @@ impl EventLoop { * Private EventLoop Internals */ -impl EventLoop { +impl EventLoop { fn post_dispatch_triggers(&mut self) { let mut sink = self.sink.lock().unwrap(); - // process a possible pending wakeup call - if self.pending_wakeup.load(Ordering::Relaxed) { - sink.send_raw_event(::Event::Awakened); - self.pending_wakeup.store(false, Ordering::Relaxed); - } + let window_target = match self.window_target.p { + ::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt, + _ => unreachable!() + }; // prune possible dead windows { - let mut cleanup_needed = self.cleanup_needed.lock().unwrap(); + let mut cleanup_needed = window_target.cleanup_needed.lock().unwrap(); if *cleanup_needed { - let pruned = self.store.lock().unwrap().cleanup(); + let pruned = window_target.store.lock().unwrap().cleanup(); *cleanup_needed = false; for wid in pruned { - sink.send_event(::WindowEvent::Destroyed, wid); + sink.send_event(::event::WindowEvent::Destroyed, wid); } } } // process pending resize/refresh - self.store.lock().unwrap().for_each( + window_target.store.lock().unwrap().for_each( |newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| { if let Some(frame) = frame { if let Some((w, h)) = newsize { frame.resize(w, h); frame.refresh(); - let logical_size = ::LogicalSize::new(w as f64, h as f64); - sink.send_event(::WindowEvent::Resized(logical_size), wid); + let logical_size = ::dpi::LogicalSize::new(w as f64, h as f64); + sink.send_event(::event::WindowEvent::Resized(logical_size), wid); *size = (w, h); } else if frame_refresh { frame.refresh(); @@ -272,13 +325,13 @@ impl EventLoop { } } if let Some(dpi) = new_dpi { - sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid); + sink.send_event(::event::WindowEvent::HiDpiFactorChanged(dpi as f64), wid); } if refresh { - sink.send_event(::WindowEvent::Redraw, wid); + sink.send_event(::event::WindowEvent::RedrawRequested, wid); } if closed { - sink.send_event(::WindowEvent::CloseRequested, wid); + sink.send_event(::event::WindowEvent::CloseRequested, wid); } }, ) @@ -290,15 +343,14 @@ impl EventLoop { */ struct SeatManager { - sink: Arc>, + sink: Arc>, store: Arc>, - seats: Arc)>>>, - event_loop_proxy: EventLoopProxy, + seats: Arc>>, + kbd_sender: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)> } impl SeatManager { - fn add_seat(&mut self, id: u32, version: u32, registry: Proxy) { - use self::wl_registry::RequestsTrait as RegistryRequests; + fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) { use std::cmp::min; let mut seat_data = SeatData { @@ -307,12 +359,12 @@ impl SeatManager { pointer: None, keyboard: None, touch: None, - events_loop_proxy: self.events_loop_proxy.clone(), + kbd_sender: self.kbd_sender.clone(), modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), }; let seat = registry .bind(min(version, 5), id, move |seat| { - seat.implement(move |event, seat| { + seat.implement_closure(move |event, seat| { seat_data.receive(event, seat) }, ()) }) @@ -322,85 +374,28 @@ impl SeatManager { } fn remove_seat(&mut self, id: u32) { - use self::wl_seat::RequestsTrait as SeatRequests; -<<<<<<< HEAD:src/platform_impl/linux/wayland/event_loop.rs - match evt { - GlobalEvent::New { - id, - ref interface, - version, - } if interface == "wl_seat" => - { - use std::cmp::min; - - let mut seat_data = SeatData { - sink: self.sink.clone(), - store: self.store.clone(), - pointer: None, - keyboard: None, - touch: None, - events_loop_proxy: self.events_loop_proxy.clone(), - modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), - }; - let seat = registry -<<<<<<< HEAD - .bind(min(version, 5), id, move |seat| { - seat.implement(move |event, seat| { - seat_data.receive(event, seat) - }, ()) - }) - .unwrap(); -======= - .bind::(min(version, 5), id) - .unwrap() - .implement(SeatData { - sink: self.sink.clone(), - store: self.store.clone(), - pointer: None, - keyboard: None, - touch: None, - event_loop_proxy: self.event_loop_proxy.clone(), - }); ->>>>>>> Change instances of "events_loop" to "event_loop" - self.store.lock().unwrap().new_seat(&seat); - self.seats.lock().unwrap().push((id, seat)); - } - GlobalEvent::Removed { id, ref interface } if interface == "wl_seat" => { - let mut seats = self.seats.lock().unwrap(); - if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { - let (_, seat) = seats.swap_remove(idx); - if seat.version() >= 5 { - seat.release(); - } - } -======= let mut seats = self.seats.lock().unwrap(); if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { let (_, seat) = seats.swap_remove(idx); - if seat.version() >= 5 { + if seat.as_ref().version() >= 5 { seat.release(); ->>>>>>> master:src/platform/linux/wayland/event_loop.rs } } } } struct SeatData { - sink: Arc>, + sink: Arc>, store: Arc>, - pointer: Option>, - keyboard: Option>, - touch: Option>, -<<<<<<< HEAD - events_loop_proxy: EventsLoopProxy, + kbd_sender: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>, + pointer: Option, + keyboard: Option, + touch: Option, modifiers_tracker: Arc>, -======= - event_loop_proxy: EventLoopProxy, ->>>>>>> Change instances of "events_loop" to "event_loop" } impl SeatData { - fn receive(&mut self, evt: wl_seat::Event, seat: Proxy) { + fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) { match evt { wl_seat::Event::Name { .. } => (), wl_seat::Event::Capabilities { capabilities } => { @@ -416,8 +411,7 @@ impl SeatData { // destroy pointer if applicable if !capabilities.contains(wl_seat::Capability::Pointer) { if let Some(pointer) = self.pointer.take() { - if pointer.version() >= 3 { - use self::wl_pointer::RequestsTrait; + if pointer.as_ref().version() >= 3 { pointer.release(); } } @@ -426,20 +420,14 @@ impl SeatData { if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() { self.keyboard = Some(super::keyboard::init_keyboard( &seat, - self.sink.clone(), -<<<<<<< HEAD - self.events_loop_proxy.clone(), + self.kbd_sender.clone(), self.modifiers_tracker.clone(), -======= - self.event_loop_proxy.clone(), ->>>>>>> Change instances of "events_loop" to "event_loop" )) } // destroy keyboard if applicable if !capabilities.contains(wl_seat::Capability::Keyboard) { if let Some(kbd) = self.keyboard.take() { - if kbd.version() >= 3 { - use self::wl_keyboard::RequestsTrait; + if kbd.as_ref().version() >= 3 { kbd.release(); } } @@ -455,13 +443,13 @@ impl SeatData { // destroy touch if applicable if !capabilities.contains(wl_seat::Capability::Touch) { if let Some(touch) = self.touch.take() { - if touch.version() >= 3 { - use self::wl_touch::RequestsTrait; + if touch.as_ref().version() >= 3 { touch.release(); } } } - } + }, + _ => unreachable!() } } } @@ -469,20 +457,17 @@ impl SeatData { impl Drop for SeatData { fn drop(&mut self) { if let Some(pointer) = self.pointer.take() { - if pointer.version() >= 3 { - use self::wl_pointer::RequestsTrait; + if pointer.as_ref().version() >= 3 { pointer.release(); } } if let Some(kbd) = self.keyboard.take() { - if kbd.version() >= 3 { - use self::wl_keyboard::RequestsTrait; + if kbd.as_ref().version() >= 3 { kbd.release(); } } if let Some(touch) = self.touch.take() { - if touch.version() >= 3 { - use self::wl_touch::RequestsTrait; + if touch.as_ref().version() >= 3 { touch.release(); } } @@ -494,7 +479,7 @@ impl Drop for SeatData { */ pub struct MonitorHandle { - pub(crate) proxy: Proxy, + pub(crate) proxy: wl_output::WlOutput, pub(crate) mgr: OutputMgr, } diff --git a/src/platform_impl/linux/wayland/keyboard.rs b/src/platform_impl/linux/wayland/keyboard.rs index d8457d5d74..420d0ab660 100644 --- a/src/platform_impl/linux/wayland/keyboard.rs +++ b/src/platform_impl/linux/wayland/keyboard.rs @@ -1,22 +1,18 @@ use std::sync::{Arc, Mutex}; -use super::{make_wid, DeviceId, EventLoopProxy, EventLoopSink}; +use super::{make_wid, DeviceId}; use sctk::keyboard::{ self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, }; -use sctk::reexports::client::protocol::wl_keyboard; -use sctk::reexports::client::Proxy; -use sctk::reexports::client::protocol::wl_seat; -use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests; +use sctk::reexports::client::protocol::{wl_keyboard, wl_seat}; -use {ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; +use event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; pub fn init_keyboard( - seat: &Proxy, - sink: Arc>, - event_loop_proxy: EventLoopProxy, + seat: &wl_seat::WlSeat, + sink: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>, modifiers_tracker: Arc>, -) -> Proxy { +) -> wl_keyboard::WlKeyboard { // { variables to be captured by the closures let target = Arc::new(Mutex::new(None)); let my_sink = sink.clone(); @@ -30,18 +26,12 @@ pub fn init_keyboard( move |evt: KbEvent, _| match evt { KbEvent::Enter { surface, .. } => { let wid = make_wid(&surface); - my_sink - .lock() - .unwrap() - .send_event(WindowEvent::Focused(true), wid); + my_sink.send((WindowEvent::Focused(true), wid)).unwrap(); *target.lock().unwrap() = Some(wid); } KbEvent::Leave { surface, .. } => { let wid = make_wid(&surface); - my_sink - .lock() - .unwrap() - .send_event(WindowEvent::Focused(false), wid); + my_sink.send((WindowEvent::Focused(false), wid)).unwrap(); *target.lock().unwrap() = None; } KbEvent::Key { @@ -55,12 +45,12 @@ pub fn init_keyboard( let state = match state { wl_keyboard::KeyState::Pressed => ElementState::Pressed, wl_keyboard::KeyState::Released => ElementState::Released, + _ => unreachable!(), }; let vkcode = key_to_vkey(rawkey, keysym); - let mut guard = my_sink.lock().unwrap(); - guard.send_event( - WindowEvent::KeyboardInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + my_sink.send( + (WindowEvent::KeyboardInput { + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), input: KeyboardInput { state: state, scancode: rawkey, @@ -68,15 +58,15 @@ pub fn init_keyboard( modifiers: modifiers_tracker.lock().unwrap().clone(), }, }, - wid, - ); + wid) + ).unwrap(); // send char event only on key press, not release if let ElementState::Released = state { return; } if let Some(txt) = utf8 { for chr in txt.chars() { - guard.send_event(WindowEvent::ReceivedCharacter(chr), wid); + my_sink.send((WindowEvent::ReceivedCharacter(chr), wid)).unwrap(); } } } @@ -84,16 +74,15 @@ pub fn init_keyboard( KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ } KbEvent::Modifiers { modifiers: event_modifiers } => { *modifiers_tracker.lock().unwrap() = event_modifiers.into() - } + }, }, move |repeat_event: KeyRepeatEvent, _| { if let Some(wid) = *repeat_target.lock().unwrap() { let state = ElementState::Pressed; let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym); - let mut guard = repeat_sink.lock().unwrap(); - guard.send_event( + repeat_sink.send(( WindowEvent::KeyboardInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), input: KeyboardInput { state: state, scancode: repeat_event.rawkey, @@ -101,14 +90,13 @@ pub fn init_keyboard( modifiers: my_modifiers.lock().unwrap().clone(), }, }, - wid, - ); + wid) + ).unwrap(); if let Some(txt) = repeat_event.utf8 { for chr in txt.chars() { - guard.send_event(WindowEvent::ReceivedCharacter(chr), wid); + repeat_sink.send((WindowEvent::ReceivedCharacter(chr), wid)).unwrap(); } } - event_loop_proxy.wakeup().unwrap(); } }, ); @@ -128,21 +116,15 @@ pub fn init_keyboard( let my_sink = sink; // } seat.get_keyboard(|keyboard| { - keyboard.implement(move |evt, _| match evt { + keyboard.implement_closure(move |evt, _| match evt { wl_keyboard::Event::Enter { surface, .. } => { let wid = make_wid(&surface); - my_sink - .lock() - .unwrap() - .send_event(WindowEvent::Focused(true), wid); + my_sink.send((WindowEvent::Focused(true), wid)).unwrap(); target = Some(wid); } wl_keyboard::Event::Leave { surface, .. } => { let wid = make_wid(&surface); - my_sink - .lock() - .unwrap() - .send_event(WindowEvent::Focused(false), wid); + my_sink.send((WindowEvent::Focused(false), wid)).unwrap(); target = None; } wl_keyboard::Event::Key { key, state, .. } => { @@ -150,10 +132,11 @@ pub fn init_keyboard( let state = match state { wl_keyboard::KeyState::Pressed => ElementState::Pressed, wl_keyboard::KeyState::Released => ElementState::Released, + _ => unreachable!() }; - my_sink.lock().unwrap().send_event( + my_sink.send(( WindowEvent::KeyboardInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), input: KeyboardInput { state: state, scancode: key, @@ -162,7 +145,7 @@ pub fn init_keyboard( }, }, wid, - ); + )).unwrap(); } } _ => (), diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 224959d5fc..44397ea2c3 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -2,10 +2,9 @@ target_os = "netbsd", target_os = "openbsd"))] pub use self::window::Window; -pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopSink, MonitorHandle}; +pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy, WindowEventsSink, MonitorHandle}; use sctk::reexports::client::protocol::wl_surface; -use sctk::reexports::client::Proxy; mod event_loop; mod pointer; @@ -32,6 +31,6 @@ impl WindowId { } #[inline] -fn make_wid(s: &Proxy) -> WindowId { - WindowId(s.c_ptr() as usize) +fn make_wid(s: &wl_surface::WlSurface) -> WindowId { + WindowId(s.as_ref().c_ptr() as usize) } diff --git a/src/platform_impl/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs index d910a5ea64..cbada78b9b 100644 --- a/src/platform_impl/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -1,30 +1,27 @@ use std::sync::{Arc, Mutex}; -use {ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}; -use events::ModifiersState; +use event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent, ModifiersState}; use super::DeviceId; -use super::event_loop::EventLoopSink; +use super::event_loop::WindowEventsSink; use super::window::WindowStore; -use sctk::reexports::client::Proxy; use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer}; use sctk::reexports::client::protocol::wl_seat; -use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests; pub fn implement_pointer( - seat: &Proxy, - sink: Arc>, + seat: &wl_seat::WlSeat, + sink: Arc>, store: Arc>, modifiers_tracker: Arc>, -) -> Proxy { +) -> WlPointer { let mut mouse_focus = None; let mut axis_buffer = None; let mut axis_discrete_buffer = None; let mut axis_state = TouchPhase::Ended; seat.get_pointer(|pointer| { - pointer.implement(move |evt, pointer| { + pointer.implement_closure(move |evt, pointer| { let mut sink = sink.lock().unwrap(); let store = store.lock().unwrap(); match evt { @@ -39,13 +36,13 @@ pub fn implement_pointer( mouse_focus = Some(wid); sink.send_event( WindowEvent::CursorEntered { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), }, wid, ); sink.send_event( WindowEvent::CursorMoved { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), position: (surface_x, surface_y).into(), modifiers: modifiers_tracker.lock().unwrap().clone(), }, @@ -59,7 +56,7 @@ pub fn implement_pointer( if let Some(wid) = wid { sink.send_event( WindowEvent::CursorLeft { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), }, wid, ); @@ -73,7 +70,7 @@ pub fn implement_pointer( if let Some(wid) = mouse_focus { sink.send_event( WindowEvent::CursorMoved { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), position: (surface_x, surface_y).into(), modifiers: modifiers_tracker.lock().unwrap().clone(), }, @@ -86,6 +83,7 @@ pub fn implement_pointer( let state = match state { wl_pointer::ButtonState::Pressed => ElementState::Pressed, wl_pointer::ButtonState::Released => ElementState::Released, + _ => unreachable!() }; let button = match button { 0x110 => MouseButton::Left, @@ -96,7 +94,7 @@ pub fn implement_pointer( }; sink.send_event( WindowEvent::MouseInput { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), state: state, button: button, modifiers: modifiers_tracker.lock().unwrap().clone(), @@ -107,17 +105,18 @@ pub fn implement_pointer( } PtrEvent::Axis { axis, value, .. } => { if let Some(wid) = mouse_focus { - if pointer.version() < 5 { + if pointer.as_ref().version() < 5 { let (mut x, mut y) = (0.0, 0.0); // old seat compatibility match axis { // wayland vertical sign convention is the inverse of winit wl_pointer::Axis::VerticalScroll => y -= value as f32, wl_pointer::Axis::HorizontalScroll => x += value as f32, + _ => unreachable!() } sink.send_event( WindowEvent::MouseWheel { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()), phase: TouchPhase::Moved, modifiers: modifiers_tracker.lock().unwrap().clone(), @@ -130,6 +129,7 @@ pub fn implement_pointer( // wayland vertical sign convention is the inverse of winit wl_pointer::Axis::VerticalScroll => y -= value as f32, wl_pointer::Axis::HorizontalScroll => x += value as f32, + _ => unreachable!() } axis_buffer = Some((x, y)); axis_state = match axis_state { @@ -146,7 +146,7 @@ pub fn implement_pointer( if let Some((x, y)) = axis_discrete_buffer { sink.send_event( WindowEvent::MouseWheel { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), delta: MouseScrollDelta::LineDelta(x as f32, y as f32), phase: axis_state, modifiers: modifiers_tracker.lock().unwrap().clone(), @@ -156,7 +156,7 @@ pub fn implement_pointer( } else if let Some((x, y)) = axis_buffer { sink.send_event( WindowEvent::MouseWheel { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()), phase: axis_state, modifiers: modifiers_tracker.lock().unwrap().clone(), @@ -176,13 +176,15 @@ pub fn implement_pointer( // wayland vertical sign convention is the inverse of winit wl_pointer::Axis::VerticalScroll => y -= discrete, wl_pointer::Axis::HorizontalScroll => x += discrete, + _ => unreachable!() } axis_discrete_buffer = Some((x, y)); axis_state = match axis_state { TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, _ => TouchPhase::Started, } - } + }, + _ => unreachable!() } }, ()) }).unwrap() diff --git a/src/platform_impl/linux/wayland/touch.rs b/src/platform_impl/linux/wayland/touch.rs index a357f3aa0b..765d524a2f 100644 --- a/src/platform_impl/linux/wayland/touch.rs +++ b/src/platform_impl/linux/wayland/touch.rs @@ -1,15 +1,13 @@ use std::sync::{Arc, Mutex}; -use {TouchPhase, WindowEvent}; +use event::{TouchPhase, WindowEvent}; use super::{DeviceId, WindowId}; -use super::event_loop::EventLoopSink; +use super::event_loop::WindowEventsSink; use super::window::WindowStore; -use sctk::reexports::client::Proxy; use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch}; use sctk::reexports::client::protocol::wl_seat; -use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests; struct TouchPoint { wid: WindowId, @@ -18,13 +16,13 @@ struct TouchPoint { } pub(crate) fn implement_touch( - seat: &Proxy, - sink: Arc>, + seat: &wl_seat::WlSeat, + sink: Arc>, store: Arc>, -) -> Proxy { +) -> WlTouch { let mut pending_ids = Vec::new(); seat.get_touch(|touch| { - touch.implement(move |evt, _| { + touch.implement_closure(move |evt, _| { let mut sink = sink.lock().unwrap(); let store = store.lock().unwrap(); match evt { @@ -34,8 +32,8 @@ pub(crate) fn implement_touch( let wid = store.find_wid(&surface); if let Some(wid) = wid { sink.send_event( - WindowEvent::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + WindowEvent::Touch(::event::Touch { + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), phase: TouchPhase::Started, location: (x, y).into(), id: id as u64, @@ -54,8 +52,8 @@ pub(crate) fn implement_touch( if let Some(idx) = idx { let pt = pending_ids.remove(idx); sink.send_event( - WindowEvent::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + WindowEvent::Touch(::event::Touch { + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), phase: TouchPhase::Ended, location: pt.location.into(), id: id as u64, @@ -69,8 +67,8 @@ pub(crate) fn implement_touch( if let Some(pt) = pt { pt.location = (x, y); sink.send_event( - WindowEvent::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + WindowEvent::Touch(::event::Touch { + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), phase: TouchPhase::Moved, location: (x, y).into(), id: id as u64, @@ -82,8 +80,8 @@ pub(crate) fn implement_touch( TouchEvent::Frame => (), TouchEvent::Cancel => for pt in pending_ids.drain(..) { sink.send_event( - WindowEvent::Touch(::Touch { - device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), + WindowEvent::Touch(::event::Touch { + device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)), phase: TouchPhase::Cancelled, location: pt.location.into(), id: pt.id as u64, @@ -91,6 +89,7 @@ pub(crate) fn implement_touch( pt.wid, ); }, + _ => unreachable!() } }, ()) }).unwrap() diff --git a/src/platform_impl/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs index 206538580b..afbb17e547 100644 --- a/src/platform_impl/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -1,38 +1,33 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex, Weak}; -use {CreationError, MouseCursor, WindowAttributes}; use dpi::{LogicalPosition, LogicalSize}; -<<<<<<< HEAD:src/platform_impl/linux/wayland/window.rs -use platform_impl::MonitorHandle as PlatformMonitorHandle; -use window::MonitorHandle as RootMonitorHandle; -======= -use platform::{MonitorId as PlatformMonitorId, PlatformSpecificWindowBuilderAttributes as PlAttributes}; -use window::MonitorId as RootMonitorId; ->>>>>>> master:src/platform/linux/wayland/window.rs +use platform_impl::{MonitorHandle as PlatformMonitorHandle, PlatformSpecificWindowBuilderAttributes as PlAttributes}; +use monitor::MonitorHandle as RootMonitorHandle; +use window::{CreationError, WindowAttributes, MouseCursor}; use sctk::surface::{get_dpi_factor, get_outputs}; use sctk::window::{ConceptFrame, Event as WEvent, Window as SWindow, Theme}; -use sctk::reexports::client::{Display, Proxy}; +use sctk::reexports::client::Display; use sctk::reexports::client::protocol::{wl_seat, wl_surface}; -use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests; use sctk::output::OutputMgr; -use super::{make_wid, EventLoop, MonitorHandle, WindowId}; +use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; use platform_impl::platform::wayland::event_loop::{get_available_monitors, get_primary_monitor}; pub struct Window { - surface: Proxy, + surface: wl_surface::WlSurface, frame: Arc>>, outputs: OutputMgr, // Access to info for all monitors size: Arc>, kill_switch: (Arc>, Arc>), display: Arc, need_frame_refresh: Arc>, + need_refresh: Arc> } impl Window { - pub fn new(evlp: &EventLoop, attributes: WindowAttributes, pl_attribs: PlAttributes) -> Result { + pub fn new(evlp: &EventLoopWindowTarget, attributes: WindowAttributes, pl_attribs: PlAttributes) -> Result { let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600)); // Create the window let size = Arc::new(Mutex::new((width, height))); @@ -53,9 +48,9 @@ impl Window { WEvent::Configure { new_size, .. } => { let mut store = window_store.lock().unwrap(); for window in &mut store.windows { - if window.surface.equals(&my_surface) { + if window.surface.as_ref().equals(&my_surface.as_ref()) { window.newsize = new_size; - window.need_refresh = true; + *(window.need_refresh.lock().unwrap()) = true; *(window.need_frame_refresh.lock().unwrap()) = true; return; } @@ -64,7 +59,7 @@ impl Window { WEvent::Refresh => { let store = window_store.lock().unwrap(); for window in &store.windows { - if window.surface.equals(&my_surface) { + if window.surface.as_ref().equals(&my_surface.as_ref()) { *(window.need_frame_refresh.lock().unwrap()) = true; return; } @@ -73,7 +68,7 @@ impl Window { WEvent::Close => { let mut store = window_store.lock().unwrap(); for window in &mut store.windows { - if window.surface.equals(&my_surface) { + if window.surface.as_ref().equals(&my_surface.as_ref()) { window.closed = true; return; } @@ -112,12 +107,13 @@ impl Window { let kill_switch = Arc::new(Mutex::new(false)); let need_frame_refresh = Arc::new(Mutex::new(true)); let frame = Arc::new(Mutex::new(frame)); + let need_refresh = Arc::new(Mutex::new(true)); evlp.store.lock().unwrap().windows.push(InternalWindow { closed: false, newsize: None, size: size.clone(), - need_refresh: false, + need_refresh: need_refresh.clone(), need_frame_refresh: need_frame_refresh.clone(), surface: surface.clone(), kill_switch: kill_switch.clone(), @@ -134,7 +130,8 @@ impl Window { outputs: evlp.env.outputs.clone(), size: size, kill_switch: (kill_switch, evlp.cleanup_needed.clone()), - need_frame_refresh: need_frame_refresh, + need_frame_refresh, + need_refresh, }) } @@ -178,6 +175,10 @@ impl Window { Some(self.size.lock().unwrap().clone().into()) } + pub fn request_redraw(&self) { + *self.need_refresh.lock().unwrap() = true; + } + #[inline] pub fn get_outer_size(&self) -> Option { let (w, h) = self.size.lock().unwrap().clone(); @@ -269,24 +270,16 @@ impl Window { &*self.display } - pub fn get_surface(&self) -> &Proxy { + pub fn get_surface(&self) -> &wl_surface::WlSurface { &self.surface } -<<<<<<< HEAD:src/platform_impl/linux/wayland/window.rs pub fn get_current_monitor(&self) -> MonitorHandle { - // we don't know how much each monitor sees us so... - // just return the most recent one ? - let guard = self.monitors.lock().unwrap(); - guard.monitors.last().unwrap().clone() -======= - pub fn get_current_monitor(&self) -> MonitorId { let output = get_outputs(&self.surface).last().unwrap().clone(); - MonitorId { + MonitorHandle { proxy: output, mgr: self.outputs.clone(), } ->>>>>>> master:src/platform/linux/wayland/window.rs } pub fn get_available_monitors(&self) -> VecDeque { @@ -310,10 +303,10 @@ impl Drop for Window { */ struct InternalWindow { - surface: Proxy, + surface: wl_surface::WlSurface, newsize: Option<(u32, u32)>, size: Arc>, - need_refresh: bool, + need_refresh: Arc>, need_frame_refresh: Arc>, closed: bool, kill_switch: Arc>, @@ -333,9 +326,9 @@ impl WindowStore { } } - pub fn find_wid(&self, surface: &Proxy) -> Option { + pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option { for window in &self.windows { - if surface.equals(&window.surface) { + if surface.as_ref().equals(&window.surface.as_ref()) { return Some(make_wid(surface)); } } @@ -357,7 +350,7 @@ impl WindowStore { pruned } - pub fn new_seat(&self, seat: &Proxy) { + pub fn new_seat(&self, seat: &wl_seat::WlSeat) { for window in &self.windows { if let Some(w) = window.frame.upgrade() { w.lock().unwrap().new_seat(seat); @@ -365,9 +358,9 @@ impl WindowStore { } } - fn dpi_change(&mut self, surface: &Proxy, new: i32) { + fn dpi_change(&mut self, surface: &wl_surface::WlSurface, new: i32) { for window in &mut self.windows { - if surface.equals(&window.surface) { + if surface.as_ref().equals(&window.surface.as_ref()) { window.new_dpi = Some(new); } } @@ -384,7 +377,7 @@ impl WindowStore { window.newsize.take(), &mut *(window.size.lock().unwrap()), window.new_dpi, - window.need_refresh, + ::std::mem::replace(&mut *window.need_refresh.lock().unwrap(), false), ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), window.closed, make_wid(&window.surface), @@ -393,59 +386,9 @@ impl WindowStore { if let Some(dpi) = window.new_dpi.take() { window.current_dpi = dpi; } - window.need_refresh = false; // avoid re-spamming the event window.closed = false; } } } -<<<<<<< HEAD:src/platform_impl/linux/wayland/window.rs - -/* - * Monitor list with some covenience method to compute DPI - */ -struct MonitorList { - monitors: Vec -} - -impl MonitorList { - fn new() -> MonitorList { - MonitorList { - monitors: Vec::new() - } - } - - fn compute_hidpi_factor(&self) -> i32 { - let mut factor = 1; - for monitor_id in &self.monitors { - let monitor_dpi = monitor_id.get_hidpi_factor(); - if monitor_dpi > factor { factor = monitor_dpi; } - } - factor - } - - fn add_output(&mut self, monitor: MonitorHandle) -> Option { - let old_dpi = self.compute_hidpi_factor(); - let monitor_dpi = monitor.get_hidpi_factor(); - self.monitors.push(monitor); - if monitor_dpi > old_dpi { - Some(monitor_dpi) - } else { - None - } - } - - fn del_output(&mut self, output: &Proxy) -> Option { - let old_dpi = self.compute_hidpi_factor(); - self.monitors.retain(|m| !m.proxy.equals(output)); - let new_dpi = self.compute_hidpi_factor(); - if new_dpi != old_dpi { - Some(new_dpi) - } else { - None - } - } -} -======= ->>>>>>> master:src/platform/linux/wayland/window.rs From 7164a69a2bc51fa43f4b5bf35aecfd6f866ad7ca Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 15 Feb 2019 11:28:03 +0100 Subject: [PATCH 08/34] make EventLoopProxy require T: 'static --- src/event_loop.rs | 6 +++--- src/platform_impl/linux/mod.rs | 4 ++-- src/platform_impl/linux/wayland/event_loop.rs | 4 ++-- src/platform_impl/windows/event_loop.rs | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index ca217f7c97..ab6839cdc6 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -163,11 +163,11 @@ impl Deref for EventLoop { /// Used to send custom events to `EventLoop`. #[derive(Clone)] -pub struct EventLoopProxy { +pub struct EventLoopProxy { event_loop_proxy: platform_impl::EventLoopProxy, } -impl EventLoopProxy { +impl EventLoopProxy { /// Send an event to the `EventLoop` from which this proxy was created. This emits a /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this /// function. @@ -178,7 +178,7 @@ impl EventLoopProxy { } } -impl fmt::Debug for EventLoopProxy { +impl fmt::Debug for EventLoopProxy { fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { fmtr.pad("EventLoopProxy { .. }") } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index d05b3a1f48..94f324ed5d 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -413,7 +413,7 @@ pub enum EventLoop { } #[derive(Clone)] -pub enum EventLoopProxy { +pub enum EventLoopProxy { //X(x11::EventLoopProxy), Wayland(wayland::EventLoopProxy), } @@ -545,7 +545,7 @@ impl EventLoop { //} } -impl EventLoopProxy { +impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { match *self { EventLoopProxy::Wayland(ref proxy) => proxy.send_event(event), diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index ba5145de49..3732b96b92 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -64,7 +64,7 @@ pub struct EventLoop { // // We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. #[derive(Clone)] -pub struct EventLoopProxy { +pub struct EventLoopProxy { user_sender: ::calloop::channel::Sender } @@ -84,7 +84,7 @@ pub struct EventLoopWindowTarget { _marker: ::std::marker::PhantomData } -impl EventLoopProxy { +impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { self.user_sender.send(event).map_err(|_| EventLoopClosed) } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 9bc874b6fd..1d7a4aa1a9 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -601,13 +601,13 @@ impl EventLoopThreadExecutor { type ThreadExecFn = Box>; #[derive(Clone)] -pub struct EventLoopProxy { +pub struct EventLoopProxy { target_window: HWND, event_send: Sender, } -unsafe impl Send for EventLoopProxy {} +unsafe impl Send for EventLoopProxy {} -impl EventLoopProxy { +impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { unsafe { if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 { From de20a68f0da92cdeb55e8950fb5ac3ddebf60510 Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 24 Feb 2019 13:20:15 -0500 Subject: [PATCH 09/34] Revamp device event API, as well as several misc. fixes on Windows: * When you have multiple windows, you no longer receive duplicate device events * Mouse Device Events now send X-button input * Mouse Device Events now send horizontal scroll wheel input --- src/event.rs | 204 +-------- src/event/device.rs | 153 +++++++ src/platform/windows.rs | 22 +- src/platform_impl/windows/event_loop.rs | 566 +++++++++++++----------- src/platform_impl/windows/gamepad.rs | 7 +- src/platform_impl/windows/mod.rs | 83 ++-- src/platform_impl/windows/raw_input.rs | 64 ++- src/platform_impl/windows/util.rs | 8 - src/platform_impl/windows/window.rs | 10 +- src/platform_impl/windows/xinput.rs | 5 +- 10 files changed, 590 insertions(+), 532 deletions(-) create mode 100644 src/event/device.rs diff --git a/src/event.rs b/src/event.rs index 0f3bf01a4f..3fac706d09 100644 --- a/src/event.rs +++ b/src/event.rs @@ -9,7 +9,8 @@ use std::path::PathBuf; use dpi::{LogicalPosition, LogicalSize}; use window::WindowId; -use platform_impl; + +pub mod device; /// Describes a generic event. #[derive(Clone, Debug, PartialEq)] @@ -20,10 +21,7 @@ pub enum Event { event: WindowEvent, }, /// Emitted when the OS sends an event to a device. - DeviceEvent { - device_id: DeviceId, - event: DeviceEvent, - }, + DeviceEvent(device::DeviceEvent), /// Emitted when an event is sent from [`EventLoopProxy::send_event`](../event_loop/struct.EventLoopProxy.html#method.send_event) UserEvent(T), /// Emitted when new events arrive from the OS to be processed. @@ -48,7 +46,7 @@ impl Event { match self { UserEvent(_) => Err(self), WindowEvent{window_id, event} => Ok(WindowEvent{window_id, event}), - DeviceEvent{device_id, event} => Ok(DeviceEvent{device_id, event}), + DeviceEvent(event) => Ok(DeviceEvent(event)), NewEvents(cause) => Ok(NewEvents(cause)), EventsCleared => Ok(EventsCleared), LoopDestroyed => Ok(LoopDestroyed), @@ -125,12 +123,10 @@ pub enum WindowEvent { Focused(bool), /// An event from the keyboard has been received. - KeyboardInput { device_id: DeviceId, input: KeyboardInput }, + KeyboardInput(KeyboardInput), /// The cursor has moved on the window. CursorMoved { - device_id: DeviceId, - /// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. @@ -139,16 +135,16 @@ pub enum WindowEvent { }, /// The cursor has entered the window. - CursorEntered { device_id: DeviceId }, + CursorEntered, /// The cursor has left the window. - CursorLeft { device_id: DeviceId }, + CursorLeft, /// A mouse wheel movement or touchpad scroll occurred. - MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState }, + MouseWheel { delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState }, /// An mouse button press has been received. - MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton, modifiers: ModifiersState }, + MouseInput { state: ElementState, button: MouseButton, modifiers: ModifiersState }, /// Touchpad pressure event. @@ -156,10 +152,7 @@ pub enum WindowEvent { /// At the moment, only supported on Apple forcetouch-capable macbooks. /// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad /// is being pressed) and stage (integer representing the click level). - TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 }, - - /// Motion on some analog axis. May report data redundant to other, more specific events. - AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 }, + TouchpadPressure { pressure: f32, stage: i64 }, /// The OS or application has requested that the window be redrawn. RedrawRequested, @@ -179,178 +172,6 @@ pub enum WindowEvent { HiDpiFactorChanged(f64), } -/// Identifier of an input device. -/// -/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which -/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or -/// physical. Virtual devices typically aggregate inputs from multiple physical devices. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(pub(crate) platform_impl::DeviceId); - -impl DeviceId { - /// Returns a dummy `DeviceId`, useful for unit testing. The only guarantee made about the return - /// value of this function is that it will always be equal to itself and to future values returned - /// by this function. No other guarantees are made. This may be equal to a real `DeviceId`. - /// - /// **Passing this into a winit function will result in undefined behavior.** - pub unsafe fn dummy() -> Self { - DeviceId(platform_impl::DeviceId::dummy()) - } -} - -/// A hint suggesting the type of button that was pressed. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ButtonHint { - LeftMouse, - MiddleMouse, - RightMouse, - - Start, - Select, - - /// The north face button. - /// - /// * Nintendo: X - /// * Playstation: Triangle - /// * XBox: Y - North, - /// The south face button. - /// - /// * Nintendo: B - /// * Playstation: X - /// * XBox: A - South, - /// The east face button. - /// - /// * Nintendo: A - /// * Playstation: Circle - /// * XBox: B - East, - /// The west face button. - /// - /// * Nintendo: Y - /// * Playstation: Square - /// * XBox: X - West, - - LeftStick, - RightStick, - - LeftTrigger, - RightTrigger, - - LeftShoulder, - RightShoulder, - - DPadUp, - DPadDown, - DPadLeft, - DPadRight, -} - -impl ButtonHint { - #[inline] - pub fn is_mouse_button(&self) -> bool { - use self::ButtonHint::*; - match *self { - LeftMouse | MiddleMouse | RightMouse => true, - _ => false, - } - } - - #[inline] - pub fn is_gamepad_button(&self) -> bool { - !self.is_mouse_button() - } -} - -/// A hint suggesting the type of axis that moved. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum AxisHint { - MouseX, - MouseY, - - LeftStickX, - LeftStickY, - - RightStickX, - RightStickY, - - LeftTrigger, - RightTrigger, - - /// This is supposed to have a specialized meaning, referring to a point-of-view switch present on joysticks used - /// for flight simulation. However, Xbox 360 controllers (and their derivatives) use a hat switch for the D-pad. - HatSwitch, - - DPadUp, - DPadDown, - DPadLeft, - DPadRight, -} - -impl AxisHint { - #[inline] - pub fn is_mouse_axis(&self) -> bool { - use self::AxisHint::*; - match *self { - MouseX | MouseY => true, - _ => false, - } - } - - #[inline] - pub fn is_gamepad_axis(&self) -> bool { - !self.is_mouse_axis() - } -} - -/// Represents raw hardware events that are not associated with any particular window. -/// -/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person -/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because -/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs -/// may not match. -/// -/// Note that these events are delivered regardless of input focus. -#[derive(Clone, Debug, PartialEq)] -pub enum DeviceEvent { - Added, - Removed, - - /// Change in physical position of a pointing device. - /// - /// This represents raw, unfiltered physical motion. Not to be confused with `WindowEvent::CursorMoved`. - MouseMotion { - /// (x, y) change in position in unspecified units. - /// - /// Different devices may use different units. - delta: (f64, f64), - }, - - /// Physical scroll event - MouseWheel { - delta: MouseScrollDelta, - }, - - /// Motion on some analog axis. This event will be reported for all arbitrary input devices - /// that winit supports on this platform, including mouse devices. If the device is a mouse - /// device then this will be reported alongside the MouseMotion event. - Motion { - axis: AxisId, - hint: Option, - value: f64, - }, - - Button { - button: ButtonId, - hint: Option, - state: ElementState, - }, - Key(KeyboardInput), - Text { codepoint: char }, -} - /// Describes a keyboard input event. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -404,7 +225,6 @@ pub enum TouchPhase { /// Touch may be cancelled if for example window lost focus. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Touch { - pub device_id: DeviceId, pub phase: TouchPhase, pub location: LogicalPosition, /// unique identifier of a finger. @@ -421,7 +241,7 @@ pub type AxisId = u32; pub type ButtonId = u32; /// Describes the input state of a key. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ElementState { Pressed, @@ -429,7 +249,7 @@ pub enum ElementState { } /// Describes a button of a mouse controller. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MouseButton { Left, diff --git a/src/event/device.rs b/src/event/device.rs new file mode 100644 index 0000000000..216958a31e --- /dev/null +++ b/src/event/device.rs @@ -0,0 +1,153 @@ +use platform_impl; +use event::{AxisId, ButtonId, ElementState, KeyboardInput, MouseButton}; + +/// A hint suggesting the type of button that was pressed. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ButtonHint { + Start, + Select, + + /// The north face button. + /// + /// * Nintendo: X + /// * Playstation: Triangle + /// * XBox: Y + North, + /// The south face button. + /// + /// * Nintendo: B + /// * Playstation: X + /// * XBox: A + South, + /// The east face button. + /// + /// * Nintendo: A + /// * Playstation: Circle + /// * XBox: B + East, + /// The west face button. + /// + /// * Nintendo: Y + /// * Playstation: Square + /// * XBox: X + West, + + LeftStick, + RightStick, + + LeftTrigger, + RightTrigger, + + LeftShoulder, + RightShoulder, + + DPadUp, + DPadDown, + DPadLeft, + DPadRight, +} + +/// A hint suggesting the type of axis that moved. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum AxisHint { + LeftStickX, + LeftStickY, + + RightStickX, + RightStickY, + + LeftTrigger, + RightTrigger, + + /// This is supposed to have a specialized meaning, referring to a point-of-view switch present on joysticks used + /// for flight simulation. However, Xbox 360 controllers (and their derivatives) use a hat switch for the D-pad. + HatSwitch, + + DPadUp, + DPadDown, + DPadLeft, + DPadRight, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum DeviceEvent { + MouseEvent(MouseId, MouseEvent), + KeyboardEvent(KeyboardId, KeyboardEvent), + GamepadEvent(GamepadHandle, GamepadEvent), +} + +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] +pub enum MouseEvent { + Added, + Removed, + Button { + state: ElementState, + button: MouseButton, + button_id: ButtonId, + }, + Moved(f64, f64), + Wheel(f64, f64), +} + +#[derive(Debug, Copy, Clone, PartialEq, Hash)] +pub enum KeyboardEvent { + Added, + Removed, + Input(KeyboardInput), +} + +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] +pub enum GamepadEvent { + Added, + Removed, + Axis { + axis: AxisId, + hint: Option, + value: f64, + }, + Button { + button_id: ButtonId, + hint: Option, + state: ElementState, + }, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MouseId(pub(crate) platform_impl::MouseId); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct KeyboardId(pub(crate) platform_impl::KeyboardId); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GamepadHandle(pub(crate) platform_impl::GamepadHandle); + +impl MouseId { + /// Returns a dummy `MouseId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `MouseId`. + /// + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + MouseId(platform_impl::MouseId::dummy()) + } +} + +impl KeyboardId { + /// Returns a dummy `KeyboardId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `KeyboardId`. + /// + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + KeyboardId(platform_impl::KeyboardId::dummy()) + } +} + +impl GamepadHandle { + /// Returns a dummy `GamepadHandle`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `GamepadHandle`. + /// + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + GamepadHandle(platform_impl::GamepadHandle::dummy()) + } +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 7a9de6880a..55956269ad 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -5,9 +5,9 @@ use std::os::raw::c_void; use libc; use winapi::shared::windef::HWND; -use event::DeviceId; use monitor::MonitorHandle; use event_loop::EventLoop; +use event::device::{MouseId, KeyboardId, GamepadHandle}; use window::{Icon, Window, WindowBuilder}; use platform_impl::EventLoop as WindowsEventLoop; @@ -104,15 +104,29 @@ impl MonitorHandleExtWindows for MonitorHandle { } } -/// Additional methods on `DeviceId` that are specific to Windows. -pub trait DeviceIdExtWindows { +/// Additional methods on device types that are specific to Windows. +pub trait DeviceExtWindows { /// 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 DeviceIdExtWindows for DeviceId { +impl DeviceExtWindows for MouseId { + #[inline] + fn get_persistent_identifier(&self) -> Option { + self.0.get_persistent_identifier() + } +} + +impl DeviceExtWindows for KeyboardId { + #[inline] + fn get_persistent_identifier(&self) -> Option { + self.0.get_persistent_identifier() + } +} + +impl DeviceExtWindows for GamepadHandle { #[inline] fn get_persistent_identifier(&self) -> Option { self.0.get_persistent_identifier() diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 8b0ff7df00..c653a84b64 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -22,7 +22,7 @@ use std::sync::mpsc::{self, Sender, Receiver}; use std::time::{Duration, Instant}; use std::rc::Rc; use std::cell::RefCell; -use std::collections::VecDeque; +use std::collections::{HashMap, VecDeque}; use std::marker::PhantomData; use parking_lot::Mutex; @@ -41,46 +41,55 @@ use winapi::shared::minwindef::{ use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::shared::{windowsx, winerror}; use winapi::um::{winuser, winbase, ole2, processthreadsapi, commctrl, libloaderapi}; -use winapi::um::winnt::{LONG, LPCSTR, SHORT}; +use winapi::um::winnt::{HANDLE, LONG, LPCSTR, SHORT}; use window::WindowId as RootWindowId; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; use dpi::{LogicalPosition, LogicalSize, PhysicalSize}; -use event::{AxisHint, ButtonHint, DeviceEvent, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent}; -use platform_impl::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; -use platform_impl::platform::dpi::{ - become_dpi_aware, - dpi_to_scale_factor, - enable_non_client_dpi_scaling, - get_hwnd_scale_factor, +use event::{ + MouseButton, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent, + device::{DeviceEvent, GamepadEvent, KeyboardEvent, MouseEvent} +}; +use platform_impl::platform::{ + MouseId, KeyboardId, GamepadHandle, WindowId, + dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, get_hwnd_scale_factor}, + drop_handler::FileDropHandler, + event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, + gamepad::GAMEPADS, + raw_input::{self, get_raw_input_data, get_raw_mouse_button_state}, + util, + window::adjust_size, + window_state::{CursorFlags, WindowFlags, WindowState}, }; -use platform_impl::platform::drop_handler::FileDropHandler; -use platform_impl::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey}; -use platform_impl::platform::gamepad::GAMEPADS; -use platform_impl::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state}; -use platform_impl::platform::window::adjust_size; -use platform_impl::platform::window_state::{CursorFlags, WindowFlags, WindowState}; pub(crate) struct SubclassInput { pub window_state: Arc>, - pub event_loop_runner: EventLoopRunnerShared, + pub shared_data: Rc>, pub file_drop_handler: FileDropHandler, } impl SubclassInput { unsafe fn send_event(&self, event: Event) { - self.event_loop_runner.send_event(event); + self.shared_data.runner_shared.send_event(event); } } struct ThreadMsgTargetSubclassInput { - event_loop_runner: EventLoopRunnerShared, + shared_data: Rc>, user_event_receiver: Receiver, + active_device_ids: HashMap, +} + +#[derive(Debug, Clone, Copy)] +enum DeviceId { + Mouse(MouseId), + Keyboard(KeyboardId), + Gamepad(GamepadHandle), } impl ThreadMsgTargetSubclassInput { unsafe fn send_event(&self, event: Event) { - self.event_loop_runner.send_event(event); + self.shared_data.runner_shared.send_event(event); } } @@ -93,7 +102,7 @@ pub struct EventLoopWindowTarget { thread_id: DWORD, trigger_newevents_on_redraw: Arc, thread_msg_target: HWND, - pub(crate) runner_shared: EventLoopRunnerShared, + pub(crate) shared_data: Rc>, } impl EventLoop { @@ -109,11 +118,13 @@ impl EventLoop { become_dpi_aware(dpi_aware); let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; - let runner_shared = Rc::new(ELRShared { - runner: RefCell::new(None), - buffer: RefCell::new(VecDeque::new()), + let shared_data = Rc::new(SubclassSharedData { + runner_shared: ELRShared { + runner: RefCell::new(None), + buffer: RefCell::new(VecDeque::new()), + }, }); - let (thread_msg_target, thread_msg_sender) = thread_event_target_window(runner_shared.clone()); + let (thread_msg_target, thread_msg_sender) = thread_event_target_window(shared_data.clone()); EventLoop { thread_msg_sender, @@ -122,7 +133,7 @@ impl EventLoop { thread_id, trigger_newevents_on_redraw: Arc::new(AtomicBool::new(true)), thread_msg_target, - runner_shared, + shared_data, }, _marker: PhantomData, }, @@ -150,10 +161,10 @@ impl EventLoop { } ) }; { - let runner_shared = self.window_target.p.runner_shared.clone(); - let mut runner_ref = runner_shared.runner.borrow_mut(); + let shared_data = self.window_target.p.shared_data.clone(); + let mut runner_ref = shared_data.runner_shared.runner.borrow_mut(); loop { - let event = runner_shared.buffer.borrow_mut().pop_front(); + let event = shared_data.runner_shared.buffer.borrow_mut().pop_front(); match event { Some(e) => { runner.process_event(e); }, None => break @@ -163,7 +174,7 @@ impl EventLoop { } macro_rules! runner { - () => { self.window_target.p.runner_shared.runner.borrow_mut().as_mut().unwrap() }; + () => { self.window_target.p.shared_data.runner_shared.runner.borrow_mut().as_mut().unwrap() }; } unsafe { @@ -205,7 +216,7 @@ impl EventLoop { } runner!().call_event_handler(Event::LoopDestroyed); - *self.window_target.p.runner_shared.runner.borrow_mut() = None; + *self.window_target.p.shared_data.runner_shared.runner.borrow_mut() = None; } pub fn create_proxy(&self) -> EventLoopProxy { @@ -227,7 +238,10 @@ impl EventLoopWindowTarget { } } -pub(crate) type EventLoopRunnerShared = Rc>; +pub(crate) struct SubclassSharedData { + pub runner_shared: ELRShared, +} + pub(crate) struct ELRShared { runner: RefCell>>, buffer: RefCell>>, @@ -692,7 +706,7 @@ lazy_static! { }; } -fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> (HWND, Sender) { +fn thread_event_target_window(shared_data: Rc>) -> (HWND, Sender) { unsafe { let window = winuser::CreateWindowExW( winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED, @@ -718,8 +732,9 @@ fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> let (tx, rx) = mpsc::channel(); let subclass_input = ThreadMsgTargetSubclassInput { - event_loop_runner, + shared_data, user_event_receiver: rx, + active_device_ids: HashMap::default(), }; let input_ptr = Box::into_raw(Box::new(subclass_input)); let subclass_result = commctrl::SetWindowSubclass( @@ -730,6 +745,9 @@ fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> ); assert_eq!(subclass_result, 1); + // Set up raw input + raw_input::register_for_raw_input(window); + (window, tx) } } @@ -782,14 +800,14 @@ unsafe extern "system" fn public_window_callback( match msg { winuser::WM_ENTERSIZEMOVE => { - let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + let mut runner = subclass_input.shared_data.runner_shared.runner.borrow_mut(); if let Some(ref mut runner) = *runner { runner.in_modal_loop = true; } 0 }, winuser::WM_EXITSIZEMOVE => { - let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + let mut runner = subclass_input.shared_data.runner_shared.runner.borrow_mut(); if let Some(ref mut runner) = *runner { runner.in_modal_loop = false; } @@ -824,7 +842,7 @@ unsafe extern "system" fn public_window_callback( _ if msg == *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID => { use event::WindowEvent::RedrawRequested; - let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + let mut runner = subclass_input.shared_data.runner_shared.runner.borrow_mut(); if let Some(ref mut runner) = *runner { match runner.runner_state { RunnerState::Idle(..) | @@ -846,7 +864,7 @@ unsafe extern "system" fn public_window_callback( let mut send_event = false; { - let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + let mut runner = subclass_input.shared_data.runner_shared.runner.borrow_mut(); if let Some(ref mut runner) = *runner { match runner.runner_state { RunnerState::Idle(..) | @@ -939,7 +957,7 @@ unsafe extern "system" fn public_window_callback( if mouse_was_outside_window { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: CursorEntered { device_id: DEVICE_ID }, + event: CursorEntered, }); // Calling TrackMouseEvent in order to receive mouse leave events. @@ -958,7 +976,7 @@ unsafe extern "system" fn public_window_callback( subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, + event: CursorMoved { position, modifiers: event::get_key_mods() }, }); 0 @@ -973,7 +991,7 @@ unsafe extern "system" fn public_window_callback( subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: CursorLeft { device_id: DEVICE_ID }, + event: CursorLeft, }); 0 @@ -989,7 +1007,7 @@ unsafe extern "system" fn public_window_callback( subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() }, + event: WindowEvent::MouseWheel { delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() }, }); 0 @@ -1004,15 +1022,12 @@ unsafe extern "system" fn public_window_callback( if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Pressed, - scancode: scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - }, - }, + event: WindowEvent::KeyboardInput(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. @@ -1032,15 +1047,12 @@ unsafe extern "system" fn public_window_callback( if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Released, - scancode: scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - }, - }, + event: WindowEvent::KeyboardInput(KeyboardInput { + state: Released, + scancode: scancode, + virtual_keycode: vkey, + modifiers: event::get_key_mods(), + }), }); } 0 @@ -1048,91 +1060,84 @@ unsafe extern "system" fn public_window_callback( winuser::WM_LBUTTONDOWN => { use event::WindowEvent::MouseInput; - use event::MouseButton::Left; use event::ElementState::Pressed; capture_mouse(window, &mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() }, + event: MouseInput { state: Pressed, button: MouseButton::Left, modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_LBUTTONUP => { use event::WindowEvent::MouseInput; - use event::MouseButton::Left; use event::ElementState::Released; release_mouse(&mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() }, + event: MouseInput { state: Released, button: MouseButton::Left, modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_RBUTTONDOWN => { use event::WindowEvent::MouseInput; - use event::MouseButton::Right; use event::ElementState::Pressed; capture_mouse(window, &mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() }, + event: MouseInput { state: Pressed, button: MouseButton::Right, modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_RBUTTONUP => { use event::WindowEvent::MouseInput; - use event::MouseButton::Right; use event::ElementState::Released; release_mouse(&mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() }, + event: MouseInput { state: Released, button: MouseButton::Right, modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_MBUTTONDOWN => { use event::WindowEvent::MouseInput; - use event::MouseButton::Middle; use event::ElementState::Pressed; capture_mouse(window, &mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() }, + event: MouseInput { state: Pressed, button: MouseButton::Middle, modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_MBUTTONUP => { use event::WindowEvent::MouseInput; - use event::MouseButton::Middle; use event::ElementState::Released; release_mouse(&mut *subclass_input.window_state.lock()); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() }, + event: MouseInput { state: Released, button: MouseButton::Middle, modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_XBUTTONDOWN => { use event::WindowEvent::MouseInput; - use event::MouseButton::Other; use event::ElementState::Pressed; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); @@ -1140,14 +1145,13 @@ unsafe extern "system" fn public_window_callback( subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8), modifiers: event::get_key_mods() }, + event: MouseInput { state: Pressed, button: MouseButton::Other(xbutton as u8), modifiers: event::get_key_mods() }, }); 0 }, winuser::WM_XBUTTONUP => { use event::WindowEvent::MouseInput; - use event::MouseButton::Other; use event::ElementState::Released; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); @@ -1155,188 +1159,11 @@ unsafe extern "system" fn public_window_callback( subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8), modifiers: event::get_key_mods() }, - }); - 0 - }, - - winuser::WM_INPUT_DEVICE_CHANGE => { - let event = { - let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - match wparam as _ { - winuser::GIDC_ARRIVAL => { - gamepad_mutex.get_or_add(lparam as _); - DeviceEvent::Added - }, - winuser::GIDC_REMOVAL => { - gamepad_mutex.remove(lparam as _); - DeviceEvent::Removed - }, - _ => unreachable!(), - } - }; - - subclass_input.send_event(Event::DeviceEvent { - device_id: wrap_device_id(lparam as _), - event, + event: MouseInput { state: Released, button: MouseButton::Other(xbutton as u8), modifiers: event::get_key_mods() }, }); - 0 }, - winuser::WM_INPUT => { - use event::DeviceEvent::{Motion, MouseMotion, MouseWheel, Button, Key}; - use event::MouseScrollDelta::LineDelta; - use event::ElementState::{Pressed, Released}; - - if let Some(mut data) = get_raw_input_data(lparam as _) { - let input = &mut *(data.as_mut_ptr() as *mut winuser::RAWINPUT); - - let device_id = wrap_device_id(input.header.hDevice as _); - - if input.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = input.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 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { - axis: 0, - hint: Some(AxisHint::MouseX), - value: x, - }, - }); - } - - if y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { - axis: 1, - hint: Some(AxisHint::MouseY), - value: y, - }, - }); - } - - if x != 0.0 || y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseMotion { delta: (x, y) }, - }); - } - } - - if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseWheel { delta: LineDelta(0.0, delta as f32) }, - }); - } - - 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 _; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Button { - button, - hint: match button { - 1 => Some(ButtonHint::LeftMouse), - 2 => Some(ButtonHint::MiddleMouse), - 3 => Some(ButtonHint::RightMouse), - _ => None, - }, - state, - }, - }); - } - } - } else if input.header.dwType == winuser::RIM_TYPEKEYBOARD { - let keyboard = input.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 _) - | 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); - - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Key(KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers: event::get_key_mods(), - }), - }); - } - } - } else if input.header.dwType == winuser::RIM_TYPEHID { - let handle = input.header.hDevice; - let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - gamepad_mutex - .get_or_add(handle) - .and_then(|gamepad| gamepad - .update_state(input) - .map(|_| gamepad)) - .map(|gamepad| { - for (button, hint, state) in gamepad.get_changed_buttons() { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Button { - button, - hint, - state, - }, - }); - } - - for (axis, hint, value) in gamepad.get_changed_axes() { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { - axis, - hint, - value, - }, - }); - } - }); - } else { - unreachable!(); - } - } - - commctrl::DefSubclassProc(window, msg, wparam, lparam) - }, - winuser::WM_TOUCH => { let pcount = LOWORD( wparam as DWORD ) as usize; let mut inputs = Vec::with_capacity( pcount ); @@ -1368,7 +1195,6 @@ unsafe extern "system" fn public_window_callback( }, location, id: input.dwID as u64, - device_id: DEVICE_ID, }), }); } @@ -1391,7 +1217,7 @@ unsafe extern "system" fn public_window_callback( subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, + event: CursorMoved { position, modifiers: event::get_key_mods() }, }); 0 @@ -1584,7 +1410,7 @@ unsafe extern "system" fn thread_event_target_callback( ); }; let in_modal_loop = { - let runner = subclass_input.event_loop_runner.runner.borrow_mut(); + let runner = subclass_input.shared_data.runner_shared.runner.borrow_mut(); if let Some(ref runner) = *runner { runner.in_modal_loop } else { @@ -1619,7 +1445,7 @@ unsafe extern "system" fn thread_event_target_callback( } } - let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + let mut runner = subclass_input.shared_data.runner_shared.runner.borrow_mut(); if let Some(ref mut runner) = *runner { runner.events_cleared(); match runner.control_flow { @@ -1640,6 +1466,234 @@ unsafe extern "system" fn thread_event_target_callback( } 0 } + winuser::WM_INPUT_DEVICE_CHANGE => { + use super::raw_input::RawDeviceInfo; + + let handle = lparam as HANDLE; + + match wparam as _ { + winuser::GIDC_ARRIVAL => { + if let Some(handle_info) = raw_input::get_raw_input_device_info(handle) { + let event: Option; + + match handle_info { + RawDeviceInfo::Mouse(_) => { + let mouse_id = MouseId(handle); + subclass_input.active_device_ids.insert(handle, DeviceId::Mouse(mouse_id)); + event = Some(DeviceEvent::MouseEvent( + mouse_id.into(), + MouseEvent::Added, + )); + }, + RawDeviceInfo::Keyboard(_) => { + let keyboard_id = KeyboardId(handle); + subclass_input.active_device_ids.insert(handle, DeviceId::Keyboard(keyboard_id)); + event = Some(DeviceEvent::KeyboardEvent( + keyboard_id.into(), + KeyboardEvent::Added, + )); + }, + RawDeviceInfo::Hid(_) => { + let mut gamepad_mutex = GAMEPADS.lock().unwrap(); + event = if gamepad_mutex.get_or_add(handle).is_some() { + let gamepad_handle = GamepadHandle(handle); + subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle)); + + Some(DeviceEvent::GamepadEvent( + gamepad_handle.into(), + GamepadEvent::Added, + )) + } else { + None + }; + } + } + + if let Some(event) = event { + subclass_input.send_event(Event::DeviceEvent(event)); + } + } + }, + winuser::GIDC_REMOVAL => { + if let Some(device_id) = subclass_input.active_device_ids.remove(&handle) { + let event = match device_id { + DeviceId::Mouse(mouse_id) => DeviceEvent::MouseEvent( + mouse_id.into(), + MouseEvent::Removed, + ), + DeviceId::Keyboard(keyboard_id) => DeviceEvent::KeyboardEvent( + keyboard_id.into(), + KeyboardEvent::Removed, + ), + DeviceId::Gamepad(gamepad_handle) => { + let mut gamepad_mutex = GAMEPADS.lock().unwrap(); + gamepad_mutex.remove(handle); + DeviceEvent::GamepadEvent( + gamepad_handle.into(), + GamepadEvent::Removed + ) + } + }; + subclass_input.send_event(Event::DeviceEvent(event)); + } + }, + _ => unreachable!() + } + + 0 + }, + + winuser::WM_INPUT => { + use event::device::DeviceEvent; + use event::ElementState::{Pressed, Released}; + + if let Some(mut input) = get_raw_input_data(lparam as _) { + let device_handle = input.header.hDevice; + + if input.header.dwType == winuser::RIM_TYPEMOUSE { + let mouse = input.data.mouse(); + let mouse_handle = MouseId(device_handle).into(); + + 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 || y != 0.0 { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::MouseEvent( + mouse_handle, + MouseEvent::Moved(x, y), + ) + )); + } + } + + if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + // TODO: HOW IS RAW WHEEL DELTA HANDLED ON OTHER PLATFORMS? + let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::MouseEvent( + mouse_handle, + MouseEvent::Wheel(0.0, delta as f64), + ) + )); + } + // Check if there's horizontal wheel movement. + if util::has_flag(mouse.usButtonFlags, 0x0800) { + // TODO: HOW IS RAW WHEEL DELTA HANDLED ON OTHER PLATFORMS? + let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::MouseEvent( + mouse_handle, + MouseEvent::Wheel(delta as f64, 0.0), + ) + )); + } + + let button_state = get_raw_mouse_button_state(mouse.usButtonFlags); + for (index, state) in button_state.iter().cloned().enumerate().filter_map(|(i, state)| state.map(|s| (i, s))) { + // 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_id = (index + 1) as u32; + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::MouseEvent( + mouse_handle, + MouseEvent::Button { + state, + button_id, + button: match button_id { + 1 => MouseButton::Left, + 2 => MouseButton::Middle, + 3 => MouseButton::Right, + _ => MouseButton::Other(dbg!(button_id) as u8 - 3), + }, + }, + ) + )); + } + } else if input.header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard = input.data.keyboard(); + let keyboard_id = KeyboardId(device_handle).into(); + + 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 _) + | 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); + + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::KeyboardEvent( + keyboard_id, + KeyboardEvent::Input(KeyboardInput { + scancode, + state, + virtual_keycode, + modifiers: event::get_key_mods(), + }), + ) + )); + } + } + } else if input.header.dwType == winuser::RIM_TYPEHID { + let gamepad_handle = GamepadHandle(device_handle).into(); + let mut gamepad_mutex = GAMEPADS.lock().unwrap(); + + gamepad_mutex + .get_or_add(device_handle) + .and_then(|gamepad| gamepad + .update_state(&mut input) + .map(|_| gamepad)) + .map(|gamepad| { + for (button_id, hint, state) in gamepad.get_changed_buttons() { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle, + GamepadEvent::Button { + button_id, + hint, + state, + }, + ) + )); + } + + for (axis, hint, value) in gamepad.get_changed_axes() { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle, + GamepadEvent::Axis { + axis, + hint, + value, + }, + ) + )); + } + }); + } else { + unreachable!(); + } + } + + commctrl::DefSubclassProc(window, msg, wparam, lparam) + }, _ if msg == *USER_EVENT_MSG_ID => { if let Ok(event) = subclass_input.user_event_receiver.recv() { subclass_input.send_event(Event::UserEvent(event)); diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index c40d388c4e..95dad66563 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -4,7 +4,10 @@ use std::sync::{Arc, Mutex}; use winapi::um::winnt::HANDLE; use winapi::um::winuser::RAWINPUT; -use event::{AxisHint, ButtonHint, ElementState}; +use event::{ + ElementState, + device::{AxisHint, ButtonHint}, +}; use platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}; use platform_impl::platform::xinput::{self, XInputGamepad}; @@ -71,7 +74,7 @@ impl Gamepad { }) } - pub unsafe fn update_state(&mut self, input: *mut RAWINPUT) -> Option<()> { + pub unsafe fn update_state(&mut self, input: &mut RAWINPUT) -> Option<()> { match self.backend { GamepadType::Raw(ref mut gamepad) => gamepad.update_state(input), GamepadType::XInput(ref mut gamepad) => gamepad.update_state(), diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index de40778352..06a315dee8 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -1,15 +1,28 @@ #![cfg(target_os = "windows")] +mod dpi; +mod drop_handler; +mod event; +mod event_loop; +mod gamepad; +mod icon; +mod monitor; +mod raw_input; +mod util; +mod window; +mod window_state; +mod xinput; + +use std::ptr; use winapi; use winapi::shared::windef::HWND; +use winapi::um::winnt::HANDLE; +use window::Icon; pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy}; pub use self::monitor::MonitorHandle; pub use self::window::Window; -use window::Icon; -use event::DeviceId as RootDeviceId; - #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { pub parent: Option, @@ -27,53 +40,39 @@ unsafe impl Send for Cursor {} unsafe impl Sync for Cursor {} #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(u32); +pub struct WindowId(HWND); +unsafe impl Send for WindowId {} +unsafe impl Sync for WindowId {} -impl DeviceId { +impl WindowId { pub unsafe fn dummy() -> Self { - DeviceId(0) - } -} - -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 - } + WindowId(ptr::null_mut()) } } -// Constant device ID, to be removed when this backend is updated to report real device IDs. -const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0)); - -fn wrap_device_id(id: u32) -> RootDeviceId { - RootDeviceId(DeviceId(id)) -} +macro_rules! device_id { + ($name:ident) => { + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub(crate) struct $name(HANDLE); -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(HWND); -unsafe impl Send for WindowId {} -unsafe impl Sync for WindowId {} + impl $name { + pub unsafe fn dummy() -> Self { + Self(ptr::null_mut()) + } -impl WindowId { - pub unsafe fn dummy() -> Self { - use std::ptr::null_mut; + pub fn get_persistent_identifier(&self) -> Option { + raw_input::get_raw_input_device_name(self.0) + } + } - WindowId(null_mut()) + impl From<$name> for crate::event::device::$name { + fn from(platform_id: $name) -> Self { + Self(platform_id) + } + } } } -mod dpi; -mod drop_handler; -mod event; -mod event_loop; -mod gamepad; -mod icon; -mod monitor; -mod raw_input; -mod util; -mod window; -mod window_state; -mod xinput; +device_id!(MouseId); +device_id!(KeyboardId); +device_id!(GamepadHandle); diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index d60558ccde..b3e7d31df7 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -3,7 +3,7 @@ use std::cmp::max; use std::mem::{self, size_of}; use winapi::ctypes::wchar_t; -use winapi::shared::minwindef::{BYTE, TRUE, UINT, USHORT}; +use winapi::shared::minwindef::{TRUE, INT, UINT, USHORT}; use winapi::shared::hidpi::{ HidP_GetButtonCaps, HidP_GetCaps, @@ -54,7 +54,10 @@ use winapi::um::winuser::{ }; use platform_impl::platform::util; -use event::{AxisHint, ButtonHint, ElementState}; +use event::{ + ElementState, + device::{AxisHint, ButtonHint}, +}; #[allow(dead_code)] pub fn get_raw_input_device_list() -> Option> { @@ -90,7 +93,6 @@ pub fn get_raw_input_device_list() -> Option> { Some(buffer) } -#[allow(dead_code)] pub enum RawDeviceInfo { Mouse(RID_DEVICE_INFO_MOUSE), Keyboard(RID_DEVICE_INFO_KEYBOARD), @@ -110,26 +112,29 @@ impl From for RawDeviceInfo { } } -#[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 mut data_size = info_size; let status = unsafe { winuser::GetRawInputDeviceInfoW( handle, RIDI_DEVICEINFO, &mut info as *mut _ as _, - &mut minimum_size, - ) }; + &mut data_size, + ) } as INT; - if status == UINT::max_value() || status == 0 { + // If status == -1, that means we didn't allocate enough space to store all the data. That should + // never happen since we're supposed to get a struct and not a dynamically sized buffer. + assert_ne!(status, -1); + + if status == 0 { return None; } - debug_assert_eq!(info_size, status); + debug_assert_eq!(info_size, status as _); Some(info.into()) } @@ -253,10 +258,14 @@ pub fn register_for_raw_input(window_handle: HWND) -> bool { register_raw_input_devices(&devices) } -pub fn get_raw_input_data(handle: HRAWINPUT) -> Option> { +pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { let mut data_size = 0; let header_size = size_of::() as UINT; + // I've got absolutely no idea why Microsoft decided to make this API so damn complicated. It + // looks like it will only ever read out `size_of::()` bytes at max, so why not just + // make it write directly into a RAWINPUT struct? Several other APIs do just that. It's + // perplexing. unsafe { winuser::GetRawInputData( handle, RID_INPUT, @@ -270,22 +279,27 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option> { data_size += 8 - alignment_remainder; } - let mut data = Vec::with_capacity(data_size as _); + assert!(data_size <= size_of::() as UINT); + + // Since GetRawInputData is going to write... well, a buffer that's `RAWINPUT` bytes long + // and structured like `RAWINPUT`, we're just going to cut to the chase and write directly into + // a `RAWINPUT` struct. + let mut data: RAWINPUT = unsafe{ mem::uninitialized() }; let status = unsafe { winuser::GetRawInputData( handle, RID_INPUT, - data.as_mut_ptr() as _, + &mut data as *mut RAWINPUT as *mut _, &mut data_size, header_size, - ) }; + ) } as INT; - if status == UINT::max_value() || status == 0 { + assert_ne!(-1, status); + + if status == 0 { return None; } - unsafe { data.set_len(data_size as _) }; - Some(data) } @@ -302,7 +316,7 @@ fn button_flags_to_element_state(button_flags: USHORT, down_flag: USHORT, up_fla } } -pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option; 3] { +pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option; 5] { [ button_flags_to_element_state( button_flags, @@ -319,6 +333,16 @@ pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option winuser::RI_MOUSE_RIGHT_BUTTON_DOWN, winuser::RI_MOUSE_RIGHT_BUTTON_UP, ), + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_BUTTON_4_DOWN, + winuser::RI_MOUSE_BUTTON_4_UP, + ), + button_flags_to_element_state( + button_flags, + winuser::RI_MOUSE_BUTTON_5_DOWN, + winuser::RI_MOUSE_BUTTON_5_UP, + ), ] } @@ -522,11 +546,11 @@ impl RawGamepad { Some(()) } - pub unsafe fn update_state(&mut self, input: *mut RAWINPUT) -> Option<()> { - if (*input).header.dwType != winuser::RIM_TYPEHID { + pub unsafe fn update_state(&mut self, input: &mut RAWINPUT) -> Option<()> { + if input.header.dwType != winuser::RIM_TYPEHID { return None; } - let hid = (*input).data.hid_mut(); + let hid = input.data.hid_mut(); self.update_button_state(hid)?; self.update_axis_state(hid)?; Some(()) diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 8bf0e0958e..94675e2384 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -1,5 +1,4 @@ use std::{self, mem, ptr, slice, io}; -use std::ops::BitAnd; use std::sync::atomic::{AtomicBool, Ordering}; use window::MouseCursor; @@ -25,13 +24,6 @@ use winapi::um::winuser; pub use util::*; -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).to_string() } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 1f8d1d5ce9..2ef482173c 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -28,7 +28,6 @@ use platform_impl::platform::{ event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID, REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID}, icon::{self, IconType, WinIcon}, monitor, - raw_input::register_for_raw_input, util, window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, }; @@ -69,10 +68,10 @@ impl Window { panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`"); } - let file_drop_runner = event_loop.runner_shared.clone(); + let shared_data = event_loop.shared_data.clone(); let file_drop_handler = FileDropHandler::new( win.window.0, - Box::new(move |event| if let Ok(e) = event.map_nonuser_event() {file_drop_runner.send_event(e)}) + Box::new(move |event| if let Ok(e) = event.map_nonuser_event() {shared_data.runner_shared.send_event(e)}) ); let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET; @@ -82,7 +81,7 @@ impl Window { let subclass_input = event_loop::SubclassInput { window_state: win.window_state.clone(), - event_loop_runner: event_loop.runner_shared.clone(), + shared_data: event_loop.shared_data.clone(), file_drop_handler, }; @@ -674,9 +673,6 @@ unsafe fn init( WindowWrapper(handle) }; - // Set up raw input - register_for_raw_input(real_window.0); - // Register for touch events if applicable { let digitizer = winuser::GetSystemMetrics( winuser::SM_DIGITIZER ) as u32; diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 77c7752a0b..43efb6bac4 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -5,7 +5,10 @@ use rusty_xinput::*; use winapi::shared::minwindef::{DWORD, WORD}; use winapi::um::xinput::*; -use event::{AxisHint, ButtonHint, ElementState}; +use event::{ + ElementState, + device::{AxisHint, ButtonHint}, +}; use platform_impl::platform::util; lazy_static! { From 8d2826c622f7f36b14d2b4760c4488609015d544 Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 24 Feb 2019 13:48:27 -0500 Subject: [PATCH 10/34] Add MouseEvent documentation and Device ID debug passthrough --- src/event/device.rs | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/event/device.rs b/src/event/device.rs index 216958a31e..6d164567d7 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -1,5 +1,6 @@ use platform_impl; use event::{AxisId, ButtonId, ElementState, KeyboardInput, MouseButton}; +use std::fmt; /// A hint suggesting the type of button that was pressed. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -69,6 +70,12 @@ pub enum AxisHint { DPadRight, } +/// Represents raw hardware events that are not associated with any particular window. +/// +/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person +/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. +/// +/// Note that these events are delivered regardless of input focus. #[derive(Clone, Copy, Debug, PartialEq)] pub enum DeviceEvent { MouseEvent(MouseId, MouseEvent), @@ -78,14 +85,23 @@ pub enum DeviceEvent { #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub enum MouseEvent { + /// A mouse device has been added. Added, + /// A mouse device has been removed. Removed, + /// A mouse button has been pressed or released. Button { state: ElementState, button: MouseButton, button_id: ButtonId, }, + /// Change in physical position of a pointing device. + /// + /// This represents raw, unfiltered physical motion, NOT the position of the mouse. Accordingly, + /// the values provided here are the change in position of the mouse since the previous `Moved` + /// event. Moved(f64, f64), + /// Change in rotation of mouse wheel. Wheel(f64, f64), } @@ -112,11 +128,11 @@ pub enum GamepadEvent { }, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MouseId(pub(crate) platform_impl::MouseId); -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct KeyboardId(pub(crate) platform_impl::KeyboardId); -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct GamepadHandle(pub(crate) platform_impl::GamepadHandle); impl MouseId { @@ -151,3 +167,21 @@ impl GamepadHandle { GamepadHandle(platform_impl::GamepadHandle::dummy()) } } + +impl fmt::Debug for MouseId { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +impl fmt::Debug for KeyboardId { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +impl fmt::Debug for GamepadHandle { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} From abea21014b3cc155b531b7f6d91bde7e331fe8ef Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 28 Feb 2019 22:51:31 -0500 Subject: [PATCH 11/34] Improve type safety on get_raw_input_data --- src/platform_impl/windows/event_loop.rs | 112 +++++++------ src/platform_impl/windows/gamepad.rs | 10 +- src/platform_impl/windows/raw_input.rs | 204 +++++++++++++++++------- 3 files changed, 209 insertions(+), 117 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c653a84b64..2d11eb6782 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -56,7 +56,7 @@ use platform_impl::platform::{ drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, gamepad::GAMEPADS, - raw_input::{self, get_raw_input_data, get_raw_mouse_button_state}, + raw_input::{self, get_raw_input_data, get_raw_mouse_button_state, RawInputData}, util, window::adjust_size, window_state::{CursorFlags, WindowFlags, WindowState}, @@ -1547,16 +1547,13 @@ unsafe extern "system" fn thread_event_target_callback( use event::device::DeviceEvent; use event::ElementState::{Pressed, Released}; - if let Some(mut input) = get_raw_input_data(lparam as _) { - let device_handle = input.header.hDevice; - - if input.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = input.data.mouse(); + match get_raw_input_data(lparam as _) { + Some(RawInputData::Mouse{device_handle, raw_mouse}) => { let mouse_handle = MouseId(device_handle).into(); - if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { - let x = mouse.lLastX as f64; - let y = mouse.lLastY as f64; + if util::has_flag(raw_mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + let x = raw_mouse.lLastX as f64; + let y = raw_mouse.lLastY as f64; if x != 0.0 || y != 0.0 { subclass_input.send_event(Event::DeviceEvent( @@ -1568,9 +1565,9 @@ unsafe extern "system" fn thread_event_target_callback( } } - if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + if util::has_flag(raw_mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { // TODO: HOW IS RAW WHEEL DELTA HANDLED ON OTHER PLATFORMS? - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + let delta = raw_mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; subclass_input.send_event(Event::DeviceEvent( DeviceEvent::MouseEvent( mouse_handle, @@ -1579,9 +1576,9 @@ unsafe extern "system" fn thread_event_target_callback( )); } // Check if there's horizontal wheel movement. - if util::has_flag(mouse.usButtonFlags, 0x0800) { + if util::has_flag(raw_mouse.usButtonFlags, 0x0800) { // TODO: HOW IS RAW WHEEL DELTA HANDLED ON OTHER PLATFORMS? - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + let delta = raw_mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; subclass_input.send_event(Event::DeviceEvent( DeviceEvent::MouseEvent( mouse_handle, @@ -1590,7 +1587,7 @@ unsafe extern "system" fn thread_event_target_callback( )); } - let button_state = get_raw_mouse_button_state(mouse.usButtonFlags); + let button_state = get_raw_mouse_button_state(raw_mouse.usButtonFlags); for (index, state) in button_state.iter().cloned().enumerate().filter_map(|(i, state)| state.map(|s| (i, s))) { // This gives us consistency with X11, since there doesn't // seem to be anything else reasonable to do for a mouse @@ -1612,14 +1609,14 @@ unsafe extern "system" fn thread_event_target_callback( ) )); } - } else if input.header.dwType == winuser::RIM_TYPEKEYBOARD { - let keyboard = input.data.keyboard(); + }, + Some(RawInputData::Keyboard{device_handle, raw_keyboard}) => { let keyboard_id = KeyboardId(device_handle).into(); - let pressed = keyboard.Message == winuser::WM_KEYDOWN - || keyboard.Message == winuser::WM_SYSKEYDOWN; - let released = keyboard.Message == winuser::WM_KEYUP - || keyboard.Message == winuser::WM_SYSKEYUP; + let pressed = raw_keyboard.Message == winuser::WM_KEYDOWN + || raw_keyboard.Message == winuser::WM_SYSKEYDOWN; + let released = raw_keyboard.Message == winuser::WM_KEYUP + || raw_keyboard.Message == winuser::WM_SYSKEYUP; if pressed || released { let state = if pressed { @@ -1628,11 +1625,11 @@ unsafe extern "system" fn thread_event_target_callback( Released }; - let scancode = keyboard.MakeCode as _; - let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) - | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _); + let scancode = raw_keyboard.MakeCode as _; + let extended = util::has_flag(raw_keyboard.Flags, winuser::RI_KEY_E0 as _) + | util::has_flag(raw_keyboard.Flags, winuser::RI_KEY_E1 as _); if let Some((vkey, scancode)) = handle_extended_keys( - keyboard.VKey as _, + raw_keyboard.VKey as _, scancode, extended, ) { @@ -1651,45 +1648,46 @@ unsafe extern "system" fn thread_event_target_callback( )); } } - } else if input.header.dwType == winuser::RIM_TYPEHID { + }, + Some(RawInputData::Hid{device_handle, mut raw_hid}) => { let gamepad_handle = GamepadHandle(device_handle).into(); let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - gamepad_mutex + let gamepad_updated = gamepad_mutex .get_or_add(device_handle) .and_then(|gamepad| gamepad - .update_state(&mut input) - .map(|_| gamepad)) - .map(|gamepad| { - for (button_id, hint, state) in gamepad.get_changed_buttons() { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( - gamepad_handle, - GamepadEvent::Button { - button_id, - hint, - state, - }, - ) - )); - } + .update_state(&mut raw_hid.raw_input) + .map(|_| gamepad) + ); + if let Some(gamepad) = gamepad_updated { + for (button_id, hint, state) in gamepad.get_changed_buttons() { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle, + GamepadEvent::Button { + button_id, + hint, + state, + }, + ) + )); + } - for (axis, hint, value) in gamepad.get_changed_axes() { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( - gamepad_handle, - GamepadEvent::Axis { - axis, - hint, - value, - }, - ) - )); - } - }); - } else { - unreachable!(); - } + for (axis, hint, value) in gamepad.get_changed_axes() { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle, + GamepadEvent::Axis { + axis, + hint, + value, + }, + ) + )); + } + } + }, + None => () } commctrl::DefSubclassProc(window, msg, wparam, lparam) diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index 95dad66563..0562ad67c4 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use winapi::um::winnt::HANDLE; -use winapi::um::winuser::RAWINPUT; use event::{ ElementState, @@ -66,17 +65,18 @@ impl Gamepad { xinput::id_from_name(&name) .and_then(XInputGamepad::new) .map(GamepadType::XInput) - .or_else(|| RawGamepad::new(handle) - .map(GamepadType::Raw)) + .or_else(|| + RawGamepad::new(handle).map(GamepadType::Raw) + ) .map(|backend| Gamepad { handle, backend, }) } - pub unsafe fn update_state(&mut self, input: &mut RAWINPUT) -> Option<()> { + pub unsafe fn update_state(&mut self, raw_input_report: &mut [u8]) -> Option<()> { match self.backend { - GamepadType::Raw(ref mut gamepad) => gamepad.update_state(input), + GamepadType::Raw(ref mut gamepad) => gamepad.update_state(raw_input_report), GamepadType::XInput(ref mut gamepad) => gamepad.update_state(), } } diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index b3e7d31df7..7ec59b7799 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -1,4 +1,4 @@ -use std::{fmt, ptr}; +use std::{fmt, ptr, slice}; use std::cmp::max; use std::mem::{self, size_of}; @@ -33,7 +33,6 @@ use winapi::um::winnt::{HANDLE, PCHAR}; use winapi::um::winuser::{ self, HRAWINPUT, - RAWHID, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST, @@ -258,14 +257,38 @@ pub fn register_for_raw_input(window_handle: HWND) -> bool { register_raw_input_devices(&devices) } -pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { +pub enum RawInputData { + Mouse { + device_handle: HANDLE, + raw_mouse: winuser::RAWMOUSE, + }, + Keyboard { + device_handle: HANDLE, + raw_keyboard: winuser::RAWKEYBOARD, + }, + Hid { + device_handle: HANDLE, + raw_hid: RawHidData, + }, +} + +pub struct RawHidData { + pub hid_input_size: u32, + pub hid_input_count: u32, + pub raw_input: Box<[u8]>, +} + +pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { let mut data_size = 0; let header_size = size_of::() as UINT; - // I've got absolutely no idea why Microsoft decided to make this API so damn complicated. It - // looks like it will only ever read out `size_of::()` bytes at max, so why not just - // make it write directly into a RAWINPUT struct? Several other APIs do just that. It's - // perplexing. + // There are two classes of data this function can output: + // - Raw mouse and keyboard data + // - Raw HID data + // The first class (mouse and keyboard) is always going to write data formatted like the + // `RAWINPUT` struct, with no other data, and can be placed on the stack into `RAWINPUT`. + // The second class (raw HID data) writes the struct, and then a buffer of data appended to + // the end. That data needs to be heap-allocated so we can store all of it. unsafe { winuser::GetRawInputData( handle, RID_INPUT, @@ -274,27 +297,98 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { header_size, ) }; - let alignment_remainder = data_size % 8; - if alignment_remainder != 0 { - data_size += 8 - alignment_remainder; - } + let (status, data): (INT, RawInputData); - assert!(data_size <= size_of::() as UINT); + if data_size <= size_of::() as UINT { + // Since GetRawInputData is going to write... well, a buffer that's `RAWINPUT` bytes long + // and structured like `RAWINPUT`, we're just going to cut to the chase and write directly into + // a `RAWINPUT` struct. + let mut rawinput_data: RAWINPUT = unsafe{ mem::uninitialized() }; - // Since GetRawInputData is going to write... well, a buffer that's `RAWINPUT` bytes long - // and structured like `RAWINPUT`, we're just going to cut to the chase and write directly into - // a `RAWINPUT` struct. - let mut data: RAWINPUT = unsafe{ mem::uninitialized() }; + status = unsafe { winuser::GetRawInputData( + handle, + RID_INPUT, + &mut rawinput_data as *mut RAWINPUT as *mut _, + &mut data_size, + header_size, + ) } as INT; + + assert_ne!(-1, status); + + let device_handle = rawinput_data.header.hDevice; + + data = match rawinput_data.header.dwType { + winuser::RIM_TYPEMOUSE => { + let raw_mouse = unsafe{ rawinput_data.data.mouse().clone() }; + RawInputData::Mouse{ device_handle, raw_mouse } + }, + winuser::RIM_TYPEKEYBOARD => { + let raw_keyboard = unsafe{ rawinput_data.data.keyboard().clone() }; + RawInputData::Keyboard{ device_handle, raw_keyboard } + }, + winuser::RIM_TYPEHID => { + let hid_data = unsafe{ rawinput_data.data.hid() }; + let buf_len = hid_data.dwSizeHid as usize * hid_data.dwCount as usize; + let data = unsafe{ slice::from_raw_parts(hid_data.bRawData.as_ptr(), buf_len) }; + RawInputData::Hid { + device_handle, + raw_hid: RawHidData { + hid_input_size: hid_data.dwSizeHid, + hid_input_count: hid_data.dwCount, + raw_input: Box::from(data) + }, + } + }, + _ => unreachable!() + }; + } else { + let mut buf = vec![0u8; data_size as usize]; - let status = unsafe { winuser::GetRawInputData( - handle, - RID_INPUT, - &mut data as *mut RAWINPUT as *mut _, - &mut data_size, - header_size, - ) } as INT; + status = unsafe { winuser::GetRawInputData( + handle, + RID_INPUT, + buf.as_mut_ptr() as *mut _, + &mut data_size, + header_size, + ) } as INT; + + let rawinput_data = buf.as_ptr() as *const RAWINPUT; + + let device_handle = unsafe{ (&*rawinput_data).header.hDevice }; + + data = match unsafe{ *rawinput_data }.header.dwType { + winuser::RIM_TYPEMOUSE => { + let raw_mouse = unsafe{ (&*rawinput_data).data.mouse().clone() }; + RawInputData::Mouse{ device_handle, raw_mouse } + }, + winuser::RIM_TYPEKEYBOARD => { + let raw_keyboard = unsafe{ (&*rawinput_data).data.keyboard().clone() }; + RawInputData::Keyboard{ device_handle, raw_keyboard } + }, + winuser::RIM_TYPEHID => { + let hid_data: winuser::RAWHID = unsafe{ (&*rawinput_data).data.hid().clone() }; + + let hid_data_index = { + let hid_data_start = unsafe{ &((&*rawinput_data).data.hid().bRawData) } as *const _; + hid_data_start as usize - buf.as_ptr() as usize + }; + + buf.drain(..hid_data_index); + + RawInputData::Hid { + device_handle, + raw_hid: RawHidData { + hid_input_size: hid_data.dwSizeHid, + hid_input_count: hid_data.dwCount, + raw_input: buf.into_boxed_slice() + }, + } + }, + _ => unreachable!() + }; - assert_ne!(-1, status); + assert_ne!(-1, status); + } if status == 0 { return None; @@ -430,20 +524,24 @@ impl RawGamepad { caps: axis_cap, value: 0.0, prev_value: 0.0, - hint: match unsafe { axis_cap.u.Range().UsageMin } { - 0x30 => Some(AxisHint::LeftStickX), - 0x31 => Some(AxisHint::LeftStickY), - 0x32 => Some(AxisHint::RightStickX), - 0x33 => Some(AxisHint::LeftTrigger), - 0x34 => Some(AxisHint::RightTrigger), - 0x35 => Some(AxisHint::RightStickY), - 0x39 => Some(AxisHint::HatSwitch), - 0x90 => Some(AxisHint::DPadUp), - 0x91 => Some(AxisHint::DPadDown), - 0x92 => Some(AxisHint::DPadRight), - 0x93 => Some(AxisHint::DPadLeft), - _ => None, - }, + hint: None, + // @francesca64 when reviewing - where did you get these values? I've commented them + // out because the HOTAS controller I've been using to test the raw input backend has + // been getting axis hints when it shouldn't, and this seems to be the culprit. + // match unsafe { axis_cap.u.Range().UsageMin } { + // 0x30 => Some(AxisHint::LeftStickX), + // 0x31 => Some(AxisHint::LeftStickY), + // 0x32 => Some(AxisHint::RightStickX), + // 0x33 => Some(AxisHint::LeftTrigger), + // 0x34 => Some(AxisHint::RightTrigger), + // 0x35 => Some(AxisHint::RightStickY), + // 0x39 => Some(AxisHint::HatSwitch), + // 0x90 => Some(AxisHint::DPadUp), + // 0x91 => Some(AxisHint::DPadDown), + // 0x92 => Some(AxisHint::DPadRight), + // 0x93 => Some(AxisHint::DPadLeft), + // _ => None, + // }, }); axis_count = max(axis_count, axis_index + 1); } @@ -462,7 +560,7 @@ impl RawGamepad { self.pre_parsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA } - fn update_button_state(&mut self, hid: &mut RAWHID) -> Option<()> { + fn update_button_state(&mut self, raw_input_report: &mut [u8]) -> Option<()> { let pre_parsed_data_ptr = self.pre_parsed_data_ptr(); self.prev_button_state = mem::replace( &mut self.button_state, @@ -477,8 +575,8 @@ impl RawGamepad { ptr::null_mut(), &mut usages_len, pre_parsed_data_ptr, - hid.bRawData.as_mut_ptr() as PCHAR, - hid.dwSizeHid, + raw_input_report.as_mut_ptr() as PCHAR, + raw_input_report.len() as _, ) }; let mut usages = Vec::with_capacity(usages_len as _); let status = unsafe { HidP_GetUsagesEx( @@ -487,8 +585,8 @@ impl RawGamepad { usages.as_mut_ptr(), &mut usages_len, pre_parsed_data_ptr, - hid.bRawData.as_mut_ptr() as PCHAR, - hid.dwSizeHid, + raw_input_report.as_mut_ptr() as PCHAR, + raw_input_report.len() as _, ) }; if status != HIDP_STATUS_SUCCESS { return None; @@ -503,7 +601,7 @@ impl RawGamepad { Some(()) } - fn update_axis_state(&mut self, hid: &mut RAWHID) -> Option<()> { + fn update_axis_state(&mut self, raw_input_report: &mut [u8]) -> Option<()> { let pre_parsed_data_ptr = self.pre_parsed_data_ptr(); for axis in &mut self.axis_state { let (status, axis_value) = if axis.caps.LogicalMin < 0 { @@ -515,8 +613,8 @@ impl RawGamepad { axis.caps.u.Range().UsageMin, &mut scaled_axis_value, pre_parsed_data_ptr, - hid.bRawData.as_mut_ptr() as PCHAR, - hid.dwSizeHid, + raw_input_report.as_mut_ptr() as PCHAR, + raw_input_report.len() as _, ) }; (status, scaled_axis_value as f64) } else { @@ -528,8 +626,8 @@ impl RawGamepad { axis.caps.u.Range().UsageMin, &mut axis_value, pre_parsed_data_ptr, - hid.bRawData.as_mut_ptr() as PCHAR, - hid.dwSizeHid, + raw_input_report.as_mut_ptr() as PCHAR, + raw_input_report.len() as _, ) }; (status, axis_value as f64) }; @@ -546,13 +644,9 @@ impl RawGamepad { Some(()) } - pub unsafe fn update_state(&mut self, input: &mut RAWINPUT) -> Option<()> { - if input.header.dwType != winuser::RIM_TYPEHID { - return None; - } - let hid = input.data.hid_mut(); - self.update_button_state(hid)?; - self.update_axis_state(hid)?; + pub unsafe fn update_state(&mut self, raw_input_report: &mut [u8]) -> Option<()> { + self.update_button_state(raw_input_report)?; + self.update_axis_state(raw_input_report)?; Some(()) } From eb6d43ebeaa1dca86053c9cd21c9bf4b4c67c4ad Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 28 Feb 2019 22:55:11 -0500 Subject: [PATCH 12/34] Remove button_id field from MouseEvent::Button in favor of utton --- src/event/device.rs | 1 - src/platform_impl/windows/event_loop.rs | 15 +++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/event/device.rs b/src/event/device.rs index 6d164567d7..f26d28c270 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -93,7 +93,6 @@ pub enum MouseEvent { Button { state: ElementState, button: MouseButton, - button_id: ButtonId, }, /// Change in physical position of a pointing device. /// diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 2d11eb6782..8c8abbbb4b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1589,21 +1589,16 @@ unsafe extern "system" fn thread_event_target_callback( let button_state = get_raw_mouse_button_state(raw_mouse.usButtonFlags); for (index, state) in button_state.iter().cloned().enumerate().filter_map(|(i, state)| state.map(|s| (i, s))) { - // 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_id = (index + 1) as u32; subclass_input.send_event(Event::DeviceEvent( DeviceEvent::MouseEvent( mouse_handle, MouseEvent::Button { state, - button_id, - button: match button_id { - 1 => MouseButton::Left, - 2 => MouseButton::Middle, - 3 => MouseButton::Right, - _ => MouseButton::Other(dbg!(button_id) as u8 - 3), + button: match index { + 0 => MouseButton::Left, + 1 => MouseButton::Middle, + 2 => MouseButton::Right, + _ => MouseButton::Other(index as u8 - 2), }, }, ) From 2f4e18a36895f49ad1df0793daf534597aae49eb Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 28 Feb 2019 23:13:15 -0500 Subject: [PATCH 13/34] Remove regex dependency on Windows --- Cargo.toml | 1 - src/lib.rs | 2 -- src/platform_impl/windows/xinput.rs | 22 ++++++++++------------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 776471deeb..0acc7cace3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ core-graphics = "0.17.3" [target.'cfg(target_os = "windows")'.dependencies] backtrace = "0.3" bitflags = "1" -regex = "1.0" rusty-xinput = "1.0" [target.'cfg(target_os = "windows")'.dependencies.winapi] diff --git a/src/lib.rs b/src/lib.rs index 7e15782de6..9c3a8fcbb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,8 +87,6 @@ extern crate image; #[macro_use] extern crate serde; -#[cfg(target_os = "windows")] -extern crate regex; #[cfg(target_os = "windows")] extern crate rusty_xinput; #[cfg(target_os = "windows")] diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 43efb6bac4..77512e8609 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -1,6 +1,5 @@ use std::mem; -use regex::Regex; use rusty_xinput::*; use winapi::shared::minwindef::{DWORD, WORD}; use winapi::um::xinput::*; @@ -13,7 +12,6 @@ use platform_impl::platform::util; lazy_static! { static ref XINPUT_GUARD: Option<()> = dynamic_load_xinput().ok(); - static ref ID_REGEX: Regex = Regex::new(r"(?m)IG_0([0-3])").unwrap(); } static BUTTONS: &[(WORD , u32, ButtonHint)] = &[ @@ -36,16 +34,16 @@ static BUTTONS: &[(WORD , u32, ButtonHint)] = &[ pub fn id_from_name(name: &str) -> Option { // A device name looks like \\?\HID#VID_046D&PID_C21D&IG_00#8&6daf3b6&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} // The IG_00 substring indicates that this is an XInput gamepad, and that the ID is 00 - // So we use this regex to extract the ID, 0 - ID_REGEX - .captures_iter(name) - .next() - .and_then(|id| id - .get(1) - .unwrap() - .as_str() - .parse() - .ok()) + let pat = "IG_0"; + name.find(pat) + .and_then(|i| name[i + pat.len()..].chars().next()) + .and_then(|c| match c { + '0' => Some(0), + '1' => Some(1), + '2' => Some(2), + '3' => Some(3), + _ => None, + }) } #[derive(Debug)] From e76f47eec9af49f40cbab3e6a146f3e0cdc63a90 Mon Sep 17 00:00:00 2001 From: Osspial Date: Fri, 1 Mar 2019 00:11:42 -0500 Subject: [PATCH 14/34] Remove axis filtering in XInput --- src/platform_impl/windows/event_loop.rs | 6 +- src/platform_impl/windows/gamepad.rs | 30 +++++++- src/platform_impl/windows/raw_input.rs | 11 +-- src/platform_impl/windows/xinput.rs | 91 +++++++++++-------------- 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 8c8abbbb4b..b662c20f68 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -55,7 +55,7 @@ use platform_impl::platform::{ dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, get_hwnd_scale_factor}, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, - gamepad::GAMEPADS, + gamepad::{AxisEvent, ButtonEvent, GAMEPADS}, raw_input::{self, get_raw_input_data, get_raw_mouse_button_state, RawInputData}, util, window::adjust_size, @@ -1655,7 +1655,7 @@ unsafe extern "system" fn thread_event_target_callback( .map(|_| gamepad) ); if let Some(gamepad) = gamepad_updated { - for (button_id, hint, state) in gamepad.get_changed_buttons() { + for ButtonEvent{ button_id, hint, state } in gamepad.get_changed_buttons() { subclass_input.send_event(Event::DeviceEvent( DeviceEvent::GamepadEvent( gamepad_handle, @@ -1668,7 +1668,7 @@ unsafe extern "system" fn thread_event_target_callback( )); } - for (axis, hint, value) in gamepad.get_changed_axes() { + for AxisEvent{ axis, hint, value } in gamepad.get_changed_axes() { subclass_input.send_event(Event::DeviceEvent( DeviceEvent::GamepadEvent( gamepad_handle, diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index 0562ad67c4..ce20db9473 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -58,6 +58,20 @@ pub struct Gamepad { backend: GamepadType, } +#[derive(Debug, Clone, Copy)] +pub struct AxisEvent { + pub axis: u32, + pub hint: Option, + pub value: f64, +} + +#[derive(Debug, Clone, Copy)] +pub struct ButtonEvent { + pub button_id: u32, + pub hint: Option, + pub state: ElementState, +} + impl Gamepad { pub fn new(handle: HANDLE) -> Option { // TODO: Verify that this is an HID device @@ -81,14 +95,14 @@ impl Gamepad { } } - pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { + pub fn get_changed_buttons(&self) -> Vec { match self.backend { GamepadType::Raw(ref gamepad) => gamepad.get_changed_buttons(), GamepadType::XInput(ref gamepad) => gamepad.get_changed_buttons(), } } - pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { + pub fn get_changed_axes(&self) -> Vec { match self.backend { GamepadType::Raw(ref gamepad) => gamepad.get_changed_axes(), GamepadType::XInput(ref gamepad) => gamepad.get_changed_axes(), @@ -102,3 +116,15 @@ impl Gamepad { } } } + +impl AxisEvent { + pub fn new(axis: u32, hint: Option, value: f64) -> AxisEvent { + AxisEvent{ axis, hint, value } + } +} + +impl ButtonEvent { + pub fn new(button_id: u32, hint: Option, state: ElementState) -> ButtonEvent { + ButtonEvent{ button_id, hint, state } + } +} diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 7ec59b7799..fa6158ce34 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -52,10 +52,11 @@ use winapi::um::winuser::{ RIM_TYPEHID, }; +use super::gamepad::{AxisEvent, ButtonEvent}; use platform_impl::platform::util; use event::{ ElementState, - device::{AxisHint, ButtonHint}, + device::AxisHint, }; #[allow(dead_code)] @@ -650,7 +651,7 @@ impl RawGamepad { Some(()) } - pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { + pub fn get_changed_buttons(&self) -> Vec { self.button_state .iter() .zip(self.prev_button_state.iter()) @@ -658,17 +659,17 @@ impl RawGamepad { .filter(|&(_, (button, prev_button))| button != prev_button) .map(|(index, (button, _))| { let state = if *button { ElementState::Pressed } else { ElementState::Released }; - (index as _, None, state) + ButtonEvent::new(index as _, None, state) }) .collect() } - pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { + pub fn get_changed_axes(&self) -> Vec { self.axis_state .iter() .enumerate() .filter(|&(_, axis)| axis.value != axis.prev_value) - .map(|(index, axis)| (index as _, axis.hint, axis.value)) + .map(|(index, axis)| AxisEvent::new(index as _, axis.hint, axis.value)) .collect() } diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 77512e8609..4ad8593e12 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -8,6 +8,7 @@ use event::{ ElementState, device::{AxisHint, ButtonHint}, }; +use super::gamepad::{AxisEvent, ButtonEvent}; use platform_impl::platform::util; lazy_static! { @@ -79,7 +80,7 @@ impl XInputGamepad { } fn check_trigger_digital( - events: &mut Vec<(u32, Option, ElementState)>, + events: &mut Vec, value: bool, prev_value: Option, side: Side, @@ -92,11 +93,11 @@ impl XInputGamepad { Side::Left => (LEFT_TRIGGER_ID, ButtonHint::LeftTrigger), Side::Right => (RIGHT_TRIGGER_ID, ButtonHint::RightTrigger), }; - events.push((id, Some(hint), state)); + events.push(ButtonEvent::new(id, Some(hint), state)); } } - pub fn get_changed_buttons(&self) -> Vec<(u32, Option, ElementState)> { + pub fn get_changed_buttons(&self) -> Vec { let (buttons, left_trigger, right_trigger) = match self.state.as_ref() { Some(state) => ( state.raw.Gamepad.wButtons, @@ -131,9 +132,9 @@ impl XInputGamepad { let mut events = Vec::with_capacity(BUTTONS.len() + 2); for &(flag, id, hint) in BUTTONS { if util::has_flag(pressed, flag) { - events.push((id, Some(hint), ElementState::Pressed)); + events.push(ButtonEvent::new(id, Some(hint), ElementState::Pressed)); } else if util::has_flag(released, flag) { - events.push((id, Some(hint), ElementState::Released)); + events.push(ButtonEvent::new(id, Some(hint), ElementState::Released)); } } Self::check_trigger_digital(&mut events, left_trigger, prev_left, Side::Left); @@ -142,7 +143,7 @@ impl XInputGamepad { } fn check_trigger( - events: &mut Vec<(u32, Option, f64)>, + events: &mut Vec, value: u8, prev_value: Option, side: Side, @@ -154,33 +155,32 @@ impl XInputGamepad { Side::Left => (LEFT_TRIGGER_ID, AxisHint::LeftTrigger), Side::Right => (RIGHT_TRIGGER_ID, AxisHint::RightTrigger), }; - events.push((id, Some(hint), value as f64 / u8::max_value() as f64)); + events.push(AxisEvent::new(id, Some(hint), value as f64 / u8::max_value() as f64)); } } fn check_axis( - events: &mut Vec<(u32, Option, f64)>, - value: f32, - prev_value: Option, + events: &mut Vec, + value: i16, + prev_value: Option, id: u32, hint: AxisHint, - flip: bool, ) { if Some(value) != prev_value { - let value = f64::from(if flip && value != 0.0 { - // Flip the Y axis so the top left is (-1, -1) - -value - } else { - value - }); - events.push((id, Some(hint), f64::from(value))); + let value = match value.signum() { + 0 => 0.0, + 1 => value as f64 / i16::max_value() as f64, + -1 => value as f64 / (i16::min_value() as f64).abs(), + _ => unreachable!() + }; + events.push(AxisEvent::new(id, Some(hint), f64::from(value))); } } fn check_stick( - events: &mut Vec<(u32, Option, f64)>, - value: (f32, f32), - prev_value: Option<(f32, f32)>, + events: &mut Vec, + value: (i16, i16), + prev_value: Option<(i16, i16)>, stick: Side, ) { let (id, hint) = match stick { @@ -189,40 +189,28 @@ impl XInputGamepad { }; let prev_x = prev_value.map(|prev| prev.0); let prev_y = prev_value.map(|prev| prev.1); - Self::check_axis(events, value.0, prev_x, id.0, hint.0, false); - Self::check_axis(events, value.1, prev_y, id.1, hint.1, true); + Self::check_axis(events, value.0, prev_x, id.0, hint.0); + Self::check_axis(events, value.1, prev_y, id.1, hint.1); } - pub fn get_changed_axes(&self) -> Vec<(u32, Option, f64)> { - let ( - left_stick, - right_stick, - left_trigger, - right_trigger, - ) = match self.state { - Some(ref state) => ( - state.left_stick_normalized(), - state.right_stick_normalized(), - state.left_trigger(), - state.right_trigger(), - ), - None => return Vec::with_capacity(0), + pub fn get_changed_axes(&self) -> Vec { + let state = match self.state { + Some(ref state) => state, + None => return Vec::new(), }; + let left_stick = state.left_stick_raw(); + let right_stick = state.right_stick_raw(); + let left_trigger = state.left_trigger(); + let right_trigger = state.right_trigger(); + let mut events = Vec::with_capacity(6); - let ( - prev_left_stick, - prev_right_stick, - prev_left_trigger, - prev_right_trigger, - ) = self.prev_state - .as_ref() - .map(|state| ( - Some(state.left_stick_normalized()), - Some(state.right_stick_normalized()), - Some(state.left_trigger()), - Some(state.right_trigger()), - )) - .unwrap_or_else(|| (None, None, None, None)); + + let prev_state = self.prev_state.as_ref(); + let prev_left_stick = prev_state.map(|state| state.left_stick_raw()); + let prev_right_stick = prev_state.map(|state| state.right_stick_raw()); + let prev_left_trigger = prev_state.map(|state| state.left_trigger()); + let prev_right_trigger = prev_state.map(|state| state.right_trigger()); + Self::check_stick(&mut events, left_stick, prev_left_stick, Side::Left); Self::check_stick(&mut events, right_stick, prev_right_stick, Side::Right); Self::check_trigger(&mut events, left_trigger, prev_left_trigger, Side::Left); @@ -235,4 +223,3 @@ impl XInputGamepad { let _ = xinput_set_state(self.id, left_speed, right_speed); } } - From 7775524bff148c37af852474da97e7d52179587e Mon Sep 17 00:00:00 2001 From: Osspial Date: Sat, 2 Mar 2019 18:28:34 -0500 Subject: [PATCH 15/34] Make gamepads not use lazy_static --- src/platform_impl/windows/event_loop.rs | 88 +++++++++++-------------- src/platform_impl/windows/gamepad.rs | 39 ----------- 2 files changed, 40 insertions(+), 87 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b662c20f68..3f1c07ea62 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -55,7 +55,7 @@ use platform_impl::platform::{ dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, get_hwnd_scale_factor}, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, - gamepad::{AxisEvent, ButtonEvent, GAMEPADS}, + gamepad::{AxisEvent, ButtonEvent, Gamepad}, raw_input::{self, get_raw_input_data, get_raw_mouse_button_state, RawInputData}, util, window::adjust_size, @@ -80,11 +80,11 @@ struct ThreadMsgTargetSubclassInput { active_device_ids: HashMap, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] enum DeviceId { Mouse(MouseId), Keyboard(KeyboardId), - Gamepad(GamepadHandle), + Gamepad(GamepadHandle, Gamepad), } impl ThreadMsgTargetSubclassInput { @@ -1494,18 +1494,15 @@ unsafe extern "system" fn thread_event_target_callback( )); }, RawDeviceInfo::Hid(_) => { - let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - event = if gamepad_mutex.get_or_add(handle).is_some() { + event = Gamepad::new(handle).map(|gamepad| { let gamepad_handle = GamepadHandle(handle); - subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle)); + subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle, gamepad)); - Some(DeviceEvent::GamepadEvent( + DeviceEvent::GamepadEvent( gamepad_handle.into(), GamepadEvent::Added, - )) - } else { - None - }; + ) + }); } } @@ -1525,14 +1522,12 @@ unsafe extern "system" fn thread_event_target_callback( keyboard_id.into(), KeyboardEvent::Removed, ), - DeviceId::Gamepad(gamepad_handle) => { - let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - gamepad_mutex.remove(handle); + DeviceId::Gamepad(gamepad_handle, _) => { DeviceEvent::GamepadEvent( gamepad_handle.into(), GamepadEvent::Removed ) - } + }, }; subclass_input.send_event(Event::DeviceEvent(event)); } @@ -1645,41 +1640,38 @@ unsafe extern "system" fn thread_event_target_callback( } }, Some(RawInputData::Hid{device_handle, mut raw_hid}) => { + let (mut changed_buttons, mut changed_axes) = (vec![], vec![]); + if let Some(DeviceId::Gamepad(_, ref mut gamepad)) = subclass_input.active_device_ids.get_mut(&device_handle) { + gamepad.update_state(&mut raw_hid.raw_input); + changed_buttons = gamepad.get_changed_buttons(); + changed_axes = gamepad.get_changed_axes(); + } + let gamepad_handle = GamepadHandle(device_handle).into(); - let mut gamepad_mutex = GAMEPADS.lock().unwrap(); - - let gamepad_updated = gamepad_mutex - .get_or_add(device_handle) - .and_then(|gamepad| gamepad - .update_state(&mut raw_hid.raw_input) - .map(|_| gamepad) - ); - if let Some(gamepad) = gamepad_updated { - for ButtonEvent{ button_id, hint, state } in gamepad.get_changed_buttons() { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( - gamepad_handle, - GamepadEvent::Button { - button_id, - hint, - state, - }, - ) - )); - } + for ButtonEvent{ button_id, hint, state } in changed_buttons { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle, + GamepadEvent::Button { + button_id, + hint, + state, + }, + ) + )); + } - for AxisEvent{ axis, hint, value } in gamepad.get_changed_axes() { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( - gamepad_handle, - GamepadEvent::Axis { - axis, - hint, - value, - }, - ) - )); - } + for AxisEvent{ axis, hint, value } in changed_axes { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle, + GamepadEvent::Axis { + axis, + hint, + value, + }, + ) + )); } }, None => () diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index ce20db9473..7844ce0a51 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -1,6 +1,3 @@ -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; - use winapi::um::winnt::HANDLE; use event::{ @@ -10,42 +7,6 @@ use event::{ use platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}; use platform_impl::platform::xinput::{self, XInputGamepad}; -lazy_static! { - pub static ref GAMEPADS: Arc> = Default::default(); -} - -#[derive(Debug)] -pub struct GamepadStore { - gamepads: HashMap, -} - -impl GamepadStore { - pub fn get_or_add(&mut self, handle: HANDLE) -> Option<&mut Gamepad> { - let key = handle as isize; - let gamepad_registered = self.gamepads.contains_key(&key); - if !gamepad_registered { - self.gamepads.insert(key, Gamepad::new(handle)?); - } - self.gamepads.get_mut(&key) - } - - pub fn remove(&mut self, handle: HANDLE) { - let key = handle as isize; - self.gamepads.remove(&key); - } -} - -impl Default for GamepadStore { - fn default() -> Self { - GamepadStore { - gamepads: HashMap::with_capacity(4), - } - } -} - -unsafe impl Send for GamepadStore {} -unsafe impl Sync for GamepadStore {} - #[derive(Debug)] pub enum GamepadType { Raw(RawGamepad), From a3468c3c4e0d7592a83c72bed9d45cb737c5a8dd Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 3 Mar 2019 12:09:28 -0500 Subject: [PATCH 16/34] Publicly expose gamepad rumble --- examples/gamepad.rs | 25 ++++++--- src/event/device.rs | 8 ++- src/platform_impl/windows/event_loop.rs | 60 +++++++++++---------- src/platform_impl/windows/gamepad.rs | 27 ++++++++-- src/platform_impl/windows/mod.rs | 70 ++++++++++++++++++++++++- src/platform_impl/windows/raw_input.rs | 10 ++-- src/platform_impl/windows/xinput.rs | 30 ++++++++--- 7 files changed, 179 insertions(+), 51 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 51554a16c4..3aa4b3c529 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -1,6 +1,7 @@ extern crate winit; use winit::window::WindowBuilder; -use winit::event::{DeviceEvent, Event, WindowEvent}; +use winit::event::{ElementState, Event, WindowEvent}; +use winit::event::device::{DeviceEvent, GamepadEvent}; use winit::event_loop::{EventLoop, ControlFlow}; fn main() { @@ -11,12 +12,24 @@ fn main() { .build(&event_loop) .unwrap(); - event_loop.run(|event, _, control_flow| { + let mut rumble_left = true; + + event_loop.run(move |event, _, control_flow| { match event { - Event::DeviceEvent { device_id, event } => match event { - DeviceEvent::Button { .. } - | DeviceEvent::Motion { .. } => { - println!("[{:?}] {:#?}", device_id, event); + Event::DeviceEvent(DeviceEvent::GamepadEvent(gamepad_handle, event)) => match event { + GamepadEvent::Axis {value, ..} => { + println!("[{:?}] {:#?}", gamepad_handle, event); + }, + GamepadEvent::Button { state, .. } => { + println!("[{:?}] {:#?}", gamepad_handle, event); + match state { + ElementState::Pressed if rumble_left => gamepad_handle.rumble(1.0, 0.0), + ElementState::Pressed => gamepad_handle.rumble(0.0, 1.0), + ElementState::Released => { + gamepad_handle.rumble(0.0, 0.0); + rumble_left = !rumble_left; + }, + } }, _ => () }, diff --git a/src/event/device.rs b/src/event/device.rs index f26d28c270..a5f6d7d689 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -76,7 +76,7 @@ pub enum AxisHint { /// game controls. Many physical actions, such as mouse movement, can produce both device and window events. /// /// Note that these events are delivered regardless of input focus. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum DeviceEvent { MouseEvent(MouseId, MouseEvent), KeyboardEvent(KeyboardId, KeyboardEvent), @@ -131,7 +131,7 @@ pub enum GamepadEvent { pub struct MouseId(pub(crate) platform_impl::MouseId); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct KeyboardId(pub(crate) platform_impl::KeyboardId); -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct GamepadHandle(pub(crate) platform_impl::GamepadHandle); impl MouseId { @@ -165,6 +165,10 @@ impl GamepadHandle { pub unsafe fn dummy() -> Self { GamepadHandle(platform_impl::GamepadHandle::dummy()) } + + pub fn rumble(&self, left_speed: f64, right_speed: f64) { + self.0.rumble(left_speed, right_speed); + } } impl fmt::Debug for MouseId { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3f1c07ea62..3c587304f4 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1495,8 +1495,11 @@ unsafe extern "system" fn thread_event_target_callback( }, RawDeviceInfo::Hid(_) => { event = Gamepad::new(handle).map(|gamepad| { - let gamepad_handle = GamepadHandle(handle); - subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle, gamepad)); + let gamepad_handle = GamepadHandle { + handle, + rumbler: gamepad.rumbler(), + }; + subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle.clone(), gamepad)); DeviceEvent::GamepadEvent( gamepad_handle.into(), @@ -1640,38 +1643,41 @@ unsafe extern "system" fn thread_event_target_callback( } }, Some(RawInputData::Hid{device_handle, mut raw_hid}) => { + let mut gamepad_handle_opt: Option<::event::device::GamepadHandle> = None; let (mut changed_buttons, mut changed_axes) = (vec![], vec![]); - if let Some(DeviceId::Gamepad(_, ref mut gamepad)) = subclass_input.active_device_ids.get_mut(&device_handle) { + if let Some(DeviceId::Gamepad(gamepad_handle, ref mut gamepad)) = subclass_input.active_device_ids.get_mut(&device_handle) { gamepad.update_state(&mut raw_hid.raw_input); changed_buttons = gamepad.get_changed_buttons(); changed_axes = gamepad.get_changed_axes(); + gamepad_handle_opt = Some(gamepad_handle.clone().into()); } - let gamepad_handle = GamepadHandle(device_handle).into(); - for ButtonEvent{ button_id, hint, state } in changed_buttons { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( - gamepad_handle, - GamepadEvent::Button { - button_id, - hint, - state, - }, - ) - )); - } + if let Some(gamepad_handle) = gamepad_handle_opt { + for ButtonEvent{ button_id, hint, state } in changed_buttons { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle.clone(), + GamepadEvent::Button { + button_id, + hint, + state, + }, + ) + )); + } - for AxisEvent{ axis, hint, value } in changed_axes { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( - gamepad_handle, - GamepadEvent::Axis { - axis, - hint, - value, - }, - ) - )); + for AxisEvent{ axis, hint, value } in changed_axes { + subclass_input.send_event(Event::DeviceEvent( + DeviceEvent::GamepadEvent( + gamepad_handle.clone(), + GamepadEvent::Axis { + axis, + hint, + value, + }, + ) + )); + } } }, None => () diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index 7844ce0a51..544e5941eb 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -1,3 +1,5 @@ +use std::rc::Weak; + use winapi::um::winnt::HANDLE; use event::{ @@ -5,7 +7,7 @@ use event::{ device::{AxisHint, ButtonHint}, }; use platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}; -use platform_impl::platform::xinput::{self, XInputGamepad}; +use platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadRumbler}; #[derive(Debug)] pub enum GamepadType { @@ -13,6 +15,13 @@ pub enum GamepadType { XInput(XInputGamepad), } +#[derive(Clone)] +pub enum GamepadRumbler { + Raw(()), + XInput(Weak), + Dummy, +} + #[derive(Debug)] pub struct Gamepad { handle: HANDLE, @@ -70,10 +79,10 @@ impl Gamepad { } } - pub fn rumble(&mut self, left_speed: u16, right_speed: u16) { + pub fn rumbler(&self) -> GamepadRumbler { match self.backend { - GamepadType::Raw(ref mut gamepad) => gamepad.rumble(left_speed, right_speed), - GamepadType::XInput(ref mut gamepad) => gamepad.rumble(left_speed, right_speed), + GamepadType::Raw(_) => GamepadRumbler::Raw(()), + GamepadType::XInput(ref gamepad) => GamepadRumbler::XInput(gamepad.rumbler()), } } } @@ -89,3 +98,13 @@ impl ButtonEvent { ButtonEvent{ button_id, hint, state } } } + +impl GamepadRumbler { + pub fn rumble(&self, left_speed: f64, right_speed: f64) { + match self { + GamepadRumbler::Raw(_) => (), + GamepadRumbler::XInput(ref rumbler) => {rumbler.upgrade().map(|r| r.rumble(left_speed, right_speed));}, + GamepadRumbler::Dummy => (), + } + } +} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 06a315dee8..38bf9f2882 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -13,6 +13,9 @@ mod window; mod window_state; mod xinput; +use std::cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd}; +use std::hash::{Hash, Hasher}; +use std::fmt; use std::ptr; use winapi; use winapi::shared::windef::HWND; @@ -20,6 +23,7 @@ use winapi::um::winnt::HANDLE; use window::Icon; pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy}; +pub use self::gamepad::GamepadRumbler; pub use self::monitor::MonitorHandle; pub use self::window::Window; @@ -75,4 +79,68 @@ macro_rules! device_id { device_id!(MouseId); device_id!(KeyboardId); -device_id!(GamepadHandle); + +#[derive(Clone)] +pub(crate) struct GamepadHandle { + handle: HANDLE, + rumbler: GamepadRumbler, +} + +impl GamepadHandle { + pub unsafe fn dummy() -> Self { + Self { + handle: ptr::null_mut(), + rumbler: GamepadRumbler::Dummy, + } + } + + pub fn get_persistent_identifier(&self) -> Option { + raw_input::get_raw_input_device_name(self.handle) + } + + pub fn rumble(&self, left_speed: f64, right_speed: f64) { + self.rumbler.rumble(left_speed, right_speed); + } +} + +impl From for crate::event::device::GamepadHandle { + fn from(platform_id: GamepadHandle) -> Self { + Self(platform_id) + } +} + +impl fmt::Debug for GamepadHandle { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.debug_tuple("GamepadHandle") + .field(&self.handle) + .finish() + } +} + +impl Eq for GamepadHandle {} +impl PartialEq for GamepadHandle { + #[inline(always)] + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle + } +} + +impl Ord for GamepadHandle { + #[inline(always)] + fn cmp(&self, other: &Self) -> Ordering { + self.handle.cmp(&other.handle) + } +} +impl PartialOrd for GamepadHandle { + #[inline(always)] + fn partial_cmp(&self, other: &Self) -> Option { + self.handle.partial_cmp(&other.handle) + } +} + +impl Hash for GamepadHandle { + #[inline(always)] + fn hash(&self, state: &mut H) { + self.handle.hash(state); + } +} diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index fa6158ce34..837073e86b 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -673,9 +673,9 @@ impl RawGamepad { .collect() } - pub fn rumble(&mut self, _left_speed: u16, _right_speed: u16) { - // Even though I can't read German, this is still the most useful resource I found: - // https://zfx.info/viewtopic.php?t=3574&f=7 - // I'm not optimistic about it being possible to implement this. - } + // pub fn rumble(&mut self, _left_speed: u16, _right_speed: u16) { + // // Even though I can't read German, this is still the most useful resource I found: + // // https://zfx.info/viewtopic.php?t=3574&f=7 + // // I'm not optimistic about it being possible to implement this. + // } } diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 4ad8593e12..4d4d3899f8 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -1,4 +1,5 @@ use std::mem; +use std::rc::{Rc, Weak}; use rusty_xinput::*; use winapi::shared::minwindef::{DWORD, WORD}; @@ -55,22 +56,31 @@ enum Side { #[derive(Debug)] pub struct XInputGamepad { - id: DWORD, + port: DWORD, prev_state: Option, state: Option, + rumbler: Rc, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct XInputGamepadRumbler { + port: DWORD, } impl XInputGamepad { - pub fn new(id: DWORD) -> Option { + pub fn new(port: DWORD) -> Option { XINPUT_GUARD.map(|_| XInputGamepad { - id, + port, prev_state: None, state: None, + rumbler: Rc::new(XInputGamepadRumbler { + port, + }) }) } pub fn update_state(&mut self) -> Option<()> { - let state = xinput_get_state(self.id).ok(); + let state = xinput_get_state(self.port).ok(); if state.is_some() { self.prev_state = mem::replace(&mut self.state, state); Some(()) @@ -218,8 +228,16 @@ impl XInputGamepad { events } - pub fn rumble(&mut self, left_speed: u16, right_speed: u16) { + pub fn rumbler(&self) -> Weak { + Rc::downgrade(&self.rumbler) + } +} + +impl XInputGamepadRumbler { + pub fn rumble(&self, left_speed: f64, right_speed: f64) { + let left_speed = (left_speed * u16::max_value() as f64) as u16; + let right_speed = (right_speed * u16::max_value() as f64) as u16; // TODO: We should probably return the status - let _ = xinput_set_state(self.id, left_speed, right_speed); + let _ = xinput_set_state(self.port, left_speed, right_speed); } } From 437ead3919393b82175f19cd56fab599fa999a7c Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 3 Mar 2019 12:48:58 -0500 Subject: [PATCH 17/34] Unstack DeviceEvent and fix examples/tests --- examples/cursor.rs | 2 +- examples/cursor_grab.rs | 13 ++--- examples/fullscreen.rs | 12 ++--- examples/gamepad.rs | 8 ++-- examples/handling_close.rs | 12 ++--- examples/multiwindow.rs | 2 +- examples/resizable.rs | 12 ++--- src/event.rs | 11 +++-- src/event/device.rs | 23 +++------ src/platform_impl/windows/event_loop.rs | 63 ++++++++++++------------- src/platform_impl/windows/gamepad.rs | 2 +- src/platform_impl/windows/mod.rs | 6 +++ src/platform_impl/windows/xinput.rs | 8 ++-- tests/send_objects.rs | 6 ++- 14 files changed, 83 insertions(+), 97 deletions(-) diff --git a/examples/cursor.rs b/examples/cursor.rs index 27c003ac2d..df9bb1f000 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -14,7 +14,7 @@ fn main() { event_loop.run(move |event, _, control_flow| { match event { - Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => { + Event::WindowEvent { event: WindowEvent::KeyboardInput(KeyboardInput { state: ElementState::Pressed, .. }), .. } => { println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]); window.set_cursor(CURSORS[cursor_idx]); if cursor_idx < CURSORS.len() - 1 { diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 741d0d74aa..06d2f878aa 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -17,15 +17,12 @@ fn main() { if let Event::WindowEvent { event, .. } = event { match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { - input: KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(key), - modifiers, - .. - }, + WindowEvent::KeyboardInput(KeyboardInput { + state: ElementState::Released, + virtual_keycode: Some(key), + modifiers, .. - } => { + }) => { use winit::event::VirtualKeyCode::*; match key { Escape => *control_flow = ControlFlow::Exit, diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 788a608c85..0c8bb8279a 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -56,15 +56,11 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state, - .. - }, + WindowEvent::KeyboardInput(KeyboardInput { + virtual_keycode: Some(virtual_code), + state, .. - } => match (virtual_code, state) { + }) => match (virtual_code, state) { (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, (VirtualKeyCode::F, ElementState::Pressed) => { #[cfg(target_os = "macos")] diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 3aa4b3c529..7204c8eeb6 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -1,11 +1,11 @@ extern crate winit; use winit::window::WindowBuilder; use winit::event::{ElementState, Event, WindowEvent}; -use winit::event::device::{DeviceEvent, GamepadEvent}; +use winit::event::device::GamepadEvent; use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut event_loop = EventLoop::new(); + let event_loop = EventLoop::new(); let _window = WindowBuilder::new() .with_title("The world's worst video game") @@ -16,8 +16,8 @@ fn main() { event_loop.run(move |event, _, control_flow| { match event { - Event::DeviceEvent(DeviceEvent::GamepadEvent(gamepad_handle, event)) => match event { - GamepadEvent::Axis {value, ..} => { + Event::GamepadEvent(gamepad_handle, event) => match event { + GamepadEvent::Axis {..} => { println!("[{:?}] {:#?}", gamepad_handle, event); }, GamepadEvent::Button { state, .. } => { diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 31f17da33e..8e01deaaaa 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -38,15 +38,11 @@ fn main() { // closing the window. How to close the window is detailed in the handler for // the Y key. } - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state: Released, - .. - }, + WindowEvent::KeyboardInput(KeyboardInput { + virtual_keycode: Some(virtual_code), + state: Released, .. - } => match virtual_code { + }) => match virtual_code { Y => { if close_requested { // This is where you'll want to do any cleanup you need. diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index cf2703c2dd..5657f72f25 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -29,7 +29,7 @@ fn main() { *control_flow = ControlFlow::Exit; } }, - WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => { + WindowEvent::KeyboardInput(KeyboardInput { state: ElementState::Pressed, .. }) => { let window = Window::new(&event_loop).unwrap(); windows.insert(window.id(), window); }, diff --git a/examples/resizable.rs b/examples/resizable.rs index f5690b86b7..ad017e4398 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -20,15 +20,11 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Space), - state: ElementState::Released, - .. - }, + WindowEvent::KeyboardInput(KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Space), + state: ElementState::Released, .. - } => { + }) => { resizable = !resizable; println!("Resizable: {}", resizable); window.set_resizable(resizable); diff --git a/src/event.rs b/src/event.rs index 3fac706d09..84f03b8c88 100644 --- a/src/event.rs +++ b/src/event.rs @@ -20,8 +20,11 @@ pub enum Event { window_id: WindowId, event: WindowEvent, }, - /// Emitted when the OS sends an event to a device. - DeviceEvent(device::DeviceEvent), + + MouseEvent(device::MouseId, device::MouseEvent), + KeyboardEvent(device::KeyboardId, device::KeyboardEvent), + GamepadEvent(device::GamepadHandle, device::GamepadEvent), + /// Emitted when an event is sent from [`EventLoopProxy::send_event`](../event_loop/struct.EventLoopProxy.html#method.send_event) UserEvent(T), /// Emitted when new events arrive from the OS to be processed. @@ -46,7 +49,9 @@ impl Event { match self { UserEvent(_) => Err(self), WindowEvent{window_id, event} => Ok(WindowEvent{window_id, event}), - DeviceEvent(event) => Ok(DeviceEvent(event)), + MouseEvent(id, event) => Ok(MouseEvent(id, event)), + KeyboardEvent(id, event) => Ok(KeyboardEvent(id, event)), + GamepadEvent(id, event) => Ok(GamepadEvent(id, event)), NewEvents(cause) => Ok(NewEvents(cause)), EventsCleared => Ok(EventsCleared), LoopDestroyed => Ok(LoopDestroyed), diff --git a/src/event/device.rs b/src/event/device.rs index a5f6d7d689..40bcc56fe4 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -64,23 +64,12 @@ pub enum AxisHint { /// for flight simulation. However, Xbox 360 controllers (and their derivatives) use a hat switch for the D-pad. HatSwitch, - DPadUp, - DPadDown, - DPadLeft, - DPadRight, -} - -/// Represents raw hardware events that are not associated with any particular window. -/// -/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person -/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. -/// -/// Note that these events are delivered regardless of input focus. -#[derive(Clone, Debug, PartialEq)] -pub enum DeviceEvent { - MouseEvent(MouseId, MouseEvent), - KeyboardEvent(KeyboardId, KeyboardEvent), - GamepadEvent(GamepadHandle, GamepadEvent), + // @francesca64 review: why were these variants here? I don't see how it makes sense for the dpad + // to have axes, since it's four separate buttons. + // DPadUp, + // DPadDown, + // DPadLeft, + // DPadRight, } #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3c587304f4..0903a4dd14 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -48,7 +48,7 @@ use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed} use dpi::{LogicalPosition, LogicalSize, PhysicalSize}; use event::{ MouseButton, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent, - device::{DeviceEvent, GamepadEvent, KeyboardEvent, MouseEvent} + device::{GamepadEvent, KeyboardEvent, MouseEvent} }; use platform_impl::platform::{ MouseId, KeyboardId, GamepadHandle, WindowId, @@ -1474,13 +1474,13 @@ unsafe extern "system" fn thread_event_target_callback( match wparam as _ { winuser::GIDC_ARRIVAL => { if let Some(handle_info) = raw_input::get_raw_input_device_info(handle) { - let event: Option; + let event: Option>; match handle_info { RawDeviceInfo::Mouse(_) => { let mouse_id = MouseId(handle); subclass_input.active_device_ids.insert(handle, DeviceId::Mouse(mouse_id)); - event = Some(DeviceEvent::MouseEvent( + event = Some(Event::MouseEvent( mouse_id.into(), MouseEvent::Added, )); @@ -1488,7 +1488,7 @@ unsafe extern "system" fn thread_event_target_callback( RawDeviceInfo::Keyboard(_) => { let keyboard_id = KeyboardId(handle); subclass_input.active_device_ids.insert(handle, DeviceId::Keyboard(keyboard_id)); - event = Some(DeviceEvent::KeyboardEvent( + event = Some(Event::KeyboardEvent( keyboard_id.into(), KeyboardEvent::Added, )); @@ -1501,7 +1501,7 @@ unsafe extern "system" fn thread_event_target_callback( }; subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle.clone(), gamepad)); - DeviceEvent::GamepadEvent( + Event::GamepadEvent( gamepad_handle.into(), GamepadEvent::Added, ) @@ -1510,29 +1510,29 @@ unsafe extern "system" fn thread_event_target_callback( } if let Some(event) = event { - subclass_input.send_event(Event::DeviceEvent(event)); + subclass_input.send_event(event); } } }, winuser::GIDC_REMOVAL => { if let Some(device_id) = subclass_input.active_device_ids.remove(&handle) { let event = match device_id { - DeviceId::Mouse(mouse_id) => DeviceEvent::MouseEvent( + DeviceId::Mouse(mouse_id) => Event::MouseEvent( mouse_id.into(), MouseEvent::Removed, ), - DeviceId::Keyboard(keyboard_id) => DeviceEvent::KeyboardEvent( + DeviceId::Keyboard(keyboard_id) => Event::KeyboardEvent( keyboard_id.into(), KeyboardEvent::Removed, ), DeviceId::Gamepad(gamepad_handle, _) => { - DeviceEvent::GamepadEvent( + Event::GamepadEvent( gamepad_handle.into(), GamepadEvent::Removed ) }, }; - subclass_input.send_event(Event::DeviceEvent(event)); + subclass_input.send_event(event); } }, _ => unreachable!() @@ -1542,7 +1542,6 @@ unsafe extern "system" fn thread_event_target_callback( }, winuser::WM_INPUT => { - use event::device::DeviceEvent; use event::ElementState::{Pressed, Released}; match get_raw_input_data(lparam as _) { @@ -1554,41 +1553,41 @@ unsafe extern "system" fn thread_event_target_callback( let y = raw_mouse.lLastY as f64; if x != 0.0 || y != 0.0 { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::MouseEvent( + subclass_input.send_event( + Event::MouseEvent( mouse_handle, MouseEvent::Moved(x, y), ) - )); + ); } } if util::has_flag(raw_mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { // TODO: HOW IS RAW WHEEL DELTA HANDLED ON OTHER PLATFORMS? let delta = raw_mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::MouseEvent( + subclass_input.send_event( + Event::MouseEvent( mouse_handle, MouseEvent::Wheel(0.0, delta as f64), ) - )); + ); } // Check if there's horizontal wheel movement. if util::has_flag(raw_mouse.usButtonFlags, 0x0800) { // TODO: HOW IS RAW WHEEL DELTA HANDLED ON OTHER PLATFORMS? let delta = raw_mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::MouseEvent( + subclass_input.send_event( + Event::MouseEvent( mouse_handle, MouseEvent::Wheel(delta as f64, 0.0), ) - )); + ); } let button_state = get_raw_mouse_button_state(raw_mouse.usButtonFlags); for (index, state) in button_state.iter().cloned().enumerate().filter_map(|(i, state)| state.map(|s| (i, s))) { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::MouseEvent( + subclass_input.send_event( + Event::MouseEvent( mouse_handle, MouseEvent::Button { state, @@ -1600,7 +1599,7 @@ unsafe extern "system" fn thread_event_target_callback( }, }, ) - )); + ); } }, Some(RawInputData::Keyboard{device_handle, raw_keyboard}) => { @@ -1628,8 +1627,8 @@ unsafe extern "system" fn thread_event_target_callback( ) { let virtual_keycode = vkey_to_winit_vkey(vkey); - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::KeyboardEvent( + subclass_input.send_event( + Event::KeyboardEvent( keyboard_id, KeyboardEvent::Input(KeyboardInput { scancode, @@ -1638,7 +1637,7 @@ unsafe extern "system" fn thread_event_target_callback( modifiers: event::get_key_mods(), }), ) - )); + ); } } }, @@ -1654,8 +1653,8 @@ unsafe extern "system" fn thread_event_target_callback( if let Some(gamepad_handle) = gamepad_handle_opt { for ButtonEvent{ button_id, hint, state } in changed_buttons { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( + subclass_input.send_event( + Event::GamepadEvent( gamepad_handle.clone(), GamepadEvent::Button { button_id, @@ -1663,12 +1662,12 @@ unsafe extern "system" fn thread_event_target_callback( state, }, ) - )); + ); } for AxisEvent{ axis, hint, value } in changed_axes { - subclass_input.send_event(Event::DeviceEvent( - DeviceEvent::GamepadEvent( + subclass_input.send_event( + Event::GamepadEvent( gamepad_handle.clone(), GamepadEvent::Axis { axis, @@ -1676,7 +1675,7 @@ unsafe extern "system" fn thread_event_target_callback( value, }, ) - )); + ); } } }, diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index 544e5941eb..0b9f448b60 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -1,4 +1,4 @@ -use std::rc::Weak; +use std::sync::Weak; use winapi::um::winnt::HANDLE; diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 38bf9f2882..ec53ec37c9 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -59,6 +59,9 @@ macro_rules! device_id { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct $name(HANDLE); + unsafe impl Send for $name {} + unsafe impl Sync for $name {} + impl $name { pub unsafe fn dummy() -> Self { Self(ptr::null_mut()) @@ -86,6 +89,9 @@ pub(crate) struct GamepadHandle { rumbler: GamepadRumbler, } +unsafe impl Send for GamepadHandle where GamepadRumbler: Send {} +unsafe impl Sync for GamepadHandle where GamepadRumbler: Sync {} + impl GamepadHandle { pub unsafe fn dummy() -> Self { Self { diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 4d4d3899f8..fcdaa26637 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -1,5 +1,5 @@ use std::mem; -use std::rc::{Rc, Weak}; +use std::sync::{Arc, Weak}; use rusty_xinput::*; use winapi::shared::minwindef::{DWORD, WORD}; @@ -59,7 +59,7 @@ pub struct XInputGamepad { port: DWORD, prev_state: Option, state: Option, - rumbler: Rc, + rumbler: Arc, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -73,7 +73,7 @@ impl XInputGamepad { port, prev_state: None, state: None, - rumbler: Rc::new(XInputGamepadRumbler { + rumbler: Arc::new(XInputGamepadRumbler { port, }) }) @@ -229,7 +229,7 @@ impl XInputGamepad { } pub fn rumbler(&self) -> Weak { - Rc::downgrade(&self.rumbler) + Arc::downgrade(&self.rumbler) } } diff --git a/tests/send_objects.rs b/tests/send_objects.rs index 9220203fd4..b85d999cf6 100644 --- a/tests/send_objects.rs +++ b/tests/send_objects.rs @@ -4,7 +4,7 @@ fn needs_send() {} #[test] fn event_loop_proxy_send() { - fn is_send() { + fn is_send() { // ensures that `winit::EventLoopProxy` implements `Send` needs_send::>(); } @@ -20,6 +20,8 @@ fn window_send() { fn ids_send() { // ensures that the various `..Id` types implement `Send` needs_send::(); - needs_send::(); + needs_send::(); + needs_send::(); + needs_send::(); needs_send::(); } From 36b95e2a2ef92ebd280c8f62cedc738c3b46df2a Mon Sep 17 00:00:00 2001 From: Osspial Date: Sun, 3 Mar 2019 12:54:26 -0500 Subject: [PATCH 18/34] Add HANDLE retrieval method to DeviceExtWindows --- src/platform/windows.rs | 18 ++++++++++++++++++ src/platform_impl/windows/mod.rs | 10 ++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 55956269ad..43a8009bfd 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -110,6 +110,9 @@ pub trait DeviceExtWindows { /// /// Will return `None` if the device is no longer available. fn get_persistent_identifier(&self) -> Option; + + /// Returns the handle of the device - `HANDLE`. + fn handle(&self) -> *mut c_void; } impl DeviceExtWindows for MouseId { @@ -117,6 +120,11 @@ impl DeviceExtWindows for MouseId { fn get_persistent_identifier(&self) -> Option { self.0.get_persistent_identifier() } + + #[inline] + fn handle(&self) -> *mut c_void { + self.0.handle() as _ + } } impl DeviceExtWindows for KeyboardId { @@ -124,6 +132,11 @@ impl DeviceExtWindows for KeyboardId { fn get_persistent_identifier(&self) -> Option { self.0.get_persistent_identifier() } + + #[inline] + fn handle(&self) -> *mut c_void { + self.0.handle() as _ + } } impl DeviceExtWindows for GamepadHandle { @@ -131,4 +144,9 @@ impl DeviceExtWindows for GamepadHandle { fn get_persistent_identifier(&self) -> Option { self.0.get_persistent_identifier() } + + #[inline] + fn handle(&self) -> *mut c_void { + self.0.handle() as _ + } } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index ec53ec37c9..aad3f28728 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -70,6 +70,11 @@ macro_rules! device_id { pub fn get_persistent_identifier(&self) -> Option { raw_input::get_raw_input_device_name(self.0) } + + #[inline(always)] + pub fn handle(&self) -> HANDLE { + self.0 + } } impl From<$name> for crate::event::device::$name { @@ -104,6 +109,11 @@ impl GamepadHandle { raw_input::get_raw_input_device_name(self.handle) } + #[inline(always)] + pub fn handle(&self) -> HANDLE { + self.handle + } + pub fn rumble(&self, left_speed: f64, right_speed: f64) { self.rumbler.rumble(left_speed, right_speed); } From b48bd27a24a013b231e3b5bbd2114c79f952e74b Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 01:03:58 -0500 Subject: [PATCH 19/34] Add distinction between non-joystick axes and joystick axes. This helps with properly calculating the deadzone for controller joysticks. One potential issue is that the `Stick` variant isn't used for *all* joysticks, which could be potentially confusing - for example, raw input joysticks will never use the `Stick` variant because we don't understand the semantic meaning of raw input joystick axes. --- examples/gamepad.rs | 7 +- src/event/device.rs | 16 ++- src/platform_impl/windows/event_loop.rs | 28 +----- src/platform_impl/windows/gamepad.rs | 44 +------- src/platform_impl/windows/raw_input.rs | 26 +++-- src/platform_impl/windows/xinput.rs | 127 +++++++++++++----------- 6 files changed, 118 insertions(+), 130 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 7204c8eeb6..b2fae1e8b9 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -14,13 +14,16 @@ fn main() { let mut rumble_left = true; + let deadzone = 0.1; + event_loop.run(move |event, _, control_flow| { match event { Event::GamepadEvent(gamepad_handle, event) => match event { - GamepadEvent::Axis {..} => { + GamepadEvent::Axis{stick: false, ..} => println!("[{:?}] {:#?}", gamepad_handle, event), + GamepadEvent::Stick{x_value, y_value, ..} if (x_value.powi(2) + y_value.powi(2)).sqrt() >= deadzone => { println!("[{:?}] {:#?}", gamepad_handle, event); }, - GamepadEvent::Button { state, .. } => { + GamepadEvent::Button{ state, .. } => { println!("[{:?}] {:#?}", gamepad_handle, event); match state { ElementState::Pressed if rumble_left => gamepad_handle.rumble(1.0, 0.0), diff --git a/src/event/device.rs b/src/event/device.rs index 40bcc56fe4..62b1178099 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -72,6 +72,12 @@ pub enum AxisHint { // DPadRight, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Side { + Left, + Right, +} + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub enum MouseEvent { /// A mouse device has been added. @@ -105,9 +111,17 @@ pub enum GamepadEvent { Added, Removed, Axis { - axis: AxisId, + axis_id: AxisId, hint: Option, value: f64, + stick: bool, + }, + Stick { + x_id: AxisId, + y_id: AxisId, + x_value: f64, + y_value: f64, + side: Side, }, Button { button_id: ButtonId, diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 0903a4dd14..3bdfeed233 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -55,7 +55,7 @@ use platform_impl::platform::{ dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, get_hwnd_scale_factor}, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, - gamepad::{AxisEvent, ButtonEvent, Gamepad}, + gamepad::Gamepad, raw_input::{self, get_raw_input_data, get_raw_mouse_button_state, RawInputData}, util, window::adjust_size, @@ -1643,37 +1643,19 @@ unsafe extern "system" fn thread_event_target_callback( }, Some(RawInputData::Hid{device_handle, mut raw_hid}) => { let mut gamepad_handle_opt: Option<::event::device::GamepadHandle> = None; - let (mut changed_buttons, mut changed_axes) = (vec![], vec![]); + let mut events = vec![]; if let Some(DeviceId::Gamepad(gamepad_handle, ref mut gamepad)) = subclass_input.active_device_ids.get_mut(&device_handle) { gamepad.update_state(&mut raw_hid.raw_input); - changed_buttons = gamepad.get_changed_buttons(); - changed_axes = gamepad.get_changed_axes(); + events = gamepad.get_gamepad_events(); gamepad_handle_opt = Some(gamepad_handle.clone().into()); } if let Some(gamepad_handle) = gamepad_handle_opt { - for ButtonEvent{ button_id, hint, state } in changed_buttons { + for gamepad_event in events { subclass_input.send_event( Event::GamepadEvent( gamepad_handle.clone(), - GamepadEvent::Button { - button_id, - hint, - state, - }, - ) - ); - } - - for AxisEvent{ axis, hint, value } in changed_axes { - subclass_input.send_event( - Event::GamepadEvent( - gamepad_handle.clone(), - GamepadEvent::Axis { - axis, - hint, - value, - }, + gamepad_event, ) ); } diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index 0b9f448b60..e77280b2c5 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -2,10 +2,7 @@ use std::sync::Weak; use winapi::um::winnt::HANDLE; -use event::{ - ElementState, - device::{AxisHint, ButtonHint}, -}; +use event::device::GamepadEvent; use platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}; use platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadRumbler}; @@ -28,20 +25,6 @@ pub struct Gamepad { backend: GamepadType, } -#[derive(Debug, Clone, Copy)] -pub struct AxisEvent { - pub axis: u32, - pub hint: Option, - pub value: f64, -} - -#[derive(Debug, Clone, Copy)] -pub struct ButtonEvent { - pub button_id: u32, - pub hint: Option, - pub state: ElementState, -} - impl Gamepad { pub fn new(handle: HANDLE) -> Option { // TODO: Verify that this is an HID device @@ -65,17 +48,10 @@ impl Gamepad { } } - pub fn get_changed_buttons(&self) -> Vec { - match self.backend { - GamepadType::Raw(ref gamepad) => gamepad.get_changed_buttons(), - GamepadType::XInput(ref gamepad) => gamepad.get_changed_buttons(), - } - } - - pub fn get_changed_axes(&self) -> Vec { + pub fn get_gamepad_events(&self) -> Vec { match self.backend { - GamepadType::Raw(ref gamepad) => gamepad.get_changed_axes(), - GamepadType::XInput(ref gamepad) => gamepad.get_changed_axes(), + GamepadType::Raw(ref gamepad) => gamepad.get_gamepad_events(), + GamepadType::XInput(ref gamepad) => gamepad.get_gamepad_events(), } } @@ -87,18 +63,6 @@ impl Gamepad { } } -impl AxisEvent { - pub fn new(axis: u32, hint: Option, value: f64) -> AxisEvent { - AxisEvent{ axis, hint, value } - } -} - -impl ButtonEvent { - pub fn new(button_id: u32, hint: Option, state: ElementState) -> ButtonEvent { - ButtonEvent{ button_id, hint, state } - } -} - impl GamepadRumbler { pub fn rumble(&self, left_speed: f64, right_speed: f64) { match self { diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 837073e86b..26601fe13c 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -52,11 +52,10 @@ use winapi::um::winuser::{ RIM_TYPEHID, }; -use super::gamepad::{AxisEvent, ButtonEvent}; use platform_impl::platform::util; use event::{ ElementState, - device::AxisHint, + device::{AxisHint, GamepadEvent}, }; #[allow(dead_code)] @@ -651,7 +650,7 @@ impl RawGamepad { Some(()) } - pub fn get_changed_buttons(&self) -> Vec { + pub fn get_changed_buttons(&self) -> impl '_ + Iterator { self.button_state .iter() .zip(self.prev_button_state.iter()) @@ -659,18 +658,29 @@ impl RawGamepad { .filter(|&(_, (button, prev_button))| button != prev_button) .map(|(index, (button, _))| { let state = if *button { ElementState::Pressed } else { ElementState::Released }; - ButtonEvent::new(index as _, None, state) + GamepadEvent::Button { + button_id: index as _, + hint: None, + state, + } }) - .collect() } - pub fn get_changed_axes(&self) -> Vec { + pub fn get_changed_axes(&self) -> impl '_ + Iterator { self.axis_state .iter() .enumerate() .filter(|&(_, axis)| axis.value != axis.prev_value) - .map(|(index, axis)| AxisEvent::new(index as _, axis.hint, axis.value)) - .collect() + .map(|(index, axis)| GamepadEvent::Axis { + axis_id: index as _, + hint: axis.hint, + value: axis.value, + stick: false, + }) + } + + pub fn get_gamepad_events(&self) -> Vec { + self.get_changed_axes().chain(self.get_changed_buttons()).collect() } // pub fn rumble(&mut self, _left_speed: u16, _right_speed: u16) { diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index fcdaa26637..f021b25d27 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -7,9 +7,8 @@ use winapi::um::xinput::*; use event::{ ElementState, - device::{AxisHint, ButtonHint}, + device::{AxisHint, ButtonHint, GamepadEvent, Side}, }; -use super::gamepad::{AxisEvent, ButtonEvent}; use platform_impl::platform::util; lazy_static! { @@ -48,12 +47,6 @@ pub fn id_from_name(name: &str) -> Option { }) } -#[derive(Debug)] -enum Side { - Left, - Right, -} - #[derive(Debug)] pub struct XInputGamepad { port: DWORD, @@ -90,7 +83,7 @@ impl XInputGamepad { } fn check_trigger_digital( - events: &mut Vec, + events: &mut Vec, value: bool, prev_value: Option, side: Side, @@ -99,22 +92,22 @@ impl XInputGamepad { const RIGHT_TRIGGER_ID: u32 = LEFT_TRIGGER_ID + 1; if Some(value) != prev_value { let state = if value { ElementState::Pressed } else { ElementState::Released }; - let (id, hint) = match side { - Side::Left => (LEFT_TRIGGER_ID, ButtonHint::LeftTrigger), - Side::Right => (RIGHT_TRIGGER_ID, ButtonHint::RightTrigger), + let (button_id, hint) = match side { + Side::Left => (LEFT_TRIGGER_ID, Some(ButtonHint::LeftTrigger)), + Side::Right => (RIGHT_TRIGGER_ID, Some(ButtonHint::RightTrigger)), }; - events.push(ButtonEvent::new(id, Some(hint), state)); + events.push(GamepadEvent::Button{button_id, hint, state}); } } - pub fn get_changed_buttons(&self) -> Vec { + pub fn get_changed_buttons(&self, events: &mut Vec) { let (buttons, left_trigger, right_trigger) = match self.state.as_ref() { Some(state) => ( state.raw.Gamepad.wButtons, state.left_trigger_bool(), state.right_trigger_bool(), ), - None => return Vec::with_capacity(0), + None => return, }; let (prev_buttons, prev_left, prev_right) = self.prev_state .as_ref() @@ -139,21 +132,20 @@ impl XInputGamepad { let changed = buttons ^ prev_buttons; let pressed = changed & buttons; let released = changed & prev_buttons; - let mut events = Vec::with_capacity(BUTTONS.len() + 2); - for &(flag, id, hint) in BUTTONS { + for &(flag, button_id, hint) in BUTTONS { + let hint = Some(hint); if util::has_flag(pressed, flag) { - events.push(ButtonEvent::new(id, Some(hint), ElementState::Pressed)); + events.push(GamepadEvent::Button{button_id, hint, state: ElementState::Pressed}); } else if util::has_flag(released, flag) { - events.push(ButtonEvent::new(id, Some(hint), ElementState::Released)); + events.push(GamepadEvent::Button{button_id, hint, state: ElementState::Released}); } } - Self::check_trigger_digital(&mut events, left_trigger, prev_left, Side::Left); - Self::check_trigger_digital(&mut events, right_trigger, prev_right, Side::Right); - events + Self::check_trigger_digital(events, left_trigger, prev_left, Side::Left); + Self::check_trigger_digital(events, right_trigger, prev_right, Side::Right); } fn check_trigger( - events: &mut Vec, + events: &mut Vec, value: u8, prev_value: Option, side: Side, @@ -161,34 +153,21 @@ impl XInputGamepad { const LEFT_TRIGGER_ID: u32 = 4; const RIGHT_TRIGGER_ID: u32 = LEFT_TRIGGER_ID + 1; if Some(value) != prev_value { - let (id, hint) = match side { - Side::Left => (LEFT_TRIGGER_ID, AxisHint::LeftTrigger), - Side::Right => (RIGHT_TRIGGER_ID, AxisHint::RightTrigger), - }; - events.push(AxisEvent::new(id, Some(hint), value as f64 / u8::max_value() as f64)); - } - } - - fn check_axis( - events: &mut Vec, - value: i16, - prev_value: Option, - id: u32, - hint: AxisHint, - ) { - if Some(value) != prev_value { - let value = match value.signum() { - 0 => 0.0, - 1 => value as f64 / i16::max_value() as f64, - -1 => value as f64 / (i16::min_value() as f64).abs(), - _ => unreachable!() + let (axis_id, hint) = match side { + Side::Left => (LEFT_TRIGGER_ID, Some(AxisHint::LeftTrigger)), + Side::Right => (RIGHT_TRIGGER_ID, Some(AxisHint::RightTrigger)), }; - events.push(AxisEvent::new(id, Some(hint), f64::from(value))); + events.push(GamepadEvent::Axis{ + axis_id, + hint, + value: value as f64 / u8::max_value() as f64, + stick: false, + }); } } fn check_stick( - events: &mut Vec, + events: &mut Vec, value: (i16, i16), prev_value: Option<(i16, i16)>, stick: Side, @@ -199,32 +178,68 @@ impl XInputGamepad { }; let prev_x = prev_value.map(|prev| prev.0); let prev_y = prev_value.map(|prev| prev.1); - Self::check_axis(events, value.0, prev_x, id.0, hint.0); - Self::check_axis(events, value.1, prev_y, id.1, hint.1); + + let value_f64 = |value_int: i16| match value_int.signum() { + 0 => 0.0, + 1 => value_int as f64 / i16::max_value() as f64, + -1 => value_int as f64 / (i16::min_value() as f64).abs(), + _ => unreachable!() + }; + + let value_f64 = (value_f64(value.0), value_f64(value.1)); + if prev_x != Some(value.0) { + events.push(GamepadEvent::Axis { + axis_id: id.0, + hint: Some(hint.0), + value: value_f64.0, + stick: true, + }); + } + if prev_y != Some(value.1) { + events.push(GamepadEvent::Axis { + axis_id: id.1, + hint: Some(hint.1), + value: value_f64.1, + stick: true, + }); + } + if prev_x != Some(value.0) || prev_y != Some(value.1) { + events.push(GamepadEvent::Stick { + x_id: id.0, + y_id: id.1, + x_value: value_f64.0, + y_value: value_f64.1, + side: stick, + }) + } } - pub fn get_changed_axes(&self) -> Vec { + pub fn get_changed_axes(&self, events: &mut Vec) { let state = match self.state { Some(ref state) => state, - None => return Vec::new(), + None => return, }; let left_stick = state.left_stick_raw(); let right_stick = state.right_stick_raw(); let left_trigger = state.left_trigger(); let right_trigger = state.right_trigger(); - let mut events = Vec::with_capacity(6); - let prev_state = self.prev_state.as_ref(); let prev_left_stick = prev_state.map(|state| state.left_stick_raw()); let prev_right_stick = prev_state.map(|state| state.right_stick_raw()); let prev_left_trigger = prev_state.map(|state| state.left_trigger()); let prev_right_trigger = prev_state.map(|state| state.right_trigger()); - Self::check_stick(&mut events, left_stick, prev_left_stick, Side::Left); - Self::check_stick(&mut events, right_stick, prev_right_stick, Side::Right); - Self::check_trigger(&mut events, left_trigger, prev_left_trigger, Side::Left); - Self::check_trigger(&mut events, right_trigger, prev_right_trigger, Side::Right); + Self::check_stick(events, left_stick, prev_left_stick, Side::Left); + Self::check_stick(events, right_stick, prev_right_stick, Side::Right); + Self::check_trigger(events, left_trigger, prev_left_trigger, Side::Left); + Self::check_trigger(events, right_trigger, prev_right_trigger, Side::Right); + } + + pub fn get_gamepad_events(&self) -> Vec { + let mut events = Vec::new(); + self.get_changed_axes(&mut events); + self.get_changed_buttons(&mut events); events } From 0b0066b75df95a3f1e2d500d1730697901fdb271 Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 01:22:49 -0500 Subject: [PATCH 20/34] Add ability to get gamepad port --- examples/gamepad.rs | 8 ++++--- src/event/device.rs | 4 ++++ src/platform_impl/windows/event_loop.rs | 2 +- src/platform_impl/windows/gamepad.rs | 28 ++++++++++++++++--------- src/platform_impl/windows/mod.rs | 16 ++++++++------ src/platform_impl/windows/xinput.rs | 14 ++++++++----- 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index b2fae1e8b9..317c90ef9e 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -14,12 +14,14 @@ fn main() { let mut rumble_left = true; - let deadzone = 0.1; + let deadzone = 0.12; event_loop.run(move |event, _, control_flow| { match event { Event::GamepadEvent(gamepad_handle, event) => match event { - GamepadEvent::Axis{stick: false, ..} => println!("[{:?}] {:#?}", gamepad_handle, event), + GamepadEvent::Axis{stick: false, ..} | + GamepadEvent::Added | + GamepadEvent::Removed => println!("[{:?}] {:#?}", gamepad_handle, event), GamepadEvent::Stick{x_value, y_value, ..} if (x_value.powi(2) + y_value.powi(2)).sqrt() >= deadzone => { println!("[{:?}] {:#?}", gamepad_handle, event); }, @@ -34,7 +36,7 @@ fn main() { }, } }, - _ => () + _ => (), }, Event::WindowEvent { event: WindowEvent::CloseRequested, diff --git a/src/event/device.rs b/src/event/device.rs index 62b1178099..b5ffab6d03 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -172,6 +172,10 @@ impl GamepadHandle { pub fn rumble(&self, left_speed: f64, right_speed: f64) { self.0.rumble(left_speed, right_speed); } + + pub fn port(&self) -> Option { + self.0.port() + } } impl fmt::Debug for MouseId { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 3bdfeed233..a5684b25a0 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1497,7 +1497,7 @@ unsafe extern "system" fn thread_event_target_callback( event = Gamepad::new(handle).map(|gamepad| { let gamepad_handle = GamepadHandle { handle, - rumbler: gamepad.rumbler(), + shared_data: gamepad.shared_data(), }; subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle.clone(), gamepad)); diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index e77280b2c5..0363d5bc2d 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -4,7 +4,7 @@ use winapi::um::winnt::HANDLE; use event::device::GamepadEvent; use platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}; -use platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadRumbler}; +use platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadShared}; #[derive(Debug)] pub enum GamepadType { @@ -13,9 +13,9 @@ pub enum GamepadType { } #[derive(Clone)] -pub enum GamepadRumbler { +pub enum GamepadShared { Raw(()), - XInput(Weak), + XInput(Weak), Dummy, } @@ -55,20 +55,28 @@ impl Gamepad { } } - pub fn rumbler(&self) -> GamepadRumbler { + pub fn shared_data(&self) -> GamepadShared { match self.backend { - GamepadType::Raw(_) => GamepadRumbler::Raw(()), - GamepadType::XInput(ref gamepad) => GamepadRumbler::XInput(gamepad.rumbler()), + GamepadType::Raw(_) => GamepadShared::Raw(()), + GamepadType::XInput(ref gamepad) => GamepadShared::XInput(gamepad.shared_data()), } } } -impl GamepadRumbler { +impl GamepadShared { pub fn rumble(&self, left_speed: f64, right_speed: f64) { match self { - GamepadRumbler::Raw(_) => (), - GamepadRumbler::XInput(ref rumbler) => {rumbler.upgrade().map(|r| r.rumble(left_speed, right_speed));}, - GamepadRumbler::Dummy => (), + GamepadShared::Raw(_) => (), + GamepadShared::XInput(ref data) => {data.upgrade().map(|r| r.rumble(left_speed, right_speed));}, + GamepadShared::Dummy => (), + } + } + + pub fn port(&self) -> Option { + match self { + GamepadShared::Raw(_) | + GamepadShared::Dummy => None, + GamepadShared::XInput(ref data) => data.upgrade().map(|r| r.port()), } } } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index aad3f28728..f18843c7fc 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -23,7 +23,7 @@ use winapi::um::winnt::HANDLE; use window::Icon; pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy}; -pub use self::gamepad::GamepadRumbler; +pub use self::gamepad::GamepadShared; pub use self::monitor::MonitorHandle; pub use self::window::Window; @@ -91,17 +91,17 @@ device_id!(KeyboardId); #[derive(Clone)] pub(crate) struct GamepadHandle { handle: HANDLE, - rumbler: GamepadRumbler, + shared_data: GamepadShared, } -unsafe impl Send for GamepadHandle where GamepadRumbler: Send {} -unsafe impl Sync for GamepadHandle where GamepadRumbler: Sync {} +unsafe impl Send for GamepadHandle where GamepadShared: Send {} +unsafe impl Sync for GamepadHandle where GamepadShared: Sync {} impl GamepadHandle { pub unsafe fn dummy() -> Self { Self { handle: ptr::null_mut(), - rumbler: GamepadRumbler::Dummy, + shared_data: GamepadShared::Dummy, } } @@ -115,7 +115,11 @@ impl GamepadHandle { } pub fn rumble(&self, left_speed: f64, right_speed: f64) { - self.rumbler.rumble(left_speed, right_speed); + self.shared_data.rumble(left_speed, right_speed); + } + + pub fn port(&self) -> Option { + self.shared_data.port() } } diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index f021b25d27..94c2165d08 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -52,11 +52,11 @@ pub struct XInputGamepad { port: DWORD, prev_state: Option, state: Option, - rumbler: Arc, + rumbler: Arc, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct XInputGamepadRumbler { +pub struct XInputGamepadShared { port: DWORD, } @@ -66,7 +66,7 @@ impl XInputGamepad { port, prev_state: None, state: None, - rumbler: Arc::new(XInputGamepadRumbler { + rumbler: Arc::new(XInputGamepadShared { port, }) }) @@ -243,16 +243,20 @@ impl XInputGamepad { events } - pub fn rumbler(&self) -> Weak { + pub fn shared_data(&self) -> Weak { Arc::downgrade(&self.rumbler) } } -impl XInputGamepadRumbler { +impl XInputGamepadShared { pub fn rumble(&self, left_speed: f64, right_speed: f64) { let left_speed = (left_speed * u16::max_value() as f64) as u16; let right_speed = (right_speed * u16::max_value() as f64) as u16; // TODO: We should probably return the status let _ = xinput_set_state(self.port, left_speed, right_speed); } + + pub fn port(&self) -> u8 { + self.port as _ + } } From 81eb1964a4a32b8e2fd8e667c4407ada40a58648 Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 02:03:58 -0500 Subject: [PATCH 21/34] Fix xinput controller hot swapping --- src/platform_impl/windows/xinput.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 94c2165d08..cf65d946b4 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -248,6 +248,16 @@ impl XInputGamepad { } } +impl Drop for XInputGamepad { + fn drop(&mut self) { + // For some reason, if you don't attempt to retrieve the xinput gamepad state at least once + // after the gamepad was disconnected, all future attempts to read from a given port (even + // if a controller was plugged back into said port) will fail! I don't know why that happens, + // but this fixes it, so 🤷. + xinput_get_state(self.port).ok(); + } +} + impl XInputGamepadShared { pub fn rumble(&self, left_speed: f64, right_speed: f64) { let left_speed = (left_speed * u16::max_value() as f64) as u16; From a44aafae39fbf728a0059192812995465fe6e2ef Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 02:29:43 -0500 Subject: [PATCH 22/34] Add functions for enumerating attached devices --- examples/gamepad.rs | 5 ++ src/event/device.rs | 13 +++ src/platform_impl/windows/event_loop.rs | 109 +++++++++++++++++++----- src/platform_impl/windows/mod.rs | 14 ++- 4 files changed, 115 insertions(+), 26 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 317c90ef9e..0e86361243 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -14,6 +14,11 @@ fn main() { let mut rumble_left = true; + println!("enumerating gamepads:"); + for gamepad in winit::event::device::GamepadHandle::enumerate(&event_loop) { + println!(" gamepad {:?}", gamepad); + } + let deadzone = 0.12; event_loop.run(move |event, _, control_flow| { diff --git a/src/event/device.rs b/src/event/device.rs index b5ffab6d03..aebdeb8439 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -1,5 +1,6 @@ use platform_impl; use event::{AxisId, ButtonId, ElementState, KeyboardInput, MouseButton}; +use event_loop::EventLoop; use std::fmt; /// A hint suggesting the type of button that was pressed. @@ -146,6 +147,10 @@ impl MouseId { pub unsafe fn dummy() -> Self { MouseId(platform_impl::MouseId::dummy()) } + + pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { + platform_impl::MouseId::enumerate(&event_loop.event_loop) + } } impl KeyboardId { @@ -157,6 +162,10 @@ impl KeyboardId { pub unsafe fn dummy() -> Self { KeyboardId(platform_impl::KeyboardId::dummy()) } + + pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { + platform_impl::KeyboardId::enumerate(&event_loop.event_loop) + } } impl GamepadHandle { @@ -169,6 +178,10 @@ impl GamepadHandle { GamepadHandle(platform_impl::GamepadHandle::dummy()) } + pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { + platform_impl::GamepadHandle::enumerate(&event_loop.event_loop) + } + pub fn rumble(&self, left_speed: f64, right_speed: f64) { self.0.rumble(left_speed, right_speed); } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a5684b25a0..24f4f29c1e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -77,11 +77,10 @@ impl SubclassInput { struct ThreadMsgTargetSubclassInput { shared_data: Rc>, user_event_receiver: Receiver, - active_device_ids: HashMap, } #[derive(Debug)] -enum DeviceId { +pub(crate) enum DeviceId { Mouse(MouseId), Keyboard(KeyboardId), Gamepad(GamepadHandle, Gamepad), @@ -123,6 +122,7 @@ impl EventLoop { runner: RefCell::new(None), buffer: RefCell::new(VecDeque::new()), }, + active_device_ids: RefCell::new(HashMap::default()), }); let (thread_msg_target, thread_msg_sender) = thread_event_target_window(shared_data.clone()); @@ -225,6 +225,56 @@ impl EventLoop { event_send: self.thread_msg_sender.clone(), } } + + fn devices(&self, f: impl FnMut(&DeviceId) -> Option) -> impl '_ + Iterator { + // Flush WM_INPUT and WM_INPUT_DEVICE_CHANGE events so that the active_device_ids list is + // accurate. This is essential to make this function work if called before calling `run` or + // `run_return`. + unsafe { + let mut msg = mem::uninitialized(); + loop { + let result = winuser::PeekMessageW( + &mut msg, + self.window_target.p.thread_msg_target, + winuser::WM_INPUT_DEVICE_CHANGE, + winuser::WM_INPUT, + 1 + ); + if 0 == result { + break; + } + winuser::TranslateMessage(&mut msg); + winuser::DispatchMessageW(&mut msg); + } + } + + self.window_target.p.shared_data.active_device_ids.borrow() + .values() + .filter_map(f) + .collect::>() + .into_iter() + } + + pub fn mouses(&self) -> impl '_ + Iterator { + self.devices(|d| match d { + DeviceId::Mouse(id) => Some(id.clone().into()), + _ => None + }) + } + + pub fn keyboards(&self) -> impl '_ + Iterator { + self.devices(|d| match d { + DeviceId::Keyboard(id) => Some(id.clone().into()), + _ => None + }) + } + + pub fn gamepads(&self) -> impl '_ + Iterator { + self.devices(|d| match d { + DeviceId::Gamepad(handle, _) => Some(handle.clone().into()), + _ => None + }) + } } impl EventLoopWindowTarget { @@ -240,6 +290,7 @@ impl EventLoopWindowTarget { pub(crate) struct SubclassSharedData { pub runner_shared: ELRShared, + pub active_device_ids: RefCell>, } pub(crate) struct ELRShared { @@ -734,7 +785,6 @@ fn thread_event_target_window(shared_data: Rc>) -> (HWN let subclass_input = ThreadMsgTargetSubclassInput { shared_data, user_event_receiver: rx, - active_device_ids: HashMap::default(), }; let input_ptr = Box::into_raw(Box::new(subclass_input)); let subclass_result = commctrl::SetWindowSubclass( @@ -1474,48 +1524,56 @@ unsafe extern "system" fn thread_event_target_callback( match wparam as _ { winuser::GIDC_ARRIVAL => { if let Some(handle_info) = raw_input::get_raw_input_device_info(handle) { - let event: Option>; + let device_and_event: Option<(DeviceId, Event)>; match handle_info { RawDeviceInfo::Mouse(_) => { let mouse_id = MouseId(handle); - subclass_input.active_device_ids.insert(handle, DeviceId::Mouse(mouse_id)); - event = Some(Event::MouseEvent( - mouse_id.into(), - MouseEvent::Added, + device_and_event = Some(( + DeviceId::Mouse(mouse_id), + Event::MouseEvent( + mouse_id.into(), + MouseEvent::Added, + ), )); }, RawDeviceInfo::Keyboard(_) => { let keyboard_id = KeyboardId(handle); - subclass_input.active_device_ids.insert(handle, DeviceId::Keyboard(keyboard_id)); - event = Some(Event::KeyboardEvent( - keyboard_id.into(), - KeyboardEvent::Added, + device_and_event = Some(( + DeviceId::Keyboard(keyboard_id), + Event::KeyboardEvent( + keyboard_id.into(), + KeyboardEvent::Added, + ), )); }, RawDeviceInfo::Hid(_) => { - event = Gamepad::new(handle).map(|gamepad| { + device_and_event = Gamepad::new(handle).map(|gamepad| { let gamepad_handle = GamepadHandle { handle, shared_data: gamepad.shared_data(), }; - subclass_input.active_device_ids.insert(handle, DeviceId::Gamepad(gamepad_handle.clone(), gamepad)); - Event::GamepadEvent( - gamepad_handle.into(), - GamepadEvent::Added, + ( + DeviceId::Gamepad(gamepad_handle.clone(), gamepad), + Event::GamepadEvent( + gamepad_handle.into(), + GamepadEvent::Added, + ), ) }); } } - if let Some(event) = event { + if let Some((device, event)) = device_and_event { + subclass_input.shared_data.active_device_ids.borrow_mut().insert(handle, device); subclass_input.send_event(event); } } }, winuser::GIDC_REMOVAL => { - if let Some(device_id) = subclass_input.active_device_ids.remove(&handle) { + let removed_device = subclass_input.shared_data.active_device_ids.borrow_mut().remove(&handle); + if let Some(device_id) = removed_device { let event = match device_id { DeviceId::Mouse(mouse_id) => Event::MouseEvent( mouse_id.into(), @@ -1644,10 +1702,15 @@ unsafe extern "system" fn thread_event_target_callback( Some(RawInputData::Hid{device_handle, mut raw_hid}) => { let mut gamepad_handle_opt: Option<::event::device::GamepadHandle> = None; let mut events = vec![]; - if let Some(DeviceId::Gamepad(gamepad_handle, ref mut gamepad)) = subclass_input.active_device_ids.get_mut(&device_handle) { - gamepad.update_state(&mut raw_hid.raw_input); - events = gamepad.get_gamepad_events(); - gamepad_handle_opt = Some(gamepad_handle.clone().into()); + + { + let mut devices = subclass_input.shared_data.active_device_ids.borrow_mut(); + let device_id = devices.get_mut(&device_handle); + if let Some(DeviceId::Gamepad(gamepad_handle, ref mut gamepad)) = device_id { + gamepad.update_state(&mut raw_hid.raw_input); + events = gamepad.get_gamepad_events(); + gamepad_handle_opt = Some(gamepad_handle.clone().into()); + } } if let Some(gamepad_handle) = gamepad_handle_opt { diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index f18843c7fc..40931c6c35 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -55,7 +55,7 @@ impl WindowId { } macro_rules! device_id { - ($name:ident) => { + ($name:ident, $enumerate:ident) => { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct $name(HANDLE); @@ -75,6 +75,10 @@ macro_rules! device_id { pub fn handle(&self) -> HANDLE { self.0 } + + pub fn enumerate<'a, T>(event_loop: &'a EventLoop) -> impl 'a + Iterator { + event_loop.$enumerate() + } } impl From<$name> for crate::event::device::$name { @@ -85,8 +89,8 @@ macro_rules! device_id { } } -device_id!(MouseId); -device_id!(KeyboardId); +device_id!(MouseId, mouses); +device_id!(KeyboardId, keyboards); #[derive(Clone)] pub(crate) struct GamepadHandle { @@ -121,6 +125,10 @@ impl GamepadHandle { pub fn port(&self) -> Option { self.shared_data.port() } + + pub fn enumerate<'a, T>(event_loop: &'a EventLoop) -> impl 'a + Iterator { + event_loop.gamepads() + } } impl From for crate::event::device::GamepadHandle { From 8fe2e2c4b220a32cddd9b3c17c768299ed8b795f Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 02:37:27 -0500 Subject: [PATCH 23/34] Clamp input to [0.0, 1.0] on gamepad rumble --- src/platform_impl/windows/xinput.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index cf65d946b4..08ad24f36a 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -260,8 +260,8 @@ impl Drop for XInputGamepad { impl XInputGamepadShared { pub fn rumble(&self, left_speed: f64, right_speed: f64) { - let left_speed = (left_speed * u16::max_value() as f64) as u16; - let right_speed = (right_speed * u16::max_value() as f64) as u16; + let left_speed = (left_speed.max(0.0).min(1.0) * u16::max_value() as f64) as u16; + let right_speed = (right_speed.max(0.0).min(1.0) * u16::max_value() as f64) as u16; // TODO: We should probably return the status let _ = xinput_set_state(self.port, left_speed, right_speed); } From 5334537cac16aa69d5423e76713ce3b5bfbd95b6 Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 13:36:54 -0500 Subject: [PATCH 24/34] Expose gamepad rumble errors --- examples/gamepad.rs | 25 ++---------- examples/gamepad_rumble.rs | 58 ++++++++++++++++++++++++++++ src/event/device.rs | 12 ++++-- src/platform_impl/windows/gamepad.rs | 11 +++--- src/platform_impl/windows/mod.rs | 4 +- src/platform_impl/windows/xinput.rs | 32 ++++++++------- 6 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 examples/gamepad_rumble.rs diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 0e86361243..eb834ba835 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -1,6 +1,6 @@ extern crate winit; use winit::window::WindowBuilder; -use winit::event::{ElementState, Event, WindowEvent}; +use winit::event::{Event, WindowEvent}; use winit::event::device::GamepadEvent; use winit::event_loop::{EventLoop, ControlFlow}; @@ -12,8 +12,6 @@ fn main() { .build(&event_loop) .unwrap(); - let mut rumble_left = true; - println!("enumerating gamepads:"); for gamepad in winit::event::device::GamepadHandle::enumerate(&event_loop) { println!(" gamepad {:?}", gamepad); @@ -24,24 +22,9 @@ fn main() { event_loop.run(move |event, _, control_flow| { match event { Event::GamepadEvent(gamepad_handle, event) => match event { - GamepadEvent::Axis{stick: false, ..} | - GamepadEvent::Added | - GamepadEvent::Removed => println!("[{:?}] {:#?}", gamepad_handle, event), - GamepadEvent::Stick{x_value, y_value, ..} if (x_value.powi(2) + y_value.powi(2)).sqrt() >= deadzone => { - println!("[{:?}] {:#?}", gamepad_handle, event); - }, - GamepadEvent::Button{ state, .. } => { - println!("[{:?}] {:#?}", gamepad_handle, event); - match state { - ElementState::Pressed if rumble_left => gamepad_handle.rumble(1.0, 0.0), - ElementState::Pressed => gamepad_handle.rumble(0.0, 1.0), - ElementState::Released => { - gamepad_handle.rumble(0.0, 0.0); - rumble_left = !rumble_left; - }, - } - }, - _ => (), + GamepadEvent::Axis{stick: true, ..} => (), + GamepadEvent::Stick{x_value, y_value, ..} if (x_value.powi(2) + y_value.powi(2)).sqrt() < deadzone => (), + _ => println!("[{:?}] {:#?}", gamepad_handle, event) }, Event::WindowEvent { event: WindowEvent::CloseRequested, diff --git a/examples/gamepad_rumble.rs b/examples/gamepad_rumble.rs new file mode 100644 index 0000000000..2578f307c7 --- /dev/null +++ b/examples/gamepad_rumble.rs @@ -0,0 +1,58 @@ +extern crate winit; +use winit::event_loop::EventLoop; +use std::time::Instant; + +#[derive(Debug, Clone)] +enum Rumble { + None, + Left, + Right, +} + +fn main() { + let event_loop = EventLoop::new(); + + // You should generally use `GamepadEvent::Added/Removed` to detect gamepads, as doing that will + // allow you to more easily support gamepad hotswapping. However, we're using `enumerate` here + // because it makes this example more concise. + let gamepads = winit::event::device::GamepadHandle::enumerate(&event_loop).collect::>(); + + let rumble_patterns = &[ + (0.5, Rumble::None), + (2.0, Rumble::Left), + (0.5, Rumble::None), + (2.0, Rumble::Right), + ]; + let mut rumble_iter = rumble_patterns.iter().cloned().cycle(); + + let mut active_pattern = rumble_iter.next().unwrap(); + let mut timeout = active_pattern.0; + let mut timeout_start = Instant::now(); + + event_loop.run(move |_, _, _| { + if timeout <= active_pattern.0 { + let t = (timeout / active_pattern.0) * std::f64::consts::PI; + let intensity = t.sin(); + + for g in &gamepads { + let result = match active_pattern.1 { + Rumble::Left => g.rumble(intensity, 0.0), + Rumble::Right => g.rumble(0.0, intensity), + Rumble::None => Ok(()), + }; + + if let Err(e) = result { + println!("Rumble failed: {:?}", e); + } + } + + timeout = (Instant::now() - timeout_start).as_millis() as f64 / 1000.0; + } else { + active_pattern = rumble_iter.next().unwrap(); + println!("Rumbling {:?} for {:?} seconds", active_pattern.1, active_pattern.0); + + timeout = 0.0; + timeout_start = Instant::now(); + } + }); +} diff --git a/src/event/device.rs b/src/event/device.rs index aebdeb8439..0880f7980e 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -1,7 +1,7 @@ use platform_impl; use event::{AxisId, ButtonId, ElementState, KeyboardInput, MouseButton}; use event_loop::EventLoop; -use std::fmt; +use std::{fmt, io}; /// A hint suggesting the type of button that was pressed. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -131,6 +131,12 @@ pub enum GamepadEvent { }, } +#[derive(Debug)] +pub enum RumbleError { + DeviceNotConnected, + OsError(io::Error), +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MouseId(pub(crate) platform_impl::MouseId); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -182,8 +188,8 @@ impl GamepadHandle { platform_impl::GamepadHandle::enumerate(&event_loop.event_loop) } - pub fn rumble(&self, left_speed: f64, right_speed: f64) { - self.0.rumble(left_speed, right_speed); + pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), RumbleError> { + self.0.rumble(left_speed, right_speed) } pub fn port(&self) -> Option { diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index 0363d5bc2d..993ade9ec8 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -2,7 +2,7 @@ use std::sync::Weak; use winapi::um::winnt::HANDLE; -use event::device::GamepadEvent; +use event::device::{GamepadEvent, RumbleError}; use platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}; use platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadShared}; @@ -64,11 +64,12 @@ impl Gamepad { } impl GamepadShared { - pub fn rumble(&self, left_speed: f64, right_speed: f64) { + pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), RumbleError> { match self { - GamepadShared::Raw(_) => (), - GamepadShared::XInput(ref data) => {data.upgrade().map(|r| r.rumble(left_speed, right_speed));}, - GamepadShared::Dummy => (), + GamepadShared::Raw(_) | + GamepadShared::Dummy => Ok(()), + GamepadShared::XInput(ref data) => data.upgrade().map(|r| r.rumble(left_speed, right_speed)) + .unwrap_or(Err(RumbleError::DeviceNotConnected)), } } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 40931c6c35..7345d3a937 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -118,8 +118,8 @@ impl GamepadHandle { self.handle } - pub fn rumble(&self, left_speed: f64, right_speed: f64) { - self.shared_data.rumble(left_speed, right_speed); + pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), ::event::device::RumbleError> { + self.shared_data.rumble(left_speed, right_speed) } pub fn port(&self) -> Option { diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 08ad24f36a..f712473d06 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{io, mem}; use std::sync::{Arc, Weak}; use rusty_xinput::*; @@ -7,7 +7,7 @@ use winapi::um::xinput::*; use event::{ ElementState, - device::{AxisHint, ButtonHint, GamepadEvent, Side}, + device::{AxisHint, ButtonHint, GamepadEvent, RumbleError, Side}, }; use platform_impl::platform::util; @@ -49,10 +49,9 @@ pub fn id_from_name(name: &str) -> Option { #[derive(Debug)] pub struct XInputGamepad { - port: DWORD, + shared: Arc, prev_state: Option, state: Option, - rumbler: Arc, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -63,17 +62,16 @@ pub struct XInputGamepadShared { impl XInputGamepad { pub fn new(port: DWORD) -> Option { XINPUT_GUARD.map(|_| XInputGamepad { - port, + shared: Arc::new(XInputGamepadShared { + port, + }), prev_state: None, state: None, - rumbler: Arc::new(XInputGamepadShared { - port, - }) }) } pub fn update_state(&mut self) -> Option<()> { - let state = xinput_get_state(self.port).ok(); + let state = xinput_get_state(self.shared.port).ok(); if state.is_some() { self.prev_state = mem::replace(&mut self.state, state); Some(()) @@ -244,7 +242,7 @@ impl XInputGamepad { } pub fn shared_data(&self) -> Weak { - Arc::downgrade(&self.rumbler) + Arc::downgrade(&self.shared) } } @@ -254,16 +252,22 @@ impl Drop for XInputGamepad { // after the gamepad was disconnected, all future attempts to read from a given port (even // if a controller was plugged back into said port) will fail! I don't know why that happens, // but this fixes it, so 🤷. - xinput_get_state(self.port).ok(); + xinput_get_state(self.shared.port).ok(); } } impl XInputGamepadShared { - pub fn rumble(&self, left_speed: f64, right_speed: f64) { + pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), RumbleError> { let left_speed = (left_speed.max(0.0).min(1.0) * u16::max_value() as f64) as u16; let right_speed = (right_speed.max(0.0).min(1.0) * u16::max_value() as f64) as u16; - // TODO: We should probably return the status - let _ = xinput_set_state(self.port, left_speed, right_speed); + + let result = xinput_set_state(self.port, left_speed, right_speed); + result.map_err(|e| match e { + XInputUsageError::XInputNotLoaded | + XInputUsageError::InvalidControllerID => panic!("unexpected xinput error {:?}; this is a bug and should be reported", e), + XInputUsageError::DeviceNotConnected => RumbleError::DeviceNotConnected, + XInputUsageError::UnknownError(code) => RumbleError::OsError(io::Error::from_raw_os_error(code as i32)), + }) } pub fn port(&self) -> u8 { From af110f90402b76afd5f308d976815ec933a0e0d8 Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 4 Mar 2019 23:40:11 -0500 Subject: [PATCH 25/34] Add method to check if device is still connected --- src/event/device.rs | 22 +++++++++++++++++----- src/platform_impl/windows/mod.rs | 8 ++++++++ src/platform_impl/windows/raw_input.rs | 6 +----- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/event/device.rs b/src/event/device.rs index 0880f7980e..ecf2e5efc9 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -61,12 +61,12 @@ pub enum AxisHint { LeftTrigger, RightTrigger, - /// This is supposed to have a specialized meaning, referring to a point-of-view switch present on joysticks used - /// for flight simulation. However, Xbox 360 controllers (and their derivatives) use a hat switch for the D-pad. - HatSwitch, + // @francesca64 review: why were these variants here? I don't see how it makes sense for the dpad or hat switch + // to have axes, since they're both four separate buttons. - // @francesca64 review: why were these variants here? I don't see how it makes sense for the dpad - // to have axes, since it's four separate buttons. + // /// This is supposed to have a specialized meaning, referring to a point-of-view switch present on joysticks used + // /// for flight simulation. However, Xbox 360 controllers (and their derivatives) use a hat switch for the D-pad. + // HatSwitch, // DPadUp, // DPadDown, // DPadLeft, @@ -157,6 +157,10 @@ impl MouseId { pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { platform_impl::MouseId::enumerate(&event_loop.event_loop) } + + pub fn is_connected(&self) -> bool { + self.0.is_connected() + } } impl KeyboardId { @@ -172,6 +176,10 @@ impl KeyboardId { pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { platform_impl::KeyboardId::enumerate(&event_loop.event_loop) } + + pub fn is_connected(&self) -> bool { + self.0.is_connected() + } } impl GamepadHandle { @@ -188,6 +196,10 @@ impl GamepadHandle { platform_impl::GamepadHandle::enumerate(&event_loop.event_loop) } + pub fn is_connected(&self) -> bool { + self.0.is_connected() + } + pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), RumbleError> { self.0.rumble(left_speed, right_speed) } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 7345d3a937..532a0c0a5b 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -71,6 +71,10 @@ macro_rules! device_id { raw_input::get_raw_input_device_name(self.0) } + pub fn is_connected(&self) -> bool { + raw_input::get_raw_input_device_info(self.0).is_some() + } + #[inline(always)] pub fn handle(&self) -> HANDLE { self.0 @@ -113,6 +117,10 @@ impl GamepadHandle { raw_input::get_raw_input_device_name(self.handle) } + pub fn is_connected(&self) -> bool { + raw_input::get_raw_input_device_info(self.handle).is_some() + } + #[inline(always)] pub fn handle(&self) -> HANDLE { self.handle diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 26601fe13c..864e1eac90 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -125,11 +125,7 @@ pub fn get_raw_input_device_info(handle: HANDLE) -> Option { &mut data_size, ) } as INT; - // If status == -1, that means we didn't allocate enough space to store all the data. That should - // never happen since we're supposed to get a struct and not a dynamically sized buffer. - assert_ne!(status, -1); - - if status == 0 { + if status <= 0 { return None; } From ffbb7b9d44bb1a93df3d741d1ae153f46b25eb2f Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 5 Mar 2019 00:36:13 -0500 Subject: [PATCH 26/34] Add docs --- src/event.rs | 49 ++++++++++++++--------------- src/event/device.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 26 deletions(-) diff --git a/src/event.rs b/src/event.rs index 84f03b8c88..22d67f88f4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -12,7 +12,7 @@ use window::WindowId; pub mod device; -/// Describes a generic event. +/// A generic event. #[derive(Clone, Debug, PartialEq)] pub enum Event { /// Emitted when the OS sends an event to a winit window. @@ -21,8 +21,11 @@ pub enum Event { event: WindowEvent, }, + /// Emitted when a mouse device has generated input. MouseEvent(device::MouseId, device::MouseEvent), + /// Emitted when a keyboard device has generated input. KeyboardEvent(device::KeyboardId, device::KeyboardEvent), + /// Emitted when a gamepad/joystick device has generated input. GamepadEvent(device::GamepadHandle, device::GamepadEvent), /// Emitted when an event is sent from [`EventLoopProxy::send_event`](../event_loop/struct.EventLoopProxy.html#method.send_event) @@ -60,7 +63,7 @@ impl Event { } } -/// Describes the reason the event loop is resuming. +/// The reason the event loop is resuming. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum StartCause { /// Sent if the time specified by `ControlFlow::WaitUntil` has been reached. Contains the @@ -86,7 +89,7 @@ pub enum StartCause { Init } -/// Describes an event from a `Window`. +/// An event from a `Window`. #[derive(Clone, Debug, PartialEq)] pub enum WindowEvent { /// The size of the window has changed. Contains the client area's new dimensions. @@ -177,7 +180,7 @@ pub enum WindowEvent { HiDpiFactorChanged(f64), } -/// Describes a keyboard input event. +/// A keyboard input event. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct KeyboardInput { @@ -203,36 +206,30 @@ pub struct KeyboardInput { pub modifiers: ModifiersState } -/// Describes touch-screen input state. +/// Touch input state. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TouchPhase { Started, Moved, Ended, - Cancelled + /// The touch has been cancelled by the OS. + /// + /// This can occur in a variety of situations, such as the window losing focus. + Cancelled, } -/// Represents touch event -/// -/// Every time user touches screen new Start event with some finger id is generated. -/// When the finger is removed from the screen End event with same id is generated. -/// -/// For every id there will be at least 2 events with phases Start and End (or Cancelled). -/// There may be 0 or more Move events. -/// +/// A touch event. /// -/// Depending on platform implementation id may or may not be reused by system after End event. -/// -/// Gesture regonizer using this event should assume that Start event received with same id -/// as previously received End event is a new finger and has nothing to do with an old one. -/// -/// Touch may be cancelled if for example window lost focus. +/// Every event is guaranteed to start with a `Start` event, and may end with either an `End` or +/// `Cancelled` event. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Touch { pub phase: TouchPhase, pub location: LogicalPosition, - /// unique identifier of a finger. + /// Unique identifier of a finger. + /// + /// This may get reused by the system after the touch ends. pub id: u64 } @@ -245,7 +242,7 @@ pub type AxisId = u32; /// Identifier for a specific button on some device. pub type ButtonId = u32; -/// Describes the input state of a key. +/// The input state of a key or button. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ElementState { @@ -253,7 +250,7 @@ pub enum ElementState { Released, } -/// Describes a button of a mouse controller. +/// A button on a mouse. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MouseButton { @@ -263,7 +260,7 @@ pub enum MouseButton { Other(u8), } -/// Describes a difference in the mouse scroll wheel state. +/// A difference in the mouse scroll wheel state. #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MouseScrollDelta { @@ -282,7 +279,7 @@ pub enum MouseScrollDelta { PixelDelta(LogicalPosition), } -/// Symbolic name for a keyboard key. +/// Symbolic name of a keyboard key. #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)] #[repr(u32)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -481,7 +478,7 @@ pub enum VirtualKeyCode { Cut, } -/// Represents the current state of the keyboard modifiers +/// The current state of the keyboard modifiers /// /// Each field of this struct represents a modifier and is `true` if this modifier is active. #[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)] diff --git a/src/event/device.rs b/src/event/device.rs index ecf2e5efc9..d2bc66fe12 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -1,3 +1,14 @@ +//! Raw hardware events that are not associated with any particular window. +//! +//! Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person +//! game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because +//! window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs +//! may not match. +//! +//! All attached devices are guaranteed to emit an `Added` event upon the initialization of the event loop. +//! +//! Note that device events are always delivered regardless of window focus. + use platform_impl; use event::{AxisId, ButtonId, ElementState, KeyboardInput, MouseButton}; use event_loop::EventLoop; @@ -73,12 +84,16 @@ pub enum AxisHint { // DPadRight, } +/// A given joystick's side on the gamepad. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Side { Left, Right, } +/// Raw mouse events. +/// +/// See the module-level docs for more information. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub enum MouseEvent { /// A mouse device has been added. @@ -100,47 +115,92 @@ pub enum MouseEvent { Wheel(f64, f64), } +/// Raw keyboard events. +/// +/// See the module-level docs for more information. #[derive(Debug, Copy, Clone, PartialEq, Hash)] pub enum KeyboardEvent { + /// A keyboard device has been added. Added, + /// A keyboard device has been removed. Removed, + /// A key has been pressed or released. Input(KeyboardInput), } +/// Gamepad/joystick events. +/// +/// These can be generated by any of a variety of game controllers, including (but not limited to) +/// gamepads, joysicks, and HOTAS devices. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub enum GamepadEvent { + /// A gamepad/joystick device has been added. Added, + /// A gamepad/joystick device has been removed. Removed, + /// An analog axis value on the gamepad/joystick has changed. + /// + /// Winit does NOT provide [deadzone filtering](https://www.quora.com/What-does-the-term-joystick-deadzone-mean), + /// and such filtering may have to be provided by API users for joystick axes. Axis { axis_id: AxisId, + /// A hint regarding the physical axis that moved. + /// + /// On traditional gamepads (such as an X360 controller) this can be assumed to have a + /// non-`None` value; however, other joystick devices with more varied layouts generally won't + /// provide a value here. + /// + /// TODO: DISCUSS CONTROLLER MAPPING ONCE WE FIGURE OUT WHAT WE'RE DOING THERE. hint: Option, value: f64, + /// Whether or not this axis has also produced a [`GamepadEvent::Stick`] event. stick: bool, }, + /// A two-axis joystick's value has changed. + /// + /// This is mainly provided to assist with deadzone calculation, as proper deadzones should be + /// calculated via the combined distance of each joystick axis from the center of the joystick, + /// rather than per-axis. + /// + /// Note that this is only guaranteed to be emitted for traditionally laid out gamepads. More + /// complex joysticks generally don't report specifics of their layout to the operating system, + /// preventing Winit from automatically aggregating their axis input into two-axis stick events. Stick { + /// The X axis' ID. x_id: AxisId, + /// The Y axis' ID. y_id: AxisId, x_value: f64, y_value: f64, + /// Which joystick side produced this event. side: Side, }, Button { button_id: ButtonId, + /// A hint regarding the location of the button. + /// + /// The caveats on the `Axis.hint` field also apply here. hint: Option, state: ElementState, }, } +/// Error reported if a rumble attempt unexpectedly failed. #[derive(Debug)] pub enum RumbleError { + /// The device is no longer connected. DeviceNotConnected, + /// An unknown OS error has occured. OsError(io::Error), } +/// A typed identifier for a mouse device. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MouseId(pub(crate) platform_impl::MouseId); +/// A typed identifier for a keyboard device. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct KeyboardId(pub(crate) platform_impl::KeyboardId); +/// A handle to a gamepad/joystick device. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct GamepadHandle(pub(crate) platform_impl::GamepadHandle); @@ -154,10 +214,12 @@ impl MouseId { MouseId(platform_impl::MouseId::dummy()) } + /// Enumerate all attached mouse devices. pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { platform_impl::MouseId::enumerate(&event_loop.event_loop) } + /// Check to see if this mouse device is still connected. pub fn is_connected(&self) -> bool { self.0.is_connected() } @@ -173,10 +235,12 @@ impl KeyboardId { KeyboardId(platform_impl::KeyboardId::dummy()) } + /// Enumerate all attached keyboard devices. pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { platform_impl::KeyboardId::enumerate(&event_loop.event_loop) } + /// Check to see if this keyboard device is still connected. pub fn is_connected(&self) -> bool { self.0.is_connected() } @@ -192,18 +256,29 @@ impl GamepadHandle { GamepadHandle(platform_impl::GamepadHandle::dummy()) } + /// Enumerate all attached gamepad/joystick devices. pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { platform_impl::GamepadHandle::enumerate(&event_loop.event_loop) } + /// Check to see if this gamepad/joystick device is still connected. pub fn is_connected(&self) -> bool { self.0.is_connected() } + /// Attempts to set the rumble values for an attached controller. Input values are automatically + /// bound to a [`0.0`, `1.0`] range. + /// + /// Certain gamepads assign different usages to the left and right motors - for example, X360 + /// controllers treat the left motor as a low-frequency rumble and the right motor as a + /// high-frequency rumble. However, this cannot necessarily be assumed for all gamepad devices. + /// + /// Note that, if the given gamepad does not support rumble, no error value gets thrown. pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), RumbleError> { self.0.rumble(left_speed, right_speed) } + /// Gets the port number assigned to the gamepad. pub fn port(&self) -> Option { self.0.port() } From ca6cc128ccb901e750b459d27c6d0239bf2d08fe Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 5 Mar 2019 18:07:15 -0500 Subject: [PATCH 27/34] Rename AxisHint and ButtonHint to GamepadAxis and GamepadButton --- src/event/device.rs | 8 ++-- src/platform_impl/windows/raw_input.rs | 36 +++++++------- src/platform_impl/windows/xinput.rs | 66 +++++++++++++------------- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/event/device.rs b/src/event/device.rs index d2bc66fe12..6db7a91de7 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -16,7 +16,7 @@ use std::{fmt, io}; /// A hint suggesting the type of button that was pressed. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum ButtonHint { +pub enum GamepadButton { Start, Select, @@ -62,7 +62,7 @@ pub enum ButtonHint { /// A hint suggesting the type of axis that moved. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum AxisHint { +pub enum GamepadAxis { LeftStickX, LeftStickY, @@ -151,7 +151,7 @@ pub enum GamepadEvent { /// provide a value here. /// /// TODO: DISCUSS CONTROLLER MAPPING ONCE WE FIGURE OUT WHAT WE'RE DOING THERE. - hint: Option, + axis: Option, value: f64, /// Whether or not this axis has also produced a [`GamepadEvent::Stick`] event. stick: bool, @@ -180,7 +180,7 @@ pub enum GamepadEvent { /// A hint regarding the location of the button. /// /// The caveats on the `Axis.hint` field also apply here. - hint: Option, + button: Option, state: ElementState, }, } diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 864e1eac90..1d6f7584ba 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -55,7 +55,7 @@ use winapi::um::winuser::{ use platform_impl::platform::util; use event::{ ElementState, - device::{AxisHint, GamepadEvent}, + device::{GamepadAxis, GamepadEvent}, }; #[allow(dead_code)] @@ -440,7 +440,7 @@ pub struct Axis { caps: HIDP_VALUE_CAPS, value: f64, prev_value: f64, - hint: Option, + axis: Option, } impl fmt::Debug for Axis { @@ -449,13 +449,13 @@ impl fmt::Debug for Axis { struct Axis { value: f64, prev_value: f64, - hint: Option, + axis: Option, } let axis_proxy = Axis { value: self.value, prev_value: self.prev_value, - hint: self.hint, + axis: self.axis, }; axis_proxy.fmt(f) @@ -520,22 +520,22 @@ impl RawGamepad { caps: axis_cap, value: 0.0, prev_value: 0.0, - hint: None, + axis: None, // @francesca64 when reviewing - where did you get these values? I've commented them // out because the HOTAS controller I've been using to test the raw input backend has // been getting axis hints when it shouldn't, and this seems to be the culprit. // match unsafe { axis_cap.u.Range().UsageMin } { - // 0x30 => Some(AxisHint::LeftStickX), - // 0x31 => Some(AxisHint::LeftStickY), - // 0x32 => Some(AxisHint::RightStickX), - // 0x33 => Some(AxisHint::LeftTrigger), - // 0x34 => Some(AxisHint::RightTrigger), - // 0x35 => Some(AxisHint::RightStickY), - // 0x39 => Some(AxisHint::HatSwitch), - // 0x90 => Some(AxisHint::DPadUp), - // 0x91 => Some(AxisHint::DPadDown), - // 0x92 => Some(AxisHint::DPadRight), - // 0x93 => Some(AxisHint::DPadLeft), + // 0x30 => Some(GamepadAxis::LeftStickX), + // 0x31 => Some(GamepadAxis::LeftStickY), + // 0x32 => Some(GamepadAxis::RightStickX), + // 0x33 => Some(GamepadAxis::LeftTrigger), + // 0x34 => Some(GamepadAxis::RightTrigger), + // 0x35 => Some(GamepadAxis::RightStickY), + // 0x39 => Some(GamepadAxis::HatSwitch), + // 0x90 => Some(GamepadAxis::DPadUp), + // 0x91 => Some(GamepadAxis::DPadDown), + // 0x92 => Some(GamepadAxis::DPadRight), + // 0x93 => Some(GamepadAxis::DPadLeft), // _ => None, // }, }); @@ -656,7 +656,7 @@ impl RawGamepad { let state = if *button { ElementState::Pressed } else { ElementState::Released }; GamepadEvent::Button { button_id: index as _, - hint: None, + button: None, state, } }) @@ -669,7 +669,7 @@ impl RawGamepad { .filter(|&(_, axis)| axis.value != axis.prev_value) .map(|(index, axis)| GamepadEvent::Axis { axis_id: index as _, - hint: axis.hint, + axis: axis.axis, value: axis.value, stick: false, }) diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index f712473d06..081aea7667 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -7,7 +7,7 @@ use winapi::um::xinput::*; use event::{ ElementState, - device::{AxisHint, ButtonHint, GamepadEvent, RumbleError, Side}, + device::{GamepadAxis, GamepadButton, GamepadEvent, RumbleError, Side}, }; use platform_impl::platform::util; @@ -15,21 +15,21 @@ lazy_static! { static ref XINPUT_GUARD: Option<()> = dynamic_load_xinput().ok(); } -static BUTTONS: &[(WORD , u32, ButtonHint)] = &[ - (XINPUT_GAMEPAD_DPAD_UP, 12, ButtonHint::DPadUp), - (XINPUT_GAMEPAD_DPAD_DOWN, 13, ButtonHint::DPadDown), - (XINPUT_GAMEPAD_DPAD_LEFT, 14, ButtonHint::DPadLeft), - (XINPUT_GAMEPAD_DPAD_RIGHT, 15, ButtonHint::DPadRight), - (XINPUT_GAMEPAD_START, 9, ButtonHint::Start), - (XINPUT_GAMEPAD_BACK, 8, ButtonHint::Select), - (XINPUT_GAMEPAD_LEFT_THUMB, 10, ButtonHint::LeftStick), - (XINPUT_GAMEPAD_RIGHT_THUMB, 11, ButtonHint::RightStick), - (XINPUT_GAMEPAD_LEFT_SHOULDER, 4, ButtonHint::LeftShoulder), - (XINPUT_GAMEPAD_RIGHT_SHOULDER, 5, ButtonHint::RightShoulder), - (XINPUT_GAMEPAD_A, 0, ButtonHint::South), - (XINPUT_GAMEPAD_B, 1, ButtonHint::East), - (XINPUT_GAMEPAD_X, 2, ButtonHint::West), - (XINPUT_GAMEPAD_Y, 3, ButtonHint::North), +static BUTTONS: &[(WORD , u32, GamepadButton)] = &[ + (XINPUT_GAMEPAD_DPAD_UP, 12, GamepadButton::DPadUp), + (XINPUT_GAMEPAD_DPAD_DOWN, 13, GamepadButton::DPadDown), + (XINPUT_GAMEPAD_DPAD_LEFT, 14, GamepadButton::DPadLeft), + (XINPUT_GAMEPAD_DPAD_RIGHT, 15, GamepadButton::DPadRight), + (XINPUT_GAMEPAD_START, 9, GamepadButton::Start), + (XINPUT_GAMEPAD_BACK, 8, GamepadButton::Select), + (XINPUT_GAMEPAD_LEFT_THUMB, 10, GamepadButton::LeftStick), + (XINPUT_GAMEPAD_RIGHT_THUMB, 11, GamepadButton::RightStick), + (XINPUT_GAMEPAD_LEFT_SHOULDER, 4, GamepadButton::LeftShoulder), + (XINPUT_GAMEPAD_RIGHT_SHOULDER, 5, GamepadButton::RightShoulder), + (XINPUT_GAMEPAD_A, 0, GamepadButton::South), + (XINPUT_GAMEPAD_B, 1, GamepadButton::East), + (XINPUT_GAMEPAD_X, 2, GamepadButton::West), + (XINPUT_GAMEPAD_Y, 3, GamepadButton::North), ]; pub fn id_from_name(name: &str) -> Option { @@ -90,11 +90,11 @@ impl XInputGamepad { const RIGHT_TRIGGER_ID: u32 = LEFT_TRIGGER_ID + 1; if Some(value) != prev_value { let state = if value { ElementState::Pressed } else { ElementState::Released }; - let (button_id, hint) = match side { - Side::Left => (LEFT_TRIGGER_ID, Some(ButtonHint::LeftTrigger)), - Side::Right => (RIGHT_TRIGGER_ID, Some(ButtonHint::RightTrigger)), + let (button_id, button) = match side { + Side::Left => (LEFT_TRIGGER_ID, Some(GamepadButton::LeftTrigger)), + Side::Right => (RIGHT_TRIGGER_ID, Some(GamepadButton::RightTrigger)), }; - events.push(GamepadEvent::Button{button_id, hint, state}); + events.push(GamepadEvent::Button{button_id, button, state}); } } @@ -130,12 +130,12 @@ impl XInputGamepad { let changed = buttons ^ prev_buttons; let pressed = changed & buttons; let released = changed & prev_buttons; - for &(flag, button_id, hint) in BUTTONS { - let hint = Some(hint); + for &(flag, button_id, button) in BUTTONS { + let button = Some(button); if util::has_flag(pressed, flag) { - events.push(GamepadEvent::Button{button_id, hint, state: ElementState::Pressed}); + events.push(GamepadEvent::Button{button_id, button, state: ElementState::Pressed}); } else if util::has_flag(released, flag) { - events.push(GamepadEvent::Button{button_id, hint, state: ElementState::Released}); + events.push(GamepadEvent::Button{button_id, button, state: ElementState::Released}); } } Self::check_trigger_digital(events, left_trigger, prev_left, Side::Left); @@ -151,13 +151,13 @@ impl XInputGamepad { const LEFT_TRIGGER_ID: u32 = 4; const RIGHT_TRIGGER_ID: u32 = LEFT_TRIGGER_ID + 1; if Some(value) != prev_value { - let (axis_id, hint) = match side { - Side::Left => (LEFT_TRIGGER_ID, Some(AxisHint::LeftTrigger)), - Side::Right => (RIGHT_TRIGGER_ID, Some(AxisHint::RightTrigger)), + let (axis_id, axis) = match side { + Side::Left => (LEFT_TRIGGER_ID, Some(GamepadAxis::LeftTrigger)), + Side::Right => (RIGHT_TRIGGER_ID, Some(GamepadAxis::RightTrigger)), }; events.push(GamepadEvent::Axis{ axis_id, - hint, + axis, value: value as f64 / u8::max_value() as f64, stick: false, }); @@ -170,9 +170,9 @@ impl XInputGamepad { prev_value: Option<(i16, i16)>, stick: Side, ) { - let (id, hint) = match stick { - Side::Left => ((0, 1), (AxisHint::LeftStickX, AxisHint::LeftStickY)), - Side::Right => ((2, 3), (AxisHint::RightStickX, AxisHint::RightStickY)), + let (id, axis) = match stick { + Side::Left => ((0, 1), (GamepadAxis::LeftStickX, GamepadAxis::LeftStickY)), + Side::Right => ((2, 3), (GamepadAxis::RightStickX, GamepadAxis::RightStickY)), }; let prev_x = prev_value.map(|prev| prev.0); let prev_y = prev_value.map(|prev| prev.1); @@ -188,7 +188,7 @@ impl XInputGamepad { if prev_x != Some(value.0) { events.push(GamepadEvent::Axis { axis_id: id.0, - hint: Some(hint.0), + axis: Some(axis.0), value: value_f64.0, stick: true, }); @@ -196,7 +196,7 @@ impl XInputGamepad { if prev_y != Some(value.1) { events.push(GamepadEvent::Axis { axis_id: id.1, - hint: Some(hint.1), + axis: Some(axis.1), value: value_f64.1, stick: true, }); From 8fee08ac14185e8120da740a8bb552f8783422c6 Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 5 Mar 2019 18:33:06 -0500 Subject: [PATCH 28/34] Add CHANGELOG entry --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c0ad7e12..e33378c70c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,18 @@ - Refactored Windows state/flag-setting code. - On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on. - On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area. +- Improve event API documentation. +- Overhaul device event API: + - **Breaking**: `Event::DeviceEvent` split into `MouseEvent`, `KeyboardEvent`, and `GamepadEvent`. + - **Breaking**: Remove `DeviceEvent::Text` variant. + - **Breaking**: `DeviceId` split into `MouseId`, `KeyboardId`, and `GamepadHandle`. + - **Breaking**: Removed device IDs from `WindowEvent` variants. + - Add `enumerate` function on device ID types to list all attached devices of that type. + - Add `is_connected` function on device ID types check if the specified device is still available. + - On Windows, rename `DeviceIdExtWindows` to `DeviceExtWindows`. + - Add `handle` function to retrieve the underlying `HANDLE`. +- On Windows, fix duplicate device events getting sent if Winit managed multiple windows. +- On Windows, raw mouse events now report Mouse4 and Mouse5 presses and releases. - Added gamepad support on Windows via raw input and XInput. # Version 0.18.1 (2018-12-30) From f29b53d75f829ec3faf001ce0a034407192165e6 Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 5 Mar 2019 21:47:37 -0500 Subject: [PATCH 29/34] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e33378c70c..1e3fe45386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,7 +51,7 @@ - **Breaking**: Removed device IDs from `WindowEvent` variants. - Add `enumerate` function on device ID types to list all attached devices of that type. - Add `is_connected` function on device ID types check if the specified device is still available. - - On Windows, rename `DeviceIdExtWindows` to `DeviceExtWindows`. + - **Breaking**: On Windows, rename `DeviceIdExtWindows` to `DeviceExtWindows`. - Add `handle` function to retrieve the underlying `HANDLE`. - On Windows, fix duplicate device events getting sent if Winit managed multiple windows. - On Windows, raw mouse events now report Mouse4 and Mouse5 presses and releases. From d65b9d8cc9878da2cf32129f962f8b5f5a5dd0ab Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 7 Mar 2019 15:52:30 -0500 Subject: [PATCH 30/34] Add HidId and MovedAbsolute --- src/event.rs | 2 + src/event/device.rs | 59 +++++++++++-- src/platform_impl/windows/event_loop.rs | 112 +++++++++++++++--------- src/platform_impl/windows/mod.rs | 1 + 4 files changed, 130 insertions(+), 44 deletions(-) diff --git a/src/event.rs b/src/event.rs index 22d67f88f4..7d21b8ff20 100644 --- a/src/event.rs +++ b/src/event.rs @@ -25,6 +25,7 @@ pub enum Event { MouseEvent(device::MouseId, device::MouseEvent), /// Emitted when a keyboard device has generated input. KeyboardEvent(device::KeyboardId, device::KeyboardEvent), + HidEvent(device::HidId, device::HidEvent), /// Emitted when a gamepad/joystick device has generated input. GamepadEvent(device::GamepadHandle, device::GamepadEvent), @@ -54,6 +55,7 @@ impl Event { WindowEvent{window_id, event} => Ok(WindowEvent{window_id, event}), MouseEvent(id, event) => Ok(MouseEvent(id, event)), KeyboardEvent(id, event) => Ok(KeyboardEvent(id, event)), + HidEvent(id, event) => Ok(HidEvent(id, event)), GamepadEvent(id, event) => Ok(GamepadEvent(id, event)), NewEvents(cause) => Ok(NewEvents(cause)), EventsCleared => Ok(EventsCleared), diff --git a/src/event/device.rs b/src/event/device.rs index 6db7a91de7..526cee3c54 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -10,6 +10,7 @@ //! Note that device events are always delivered regardless of window focus. use platform_impl; +use dpi::PhysicalPosition; use event::{AxisId, ButtonId, ElementState, KeyboardInput, MouseButton}; use event_loop::EventLoop; use std::{fmt, io}; @@ -94,7 +95,7 @@ pub enum Side { /// Raw mouse events. /// /// See the module-level docs for more information. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum MouseEvent { /// A mouse device has been added. Added, @@ -105,12 +106,17 @@ pub enum MouseEvent { state: ElementState, button: MouseButton, }, - /// Change in physical position of a pointing device. + /// Relative change in physical position of a pointing device. /// /// This represents raw, unfiltered physical motion, NOT the position of the mouse. Accordingly, - /// the values provided here are the change in position of the mouse since the previous `Moved` - /// event. - Moved(f64, f64), + /// the values provided here are the change in position of the mouse since the previous + /// `MovedRelative` event. + MovedRelative(f64, f64), + /// Change in absolute position of a pointing device. + /// + /// The `PhysicalPosition` value is the new position of the cursor relative to the desktop. This + /// generally doesn't get output by standard mouse devices, but can get output from tablet devices. + MovedAbsolute(PhysicalPosition), /// Change in rotation of mouse wheel. Wheel(f64, f64), } @@ -128,6 +134,19 @@ pub enum KeyboardEvent { Input(KeyboardInput), } +/// Raw HID event. +/// +/// See the module-level docs for more information. +#[derive(Debug, Clone, PartialEq, Hash)] +pub enum HidEvent { + /// A Human Interface Device device has been added. + Added, + /// A Human Interface Device device has been removed. + Removed, + /// A raw data packet has been received from the Human Interface Device. + Data(Box<[u8]>), +} + /// Gamepad/joystick events. /// /// These can be generated by any of a variety of game controllers, including (but not limited to) @@ -200,6 +219,9 @@ pub struct MouseId(pub(crate) platform_impl::MouseId); /// A typed identifier for a keyboard device. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct KeyboardId(pub(crate) platform_impl::KeyboardId); +/// A typed if for a Human Interface Device. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct HidId(pub(crate) platform_impl::HidId); /// A handle to a gamepad/joystick device. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct GamepadHandle(pub(crate) platform_impl::GamepadHandle); @@ -246,6 +268,27 @@ impl KeyboardId { } } +impl HidId { + /// Returns a dummy `HidId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `HidId`. + /// + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + HidId(platform_impl::HidId::dummy()) + } + + /// Enumerate all attached keyboard devices. + pub fn enumerate(event_loop: &EventLoop) -> impl '_ + Iterator { + platform_impl::HidId::enumerate(&event_loop.event_loop) + } + + /// Check to see if this keyboard device is still connected. + pub fn is_connected(&self) -> bool { + self.0.is_connected() + } +} + impl GamepadHandle { /// Returns a dummy `GamepadHandle`, useful for unit testing. The only guarantee made about the return /// value of this function is that it will always be equal to itself and to future values returned @@ -296,6 +339,12 @@ impl fmt::Debug for KeyboardId { } } +impl fmt::Debug for HidId { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + impl fmt::Debug for GamepadHandle { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.0.fmt(f) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 24f4f29c1e..3222c87404 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -45,13 +45,13 @@ use winapi::um::winnt::{HANDLE, LONG, LPCSTR, SHORT}; use window::WindowId as RootWindowId; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use dpi::{LogicalPosition, LogicalSize, PhysicalSize}; +use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use event::{ MouseButton, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent, - device::{GamepadEvent, KeyboardEvent, MouseEvent} + device::{GamepadEvent, KeyboardEvent, HidEvent, MouseEvent} }; use platform_impl::platform::{ - MouseId, KeyboardId, GamepadHandle, WindowId, + MouseId, KeyboardId, HidId, GamepadHandle, WindowId, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, get_hwnd_scale_factor}, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, @@ -83,6 +83,7 @@ struct ThreadMsgTargetSubclassInput { pub(crate) enum DeviceId { Mouse(MouseId), Keyboard(KeyboardId), + Hid(HidId), Gamepad(GamepadHandle, Gamepad), } @@ -269,6 +270,13 @@ impl EventLoop { }) } + pub fn hids(&self) -> impl '_ + Iterator { + self.devices(|d| match d { + DeviceId::Hid(id) => Some(id.clone().into()), + _ => None + }) + } + pub fn gamepads(&self) -> impl '_ + Iterator { self.devices(|d| match d { DeviceId::Gamepad(handle, _) => Some(handle.clone().into()), @@ -1524,51 +1532,54 @@ unsafe extern "system" fn thread_event_target_callback( match wparam as _ { winuser::GIDC_ARRIVAL => { if let Some(handle_info) = raw_input::get_raw_input_device_info(handle) { - let device_and_event: Option<(DeviceId, Event)>; + let device: DeviceId; + let event: Event; match handle_info { RawDeviceInfo::Mouse(_) => { let mouse_id = MouseId(handle); - device_and_event = Some(( - DeviceId::Mouse(mouse_id), - Event::MouseEvent( - mouse_id.into(), - MouseEvent::Added, - ), - )); + device = DeviceId::Mouse(mouse_id); + event = Event::MouseEvent( + mouse_id.into(), + MouseEvent::Added, + ); }, RawDeviceInfo::Keyboard(_) => { let keyboard_id = KeyboardId(handle); - device_and_event = Some(( - DeviceId::Keyboard(keyboard_id), - Event::KeyboardEvent( - keyboard_id.into(), - KeyboardEvent::Added, - ), - )); + device = DeviceId::Keyboard(keyboard_id); + event = Event::KeyboardEvent( + keyboard_id.into(), + KeyboardEvent::Added, + ); }, RawDeviceInfo::Hid(_) => { - device_and_event = Gamepad::new(handle).map(|gamepad| { - let gamepad_handle = GamepadHandle { - handle, - shared_data: gamepad.shared_data(), - }; - - ( - DeviceId::Gamepad(gamepad_handle.clone(), gamepad), - Event::GamepadEvent( + match Gamepad::new(handle) { + Some(gamepad) => { + let gamepad_handle = GamepadHandle { + handle, + shared_data: gamepad.shared_data(), + }; + + device = DeviceId::Gamepad(gamepad_handle.clone(), gamepad); + event = Event::GamepadEvent( gamepad_handle.into(), GamepadEvent::Added, - ), - ) - }); + ); + }, + None => { + let hid_id = HidId(handle); + device = DeviceId::Hid(hid_id.into()); + event = Event::HidEvent( + hid_id.into(), + HidEvent::Added, + ); + } + } } } - if let Some((device, event)) = device_and_event { - subclass_input.shared_data.active_device_ids.borrow_mut().insert(handle, device); - subclass_input.send_event(event); - } + subclass_input.shared_data.active_device_ids.borrow_mut().insert(handle, device); + subclass_input.send_event(event); } }, winuser::GIDC_REMOVAL => { @@ -1583,6 +1594,10 @@ unsafe extern "system" fn thread_event_target_callback( keyboard_id.into(), KeyboardEvent::Removed, ), + DeviceId::Hid(hid_id) => Event::HidEvent( + hid_id.into(), + HidEvent::Removed, + ), DeviceId::Gamepad(gamepad_handle, _) => { Event::GamepadEvent( gamepad_handle.into(), @@ -1606,7 +1621,19 @@ unsafe extern "system" fn thread_event_target_callback( Some(RawInputData::Mouse{device_handle, raw_mouse}) => { let mouse_handle = MouseId(device_handle).into(); - if util::has_flag(raw_mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + if util::has_flag(raw_mouse.usFlags, winuser::MOUSE_MOVE_ABSOLUTE) { + let x = raw_mouse.lLastX as f64; + let y = raw_mouse.lLastY as f64; + + if x != 0.0 || y != 0.0 { + subclass_input.send_event( + Event::MouseEvent( + mouse_handle, + MouseEvent::MovedAbsolute(PhysicalPosition{ x, y }), + ) + ); + } + } else if util::has_flag(raw_mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { let x = raw_mouse.lLastX as f64; let y = raw_mouse.lLastY as f64; @@ -1614,7 +1641,7 @@ unsafe extern "system" fn thread_event_target_callback( subclass_input.send_event( Event::MouseEvent( mouse_handle, - MouseEvent::Moved(x, y), + MouseEvent::MovedRelative(x, y), ) ); } @@ -1701,20 +1728,20 @@ unsafe extern "system" fn thread_event_target_callback( }, Some(RawInputData::Hid{device_handle, mut raw_hid}) => { let mut gamepad_handle_opt: Option<::event::device::GamepadHandle> = None; - let mut events = vec![]; + let mut gamepad_events = vec![]; { let mut devices = subclass_input.shared_data.active_device_ids.borrow_mut(); let device_id = devices.get_mut(&device_handle); if let Some(DeviceId::Gamepad(gamepad_handle, ref mut gamepad)) = device_id { gamepad.update_state(&mut raw_hid.raw_input); - events = gamepad.get_gamepad_events(); + gamepad_events = gamepad.get_gamepad_events(); gamepad_handle_opt = Some(gamepad_handle.clone().into()); } } if let Some(gamepad_handle) = gamepad_handle_opt { - for gamepad_event in events { + for gamepad_event in gamepad_events { subclass_input.send_event( Event::GamepadEvent( gamepad_handle.clone(), @@ -1722,6 +1749,13 @@ unsafe extern "system" fn thread_event_target_callback( ) ); } + } else { + subclass_input.send_event( + Event::HidEvent( + HidId(device_handle).into(), + HidEvent::Data(raw_hid.raw_input) + ) + ); } }, None => () diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 532a0c0a5b..9a68812cc8 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -95,6 +95,7 @@ macro_rules! device_id { device_id!(MouseId, mouses); device_id!(KeyboardId, keyboards); +device_id!(HidId, hids); #[derive(Clone)] pub(crate) struct GamepadHandle { From b3f03c33ad09227e2608e25eef1558251f031294 Mon Sep 17 00:00:00 2001 From: Osspial Date: Wed, 19 Jun 2019 18:55:42 -0400 Subject: [PATCH 31/34] Fix xinput deprecation warnings --- src/platform_impl/windows/xinput.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index e973280970..84c4fc976b 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -14,7 +14,7 @@ use crate::{ }; lazy_static! { - static ref XINPUT_GUARD: Option<()> = dynamic_load_xinput().ok(); + static ref XINPUT_HANDLE: Option = XInputHandle::load_default().ok(); } static BUTTONS: &[(WORD , u32, GamepadButton)] = &[ @@ -63,7 +63,7 @@ pub struct XInputGamepadShared { impl XInputGamepad { pub fn new(port: DWORD) -> Option { - XINPUT_GUARD.map(|_| XInputGamepad { + XINPUT_HANDLE.as_ref().map(|_| XInputGamepad { shared: Arc::new(XInputGamepadShared { port, }), @@ -73,7 +73,7 @@ impl XInputGamepad { } pub fn update_state(&mut self) -> Option<()> { - let state = xinput_get_state(self.shared.port).ok(); + let state = XINPUT_HANDLE.as_ref().and_then(|h| h.get_state(self.shared.port).ok()); if state.is_some() { self.prev_state = mem::replace(&mut self.state, state); Some(()) @@ -254,7 +254,7 @@ impl Drop for XInputGamepad { // after the gamepad was disconnected, all future attempts to read from a given port (even // if a controller was plugged back into said port) will fail! I don't know why that happens, // but this fixes it, so 🤷. - xinput_get_state(self.shared.port).ok(); + XINPUT_HANDLE.as_ref().and_then(|h| h.get_state(self.shared.port).ok()); } } @@ -263,7 +263,7 @@ impl XInputGamepadShared { let left_speed = (left_speed.max(0.0).min(1.0) * u16::max_value() as f64) as u16; let right_speed = (right_speed.max(0.0).min(1.0) * u16::max_value() as f64) as u16; - let result = xinput_set_state(self.port, left_speed, right_speed); + let result = XINPUT_HANDLE.as_ref().unwrap().set_state(self.port, left_speed, right_speed); result.map_err(|e| match e { XInputUsageError::XInputNotLoaded | XInputUsageError::InvalidControllerID => panic!("unexpected xinput error {:?}; this is a bug and should be reported", e), From 0bc48d600422d139851e93172c65ad442894b4ba Mon Sep 17 00:00:00 2001 From: Osspial Date: Wed, 19 Jun 2019 22:03:31 -0400 Subject: [PATCH 32/34] Add ability to retrieve gamepad battery level --- examples/gamepad.rs | 23 ++++++++++++++--------- src/event/device.rs | 19 +++++++++++++++++++ src/platform_impl/windows/gamepad.rs | 10 +++++++++- src/platform_impl/windows/mod.rs | 15 ++++++++++----- src/platform_impl/windows/xinput.rs | 19 ++++++++++++++++++- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index eb834ba835..81bd9ad583 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -14,22 +14,27 @@ fn main() { println!("enumerating gamepads:"); for gamepad in winit::event::device::GamepadHandle::enumerate(&event_loop) { - println!(" gamepad {:?}", gamepad); + println!(" gamepad={:?}\tport={:?}\tbattery level={:?}", gamepad, gamepad.port(), gamepad.battery_level()); } let deadzone = 0.12; event_loop.run(move |event, _, control_flow| { match event { - Event::GamepadEvent(gamepad_handle, event) => match event { - GamepadEvent::Axis{stick: true, ..} => (), - GamepadEvent::Stick{x_value, y_value, ..} if (x_value.powi(2) + y_value.powi(2)).sqrt() < deadzone => (), - _ => println!("[{:?}] {:#?}", gamepad_handle, event) + Event::GamepadEvent(gamepad_handle, event) => { + match event { + // Discard any Axis events that has a corresponding Stick event. + GamepadEvent::Axis{stick: true, ..} => (), + + // Discard any Stick event that falls inside the stick's deadzone. + GamepadEvent::Stick{x_value, y_value, ..} + if (x_value.powi(2) + y_value.powi(2)).sqrt() < deadzone + => (), + + _ => println!("[{:?}] {:#?}", gamepad_handle, event) + } }, - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit, _ => () } }); diff --git a/src/event/device.rs b/src/event/device.rs index 3fd0f1361f..c268891585 100644 --- a/src/event/device.rs +++ b/src/event/device.rs @@ -327,6 +327,25 @@ impl GamepadHandle { pub fn port(&self) -> Option { self.0.port() } + + /// Gets the controller's battery level. + /// + /// If the controller doesn't report a battery level, this returns `None`. + pub fn battery_level(&self) -> Option { + self.0.battery_level() + } +} + +/// TODO: IS THIS THE RIGHT ABSTRACTION FOR ALL PLATFORMS? +/// This is exposed in its current form because it's what Microsoft does for XInput, and that's my +/// (@Osspial's) main point of reference. If you're implementing this on a different platform and +/// that platform exposes battery level differently, please bring it up in the tracking issue! +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum BatteryLevel { + Empty, + Low, + Medium, + Full } impl fmt::Debug for MouseId { diff --git a/src/platform_impl/windows/gamepad.rs b/src/platform_impl/windows/gamepad.rs index d599d4ea05..9983bcf377 100644 --- a/src/platform_impl/windows/gamepad.rs +++ b/src/platform_impl/windows/gamepad.rs @@ -3,7 +3,7 @@ use std::sync::Weak; use winapi::um::winnt::HANDLE; use crate::{ - event::device::{GamepadEvent, RumbleError}, + event::device::{BatteryLevel, GamepadEvent, RumbleError}, platform_impl::platform::raw_input::{get_raw_input_device_name, RawGamepad}, platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadShared}, }; @@ -82,4 +82,12 @@ impl GamepadShared { GamepadShared::XInput(ref data) => data.upgrade().map(|r| r.port()), } } + + pub fn battery_level(&self) -> Option { + match self { + GamepadShared::Raw(_) | + GamepadShared::Dummy => None, + GamepadShared::XInput(ref data) => data.upgrade().and_then(|r| r.battery_level()), + } + } } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 6de8e7d196..68ef14154b 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -27,6 +27,7 @@ pub use self::monitor::MonitorHandle; pub use self::window::Window; use crate::window::Icon; +use crate::event::device; #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { @@ -81,12 +82,12 @@ macro_rules! device_id { self.0 } - pub fn enumerate<'a, T>(event_loop: &'a EventLoop) -> impl 'a + Iterator { + pub fn enumerate<'a, T>(event_loop: &'a EventLoop) -> impl 'a + Iterator { event_loop.$enumerate() } } - impl From<$name> for crate::event::device::$name { + impl From<$name> for device::$name { fn from(platform_id: $name) -> Self { Self(platform_id) } @@ -130,7 +131,7 @@ impl GamepadHandle { self.handle } - pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), crate::event::device::RumbleError> { + pub fn rumble(&self, left_speed: f64, right_speed: f64) -> Result<(), device::RumbleError> { self.shared_data.rumble(left_speed, right_speed) } @@ -138,12 +139,16 @@ impl GamepadHandle { self.shared_data.port() } - pub fn enumerate<'a, T>(event_loop: &'a EventLoop) -> impl 'a + Iterator { + pub fn battery_level(&self) -> Option { + self.shared_data.battery_level() + } + + pub fn enumerate<'a, T>(event_loop: &'a EventLoop) -> impl 'a + Iterator { event_loop.gamepads() } } -impl From for crate::event::device::GamepadHandle { +impl From for device::GamepadHandle { fn from(platform_id: GamepadHandle) -> Self { Self(platform_id) } diff --git a/src/platform_impl/windows/xinput.rs b/src/platform_impl/windows/xinput.rs index 84c4fc976b..c499b2e40a 100644 --- a/src/platform_impl/windows/xinput.rs +++ b/src/platform_impl/windows/xinput.rs @@ -9,7 +9,7 @@ use crate::{ platform_impl::platform::util, event::{ ElementState, - device::{GamepadAxis, GamepadButton, GamepadEvent, RumbleError, Side}, + device::{BatteryLevel, GamepadAxis, GamepadButton, GamepadEvent, RumbleError, Side}, }, }; @@ -275,4 +275,21 @@ impl XInputGamepadShared { pub fn port(&self) -> u8 { self.port as _ } + + pub fn battery_level(&self) -> Option { + use rusty_xinput::BatteryLevel as XBatteryLevel; + + let battery_info = XINPUT_HANDLE.as_ref().unwrap().get_gamepad_battery_information(self.port).ok()?; + match battery_info.battery_type { + BatteryType::ALKALINE | + BatteryType::NIMH => match battery_info.battery_level { + XBatteryLevel::EMPTY => Some(BatteryLevel::Empty), + XBatteryLevel::LOW => Some(BatteryLevel::Low), + XBatteryLevel::MEDIUM => Some(BatteryLevel::Medium), + XBatteryLevel::FULL => Some(BatteryLevel::Full), + _ => None + }, + _ => None + } + } } From 592d891a56d96b572c371a16146a923420807189 Mon Sep 17 00:00:00 2001 From: Osspial Date: Wed, 19 Jun 2019 22:05:26 -0400 Subject: [PATCH 33/34] Fix weird imports in gamepad example --- examples/gamepad.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 81bd9ad583..a0e3886043 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -1,7 +1,7 @@ extern crate winit; use winit::window::WindowBuilder; use winit::event::{Event, WindowEvent}; -use winit::event::device::GamepadEvent; +use winit::event::device::{GamepadEvent, GamepadHandle}; use winit::event_loop::{EventLoop, ControlFlow}; fn main() { @@ -13,7 +13,7 @@ fn main() { .unwrap(); println!("enumerating gamepads:"); - for gamepad in winit::event::device::GamepadHandle::enumerate(&event_loop) { + for gamepad in GamepadHandle::enumerate(&event_loop) { println!(" gamepad={:?}\tport={:?}\tbattery level={:?}", gamepad, gamepad.port(), gamepad.battery_level()); } From 7bb1118e06ef161bd722f6091e41dee383a45167 Mon Sep 17 00:00:00 2001 From: Osspial Date: Wed, 19 Jun 2019 22:13:44 -0400 Subject: [PATCH 34/34] Update CHANGELOG.md --- CHANGELOG.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd1780fbfa..5b2cf9001b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,13 +37,13 @@ - `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit. - Rename `MonitorId` to `MonitorHandle`. - Removed `serde` implementations from `ControlFlow`. -- On Wayland, add `set_wayland_theme()` to control client decoration color theme -- Added serde serialization to `os::unix::XWindowType`. -- **Breaking:** Remove the `icon_loading` feature and the associated `image` dependency. -- On Windows, fix malformed function pointer typecast that could invoke undefined behavior. -- Refactored Windows state/flag-setting code. -- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on. -- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area. +- Rename several functions to improve both internal consistency and compliance with Rust API guidelines. +- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now. +- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers. +- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor. +- On Wayland, the window now exists even if nothing has been drawn. +- On Windows, fix initial dimensions of a fullscreen window. + - Improve event API documentation. - Overhaul device event API: - **Breaking**: `Event::DeviceEvent` split into `MouseEvent`, `KeyboardEvent`, and `GamepadEvent`. @@ -57,12 +57,6 @@ - On Windows, fix duplicate device events getting sent if Winit managed multiple windows. - On Windows, raw mouse events now report Mouse4 and Mouse5 presses and releases. - Added gamepad support on Windows via raw input and XInput. -- Rename several functions to improve both internal consistency and compliance with Rust API guidelines. -- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now. -- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers. -- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor. -- On Wayland, the window now exists even if nothing has been drawn. -- On Windows, fix initial dimensions of a fullscreen window. # Version 0.19.1 (2019-04-08)