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
Expose gamepad rumble errors
  • Loading branch information
Osspial committed Mar 4, 2019
commit 5334537cac16aa69d5423e76713ce3b5bfbd95b6
25 changes: 4 additions & 21 deletions examples/gamepad.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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);
Expand All @@ -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,
Expand Down
58 changes: 58 additions & 0 deletions examples/gamepad_rumble.rs
Original file line number Diff line number Diff line change
@@ -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::<Vec<_>>();

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();
}
});
}
12 changes: 9 additions & 3 deletions src/event/device.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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<u8> {
Expand Down
11 changes: 6 additions & 5 deletions src/platform_impl/windows/gamepad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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)),
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
Expand Down
32 changes: 18 additions & 14 deletions src/platform_impl/windows/xinput.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::mem;
use std::{io, mem};
use std::sync::{Arc, Weak};

use rusty_xinput::*;
Expand All @@ -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;

Expand Down Expand Up @@ -49,10 +49,9 @@ pub fn id_from_name(name: &str) -> Option<DWORD> {

#[derive(Debug)]
pub struct XInputGamepad {
port: DWORD,
shared: Arc<XInputGamepadShared>,
prev_state: Option<XInputState>,
state: Option<XInputState>,
rumbler: Arc<XInputGamepadShared>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand All @@ -63,17 +62,16 @@ pub struct XInputGamepadShared {
impl XInputGamepad {
pub fn new(port: DWORD) -> Option<Self> {
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(())
Expand Down Expand Up @@ -244,7 +242,7 @@ impl XInputGamepad {
}

pub fn shared_data(&self) -> Weak<XInputGamepadShared> {
Arc::downgrade(&self.rumbler)
Arc::downgrade(&self.shared)
}
}

Expand All @@ -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 {
Expand Down