Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul device events API and add gamepad support on Windows #804

Merged
merged 39 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
700e65e
Initial implementation
francesca64 Nov 4, 2018
b552370
Corrected RAWINPUT buffer sizing
francesca64 Nov 4, 2018
8c7f720
Mostly complete XInput implementation
francesca64 Nov 4, 2018
666a391
XInput triggers
francesca64 Nov 4, 2018
da57daa
Add preliminary CHANGELOG entry.
francesca64 Nov 4, 2018
89b6cb7
match unix common API to evl 2.0
elinorbgr Nov 15, 2018
ae6ca46
wayland: eventloop2.0
elinorbgr Nov 15, 2018
e67c500
Merge onto EL2.0 branch
Osspial Feb 15, 2019
7164a69
make EventLoopProxy require T: 'static
elinorbgr Feb 15, 2019
4202b60
Merge branch 'evl2' of https://github.com/vberger/winit into el2-win-joy
Osspial Feb 15, 2019
de20a68
Revamp device event API, as well as several misc. fixes on Windows:
Osspial Feb 24, 2019
8d2826c
Add MouseEvent documentation and Device ID debug passthrough
Osspial Feb 24, 2019
abea210
Improve type safety on get_raw_input_data
Osspial Mar 1, 2019
eb6d43e
Remove button_id field from MouseEvent::Button in favor of utton
Osspial Mar 1, 2019
2f4e18a
Remove regex dependency on Windows
Osspial Mar 1, 2019
e76f47e
Remove axis filtering in XInput
Osspial Mar 1, 2019
7775524
Make gamepads not use lazy_static
Osspial Mar 2, 2019
a3468c3
Publicly expose gamepad rumble
Osspial Mar 3, 2019
437ead3
Unstack DeviceEvent and fix examples/tests
Osspial Mar 3, 2019
36b95e2
Add HANDLE retrieval method to DeviceExtWindows
Osspial Mar 3, 2019
b48bd27
Add distinction between non-joystick axes and joystick axes.
Osspial Mar 4, 2019
0b0066b
Add ability to get gamepad port
Osspial Mar 4, 2019
81eb196
Fix xinput controller hot swapping
Osspial Mar 4, 2019
a44aafa
Add functions for enumerating attached devices
Osspial Mar 4, 2019
8fe2e2c
Clamp input to [0.0, 1.0] on gamepad rumble
Osspial Mar 4, 2019
5334537
Expose gamepad rumble errors
Osspial Mar 4, 2019
af110f9
Add method to check if device is still connected
Osspial Mar 5, 2019
ffbb7b9
Add docs
Osspial Mar 5, 2019
ca6cc12
Rename AxisHint and ButtonHint to GamepadAxis and GamepadButton
Osspial Mar 5, 2019
17a9eae
Merge branch 'eventloop-2.0' into el2-win-joy
Osspial Mar 5, 2019
8fee08a
Add CHANGELOG entry
Osspial Mar 5, 2019
f29b53d
Update CHANGELOG.md
Osspial Mar 6, 2019
d65b9d8
Add HidId and MovedAbsolute
Osspial Mar 7, 2019
1e589d9
Merge branch 'eventloop-2.0' into el2-win-joy
Osspial May 29, 2019
ef5bdd5
Merge branch 'master' into el2-win-joy
Osspial Jun 19, 2019
b3f03c3
Fix xinput deprecation warnings
Osspial Jun 19, 2019
0bc48d6
Add ability to retrieve gamepad battery level
Osspial Jun 20, 2019
592d891
Fix weird imports in gamepad example
Osspial Jun 20, 2019
7bb1118
Update CHANGELOG.md
Osspial Jun 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Publicly expose gamepad rumble
  • Loading branch information
Osspial committed Mar 3, 2019
commit a3468c3c4e0d7592a83c72bed9d45cb737c5a8dd
25 changes: 19 additions & 6 deletions examples/gamepad.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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;
},
}
},
_ => ()
},
Expand Down
8 changes: 6 additions & 2 deletions src/event/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
60 changes: 33 additions & 27 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1495,8 +1495,11 @@ unsafe extern "system" fn thread_event_target_callback<T>(
},
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(),
Expand Down Expand Up @@ -1640,38 +1643,41 @@ unsafe extern "system" fn thread_event_target_callback<T>(
}
},
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 => ()
Expand Down
27 changes: 23 additions & 4 deletions src/platform_impl/windows/gamepad.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
use std::rc::Weak;

use winapi::um::winnt::HANDLE;

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};
use platform_impl::platform::xinput::{self, XInputGamepad, XInputGamepadRumbler};

#[derive(Debug)]
pub enum GamepadType {
Raw(RawGamepad),
XInput(XInputGamepad),
}

#[derive(Clone)]
pub enum GamepadRumbler {
Raw(()),
XInput(Weak<XInputGamepadRumbler>),
Dummy,
}

#[derive(Debug)]
pub struct Gamepad {
handle: HANDLE,
Expand Down Expand Up @@ -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()),
}
}
}
Expand All @@ -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 => (),
}
}
}
70 changes: 69 additions & 1 deletion src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ 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;
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;

Expand Down Expand Up @@ -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<String> {
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<GamepadHandle> 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<Ordering> {
self.handle.partial_cmp(&other.handle)
}
}

impl Hash for GamepadHandle {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
}
}
10 changes: 5 additions & 5 deletions src/platform_impl/windows/raw_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
// }
}
30 changes: 24 additions & 6 deletions src/platform_impl/windows/xinput.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::mem;
use std::rc::{Rc, Weak};

use rusty_xinput::*;
use winapi::shared::minwindef::{DWORD, WORD};
Expand Down Expand Up @@ -55,22 +56,31 @@ enum Side {

#[derive(Debug)]
pub struct XInputGamepad {
id: DWORD,
port: DWORD,
prev_state: Option<XInputState>,
state: Option<XInputState>,
rumbler: Rc<XInputGamepadRumbler>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct XInputGamepadRumbler {
port: DWORD,
}

impl XInputGamepad {
pub fn new(id: DWORD) -> Option<Self> {
pub fn new(port: DWORD) -> Option<Self> {
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(())
Expand Down Expand Up @@ -218,8 +228,16 @@ impl XInputGamepad {
events
}

pub fn rumble(&mut self, left_speed: u16, right_speed: u16) {
pub fn rumbler(&self) -> Weak<XInputGamepadRumbler> {
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);
}
}