├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── apitest.rs └── filter_test.rs └── src ├── devices.rs ├── event.rs ├── joystick.rs ├── keyboard.rs ├── lib.rs ├── manager.rs ├── mouse.rs ├── rawinput.rs └── registrar.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug Api Test", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "target/debug/examples/apitest.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true 17 | }, 18 | ] 19 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "multiinput" 5 | version = "0.1.0" 6 | dependencies = [ 7 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "winapi" 12 | version = "0.3.8" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | dependencies = [ 15 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "winapi-i686-pc-windows-gnu" 21 | version = "0.4.0" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "winapi-x86_64-pc-windows-gnu" 26 | version = "0.4.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [metadata] 30 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 31 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 32 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 33 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multiinput" 3 | version = "0.1.0" 4 | authors = ["Alex Jones "] 5 | description = """ 6 | A pure rust gamepad/mouse/keyboard input library based off of rawinput for the windows platform 7 | """ 8 | documentation = "http://jonesey13.github.io/multiinput-rust/doc/multiinput/index.html" 9 | homepage = "https://github.com/Jonesey13/multiinput-rust" 10 | repository = "https://github.com/Jonesey13/multiinput-rust" 11 | readme = "README.md" 12 | license = "MIT" 13 | keywords = ["keyboard", "mouse", "gamepad", "hid"] 14 | 15 | [target.'cfg(windows)'.dependencies] 16 | winapi = { version = "0.3", features = ["winuser", "basetsd", "hidpi", "winnt", "libloaderapi", "fileapi", "hidsdi", "handleapi"] } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multiinput-rust 2 | 3 | [Documentation](http://jonesey13.github.io/multiinput-rust/doc/multiinput/index.html) 4 | 5 | A [windows rawinput](https://docs.microsoft.com/en-us/windows/win32/inputdev/raw-input) library for mice/keyboards/joysticks for use with rust. 6 | 7 | The original purpose of this library was to help me learn how the art of binding dll's to rust and to allow the use of joysticks in game development (e.g. alongside the glium library). Eventually this was used to develop games that had a separate mice for each player. 8 | 9 | ## Key Features 10 | 11 | * Can differentiate between different keyboards/mice. 12 | * It is intended to be single-purpose and lightweight and can be integrated with other libraries without interference (this is done by having a hidden background input window running). 13 | * In principle this approach could support all HID devices, provide input to devices (e.g. force feedback) and should be able to break the 4 device limit on Xinput controllers. 14 | 15 | ## Known Limitations 16 | * Some track pads are not picked up 17 | * The application can crash if the wrong drivers are installed for a device (e.g. a joystick) 18 | * XInput support is limited (see the docs for details) -------------------------------------------------------------------------------- /examples/apitest.rs: -------------------------------------------------------------------------------- 1 | extern crate multiinput; 2 | 3 | use multiinput::*; 4 | fn main() { 5 | let mut manager = RawInputManager::new().unwrap(); 6 | manager.register_devices(DeviceType::Joysticks(XInputInclude::True)); 7 | manager.register_devices(DeviceType::Keyboards); 8 | manager.register_devices(DeviceType::Mice); 9 | manager.print_device_list(); 10 | let devices = manager.get_device_list(); 11 | println!("{:?}", devices); 12 | 'outer: loop { 13 | if let Some(event) = manager.get_event() { 14 | match event { 15 | RawEvent::KeyboardEvent(_, KeyId::Escape, State::Pressed) => break 'outer, 16 | _ => (), 17 | } 18 | println!("{:?}", event); 19 | } else { 20 | std::thread::sleep(std::time::Duration::from_millis(10)); 21 | } 22 | } 23 | println!("Finishing"); 24 | } 25 | -------------------------------------------------------------------------------- /examples/filter_test.rs: -------------------------------------------------------------------------------- 1 | extern crate multiinput; 2 | 3 | use multiinput::*; 4 | fn main() { 5 | let mut manager = RawInputManager::new().unwrap(); 6 | manager.register_devices(DeviceType::Joysticks(XInputInclude::True)); 7 | manager.register_devices(DeviceType::Keyboards); 8 | manager.register_devices(DeviceType::Mice); 9 | let devices = manager.get_device_list(); 10 | 11 | //Filter to pickup events from the first keyboard only 12 | let keyboard = devices.keyboards.first().unwrap(); 13 | manager.filter_devices(vec![keyboard.name.clone()]); 14 | //manager.unfilter_devices(); 15 | 16 | println!("{:?}", devices); 17 | 'outer: loop { 18 | if let Some(event) = manager.get_event() { 19 | match event { 20 | RawEvent::KeyboardEvent(_, KeyId::Escape, State::Pressed) => break 'outer, 21 | _ => (), 22 | } 23 | println!("{:?}", event); 24 | } else { 25 | std::thread::sleep(std::time::Duration::from_millis(10)); 26 | } 27 | } 28 | println!("Finishing"); 29 | } 30 | -------------------------------------------------------------------------------- /src/devices.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::collections::HashMap; 3 | use std::fmt; 4 | use winapi::shared::hidpi::{HIDP_BUTTON_CAPS, HIDP_CAPS, HIDP_VALUE_CAPS}; 5 | use winapi::um::winnt::HANDLE; 6 | use winapi::um::winuser::RID_DEVICE_INFO; 7 | 8 | #[derive(Clone)] 9 | pub struct MouseInfo { 10 | pub name: String, 11 | pub handle: HANDLE, 12 | pub serial: Option, 13 | pub info: RID_DEVICE_INFO, 14 | } 15 | 16 | impl fmt::Debug for MouseInfo { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 | f.debug_struct("Mouse Info") 19 | .field("name", &self.name) 20 | .field("handle", &self.handle) 21 | .field("serial", &self.serial) 22 | .finish() 23 | } 24 | } 25 | 26 | #[derive(Clone, Debug)] 27 | pub struct MouseDisplayInfo { 28 | pub name: String, 29 | pub serial: Option, 30 | } 31 | 32 | impl From for MouseDisplayInfo { 33 | fn from(mouse: MouseInfo) -> Self { 34 | Self { 35 | name: mouse.name, 36 | serial: mouse.serial 37 | } 38 | } 39 | } 40 | 41 | #[derive(Clone)] 42 | pub struct KeyboardInfo { 43 | pub name: String, 44 | pub handle: HANDLE, 45 | pub serial: Option, 46 | pub info: RID_DEVICE_INFO, 47 | } 48 | 49 | impl fmt::Debug for KeyboardInfo { 50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 51 | f.debug_struct("Keyboard Info") 52 | .field("name", &self.name) 53 | .field("handle", &self.handle) 54 | .field("serial", &self.serial) 55 | .finish() 56 | } 57 | } 58 | 59 | #[derive(Clone, Debug)] 60 | pub struct KeyboardDisplayInfo { 61 | pub name: String, 62 | pub serial: Option, 63 | } 64 | 65 | impl From for KeyboardDisplayInfo { 66 | fn from(keyboard: KeyboardInfo) -> Self { 67 | Self { 68 | name: keyboard.name, 69 | serial: keyboard.serial 70 | } 71 | } 72 | } 73 | 74 | #[derive(Clone)] 75 | pub struct JoystickInfo { 76 | pub name: String, 77 | pub handle: HANDLE, 78 | pub serial: Option, 79 | pub info: RID_DEVICE_INFO, 80 | pub caps: HIDP_CAPS, 81 | pub button_caps: Vec, 82 | pub value_caps: Vec, 83 | pub preparsed_data: Vec, 84 | pub state: JoystickState, 85 | pub is_360_controller: bool, 86 | } 87 | 88 | impl fmt::Debug for JoystickInfo { 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 90 | f.debug_struct("Joystick Info") 91 | .field("name", &self.name) 92 | .field("handle", &self.handle) 93 | .field("serial", &self.serial) 94 | .field("360 Controller?", &self.is_360_controller) 95 | .finish() 96 | } 97 | } 98 | 99 | #[derive(Clone, Debug)] 100 | pub struct JoystickDisplayInfo { 101 | pub name: String, 102 | pub serial: Option, 103 | } 104 | 105 | impl From for JoystickDisplayInfo { 106 | fn from(joystick: JoystickInfo) -> Self { 107 | Self { 108 | name: joystick.name, 109 | serial: joystick.serial 110 | } 111 | } 112 | } 113 | 114 | #[derive(Clone, Debug)] 115 | pub enum DeviceInfo { 116 | Mouse(MouseInfo), 117 | Keyboard(KeyboardInfo), 118 | Joystick(JoystickInfo), 119 | } 120 | 121 | /// Stores Names to All Raw Input Devices 122 | #[derive(Clone)] 123 | pub struct Devices { 124 | pub mice: Vec, 125 | pub keyboards: Vec, 126 | pub joysticks: Vec, 127 | pub device_map: HashMap, 128 | pub original_device_map: HashMap, 129 | } 130 | 131 | impl Devices { 132 | pub fn new() -> Devices { 133 | Devices { 134 | mice: Vec::new(), 135 | keyboards: Vec::new(), 136 | joysticks: Vec::new(), 137 | device_map: HashMap::new(), 138 | original_device_map: HashMap::new(), 139 | } 140 | } 141 | } 142 | 143 | impl Devices { 144 | pub fn filter_device_map(&mut self, device_filter: HashSet) { 145 | self.device_map = HashMap::new(); 146 | 147 | for (pos, mouse) in self.mice.iter().enumerate() { 148 | if device_filter.contains(&mouse.name) { 149 | self.device_map.insert(mouse.handle, pos); 150 | } 151 | } 152 | for (pos, keyboard) in self.keyboards.iter().enumerate() { 153 | if device_filter.contains(&keyboard.name) { 154 | self.device_map.insert(keyboard.handle, pos); 155 | } 156 | } 157 | for (pos, joystick) in self.joysticks.iter().enumerate() { 158 | if device_filter.contains(&joystick.name) { 159 | self.device_map.insert(joystick.handle, pos); 160 | } 161 | } 162 | } 163 | 164 | pub fn reset_device_map(&mut self) { 165 | self.device_map = self.original_device_map.clone(); 166 | } 167 | } 168 | 169 | /// Striped down version of devices fit for sharing across threads 170 | #[derive(Clone, Debug)] 171 | pub struct DevicesDisplayInfo { 172 | pub mice: Vec, 173 | pub keyboards: Vec, 174 | pub joysticks: Vec, 175 | } 176 | 177 | impl From for DevicesDisplayInfo { 178 | fn from(devices: Devices) -> Self { 179 | Self { 180 | mice: devices.mice.iter().cloned().map(|m| m.into()).collect(), 181 | keyboards: devices.keyboards.iter().cloned().map(|m| m.into()).collect(), 182 | joysticks: devices.joysticks.iter().cloned().map(|m| m.into()).collect() 183 | } 184 | } 185 | } 186 | 187 | #[derive(Clone, Debug)] 188 | pub struct JoystickState { 189 | pub button_states: Vec, 190 | pub axis_states: Axes, 191 | pub hatswitch: Option, 192 | pub raw_axis_states: RawAxes, 193 | } 194 | 195 | impl JoystickState { 196 | pub fn new( 197 | p_button_caps: Vec, 198 | p_value_caps: Vec, 199 | ) -> JoystickState { 200 | unsafe { 201 | let mut button_states: Vec = Vec::new(); 202 | if p_button_caps.len() > 0 { 203 | let ref button_caps = p_button_caps[0]; 204 | let number_of_buttons = 205 | button_caps.u.Range().UsageMax - button_caps.u.Range().UsageMin + 1; 206 | for _ in 0..number_of_buttons { 207 | button_states.push(false); 208 | } 209 | } 210 | let mut axis_states = Axes::new(); 211 | let mut hatswitch: Option = None; 212 | for value_caps in p_value_caps { 213 | if value_caps.u.Range().UsageMin == 0x30 { 214 | axis_states.x = Some(0f64); 215 | } 216 | if value_caps.u.Range().UsageMin == 0x31 { 217 | axis_states.y = Some(0f64); 218 | } 219 | if value_caps.u.Range().UsageMin == 0x32 { 220 | axis_states.z = Some(0f64); 221 | } 222 | if value_caps.u.Range().UsageMin == 0x33 { 223 | axis_states.rx = Some(0f64); 224 | } 225 | if value_caps.u.Range().UsageMin == 0x34 { 226 | axis_states.ry = Some(0f64); 227 | } 228 | if value_caps.u.Range().UsageMin == 0x35 { 229 | axis_states.rz = Some(0f64); 230 | } 231 | if value_caps.u.Range().UsageMin == 0x36 { 232 | axis_states.slider = Some(0f64); 233 | } 234 | if value_caps.u.Range().UsageMin == 0x39 { 235 | hatswitch = Some(HatSwitch::Center); 236 | } 237 | } 238 | JoystickState { 239 | button_states: button_states, 240 | axis_states: axis_states, 241 | hatswitch: hatswitch, 242 | raw_axis_states: RawAxes::new(), 243 | } 244 | } 245 | } 246 | } 247 | 248 | #[derive(Clone, Debug)] 249 | pub struct Axes { 250 | pub x: Option, 251 | pub y: Option, 252 | pub z: Option, 253 | pub rx: Option, 254 | pub ry: Option, 255 | pub rz: Option, 256 | pub slider: Option, 257 | } 258 | 259 | impl Axes { 260 | pub fn new() -> Axes { 261 | Axes { 262 | x: None, 263 | y: None, 264 | z: None, 265 | rx: None, 266 | ry: None, 267 | rz: None, 268 | slider: None, 269 | } 270 | } 271 | } 272 | 273 | #[derive(Clone, Debug)] 274 | pub struct RawAxes { 275 | pub x: u32, 276 | pub y: u32, 277 | pub z: u32, 278 | pub rx: u32, 279 | pub ry: u32, 280 | pub rz: u32, 281 | pub slider: u32, 282 | } 283 | 284 | impl RawAxes { 285 | pub fn new() -> RawAxes { 286 | RawAxes { 287 | x: 0u32, 288 | y: 0u32, 289 | z: 0u32, 290 | rx: 0u32, 291 | ry: 0u32, 292 | rz: 0u32, 293 | slider: 0u32, 294 | } 295 | } 296 | } 297 | 298 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] 299 | pub enum HatSwitch { 300 | Center, 301 | Up, 302 | UpRight, 303 | Right, 304 | DownRight, 305 | Down, 306 | DownLeft, 307 | Left, 308 | UpLeft, 309 | } 310 | -------------------------------------------------------------------------------- /src/event.rs: -------------------------------------------------------------------------------- 1 | use devices::{HatSwitch, JoystickState}; 2 | 3 | /// State of a Key or Button 4 | #[derive(Eq, PartialEq, Clone, Debug)] 5 | pub enum State { 6 | Pressed, 7 | Released, 8 | } 9 | 10 | /// Key Identifier (UK Keyboard Layout) 11 | #[derive(Eq, PartialEq, Hash, Clone, Debug)] 12 | pub enum KeyId { 13 | Escape, 14 | Return, 15 | Backspace, 16 | Left, 17 | Right, 18 | Up, 19 | Down, 20 | Space, 21 | A, 22 | B, 23 | C, 24 | D, 25 | E, 26 | F, 27 | G, 28 | H, 29 | I, 30 | J, 31 | K, 32 | L, 33 | M, 34 | N, 35 | O, 36 | P, 37 | Q, 38 | R, 39 | S, 40 | T, 41 | U, 42 | V, 43 | W, 44 | X, 45 | Y, 46 | Z, 47 | F1, 48 | F2, 49 | F3, 50 | F4, 51 | F5, 52 | F6, 53 | F7, 54 | F8, 55 | F9, 56 | F10, 57 | F11, 58 | F12, 59 | Zero, 60 | One, 61 | Two, 62 | Three, 63 | Four, 64 | Five, 65 | Six, 66 | Seven, 67 | Eight, 68 | Nine, 69 | Shift, 70 | LeftCtrl, 71 | RightCtrl, 72 | LeftAlt, 73 | RightAlt, 74 | CapsLock, 75 | Pause, 76 | PageUp, 77 | PageDown, 78 | PrintScreen, 79 | Insert, 80 | End, 81 | Home, 82 | Delete, 83 | Add, 84 | Subtract, 85 | Multiply, 86 | Separator, 87 | Decimal, 88 | Divide, 89 | BackTick, 90 | BackSlash, 91 | ForwardSlash, 92 | Plus, 93 | Minus, 94 | FullStop, 95 | Comma, 96 | Tab, 97 | Numlock, 98 | LeftSquareBracket, 99 | RightSquareBracket, 100 | SemiColon, 101 | Apostrophe, 102 | Hash, 103 | } 104 | 105 | /// Mouse Buttons 106 | #[derive(Eq, PartialEq, Hash, Clone, Debug)] 107 | pub enum MouseButton { 108 | Left, 109 | Right, 110 | Middle, 111 | Button4, 112 | Button5, 113 | } 114 | 115 | #[derive(Eq, PartialEq, Hash, Clone, Debug)] 116 | pub enum Axis { 117 | X, 118 | Y, 119 | Z, 120 | RX, 121 | RY, 122 | RZ, 123 | SLIDER, 124 | } 125 | 126 | /// Event types 127 | /// 128 | /// The usize entry acts as a device ID unique to each DeviceType (Mouse, Keyboard, Hid). 129 | /// Keyboard press events repeat when a key is held down. 130 | #[derive(Clone, Debug)] 131 | pub enum RawEvent { 132 | MouseButtonEvent(usize, MouseButton, State), 133 | MouseMoveEvent(usize, i32, i32), 134 | MouseWheelEvent(usize, f32), 135 | KeyboardEvent(usize, KeyId, State), 136 | JoystickButtonEvent(usize, usize, State), 137 | JoystickAxisEvent(usize, Axis, f64), 138 | JoystickHatSwitchEvent(usize, HatSwitch), 139 | } 140 | 141 | impl JoystickState { 142 | pub fn compare_states(&self, other_state: JoystickState, id: usize) -> Vec { 143 | let mut output: Vec = Vec::new(); 144 | for (index, (&press_state, _)) in self 145 | .button_states 146 | .iter() 147 | .zip(other_state.button_states.iter()) 148 | .enumerate() 149 | .filter(|&(_, (&a, &b))| a != b) 150 | { 151 | output.push(RawEvent::JoystickButtonEvent( 152 | id, 153 | index, 154 | if press_state { 155 | State::Released 156 | } else { 157 | State::Pressed 158 | }, 159 | )); 160 | } 161 | if self.raw_axis_states.x != other_state.raw_axis_states.x { 162 | if let Some(value) = other_state.axis_states.x { 163 | output.push(RawEvent::JoystickAxisEvent(id, Axis::X, value)); 164 | } 165 | } 166 | if self.raw_axis_states.y != other_state.raw_axis_states.y { 167 | if let Some(value) = other_state.axis_states.y { 168 | output.push(RawEvent::JoystickAxisEvent(id, Axis::Y, value)); 169 | } 170 | } 171 | if self.raw_axis_states.z != other_state.raw_axis_states.z { 172 | if let Some(value) = other_state.axis_states.z { 173 | output.push(RawEvent::JoystickAxisEvent(id, Axis::Z, value)); 174 | } 175 | } 176 | if self.raw_axis_states.rx != other_state.raw_axis_states.rx { 177 | if let Some(value) = other_state.axis_states.rx { 178 | output.push(RawEvent::JoystickAxisEvent(id, Axis::RX, value)); 179 | } 180 | } 181 | if self.raw_axis_states.ry != other_state.raw_axis_states.ry { 182 | if let Some(value) = other_state.axis_states.ry { 183 | output.push(RawEvent::JoystickAxisEvent(id, Axis::RY, value)); 184 | } 185 | } 186 | if self.raw_axis_states.rz != other_state.raw_axis_states.rz { 187 | if let Some(value) = other_state.axis_states.rz { 188 | output.push(RawEvent::JoystickAxisEvent(id, Axis::RZ, value)); 189 | } 190 | } 191 | if self.raw_axis_states.slider != other_state.raw_axis_states.slider { 192 | if let Some(value) = other_state.axis_states.slider { 193 | output.push(RawEvent::JoystickAxisEvent(id, Axis::SLIDER, value)); 194 | } 195 | } 196 | if let Some(value_other) = other_state.hatswitch { 197 | if let Some(value_self) = self.hatswitch.clone() { 198 | if value_self != value_other { 199 | output.push(RawEvent::JoystickHatSwitchEvent(id, value_other)); 200 | } 201 | } 202 | } 203 | output 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/joystick.rs: -------------------------------------------------------------------------------- 1 | use devices::{HatSwitch, JoystickInfo, JoystickState}; 2 | use event::RawEvent; 3 | use std::mem::{transmute, MaybeUninit}; 4 | use winapi::shared::hidpi::{ 5 | HidP_GetUsageValue, HidP_GetUsages, HidP_Input, HIDP_STATUS_INCOMPATIBLE_REPORT_ID, 6 | HIDP_STATUS_SUCCESS, HIDP_STATUS_INVALID_REPORT_LENGTH, HIDP_STATUS_INVALID_REPORT_TYPE, PHIDP_PREPARSED_DATA, 7 | HIDP_STATUS_BUFFER_TOO_SMALL, HIDP_STATUS_INVALID_PREPARSED_DATA, HIDP_STATUS_USAGE_NOT_FOUND 8 | }; 9 | use winapi::shared::hidusage::USAGE; 10 | use winapi::shared::ntdef::{LONG, PCHAR, ULONG}; 11 | use winapi::um::winuser::RAWHID; 12 | 13 | pub unsafe fn garbage_vec(size: usize) -> Vec { 14 | let mut v = Vec::with_capacity(size); 15 | v.set_len(size); 16 | v 17 | } 18 | 19 | pub fn process_joystick_data( 20 | raw_data: &RAWHID, 21 | id: usize, 22 | hid_info: &mut JoystickInfo, 23 | ) -> Vec { 24 | let mut output: Vec = Vec::new(); 25 | unsafe { 26 | let mut button_states: Vec = vec![]; 27 | if let Some(button_caps) = hid_info.button_caps.iter().nth(0) { 28 | let number_of_buttons: ULONG = 29 | (button_caps.u.Range().UsageMax - button_caps.u.Range().UsageMin + 1) as ULONG; 30 | let mut usage: Vec = garbage_vec(number_of_buttons as usize); 31 | let mut number_of_presses: ULONG = number_of_buttons; 32 | 33 | let status = 34 | HidP_GetUsages( 35 | HidP_Input, 36 | button_caps.UsagePage, 37 | 0, 38 | usage.as_mut_ptr(), 39 | &mut number_of_presses, 40 | hid_info.preparsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA, 41 | transmute::<_, PCHAR>(raw_data.bRawData.as_ptr()), 42 | raw_data.dwSizeHid 43 | ); 44 | 45 | assert!(status != HIDP_STATUS_INVALID_REPORT_LENGTH, "Invalid Report Length!"); 46 | assert!(status != HIDP_STATUS_INVALID_REPORT_TYPE, "Invalid Report Type!"); 47 | assert!(status != HIDP_STATUS_BUFFER_TOO_SMALL, "Status Buffer Too Small!"); 48 | assert!(status != HIDP_STATUS_INCOMPATIBLE_REPORT_ID, "Incompatible Report ID!"); 49 | assert!(status != HIDP_STATUS_INVALID_PREPARSED_DATA, "Invalid Preparsed Data!"); 50 | assert!(status != HIDP_STATUS_USAGE_NOT_FOUND, "Usage Not Found!"); 51 | 52 | button_states = vec![false; number_of_buttons as usize]; 53 | for i in 0..number_of_presses as usize { 54 | button_states[(usage[i] - button_caps.u.Range().UsageMin) as usize] = true; 55 | } 56 | } 57 | 58 | let vec_value_caps = hid_info.value_caps.clone(); 59 | 60 | let mut axis_states = hid_info.state.axis_states.clone(); 61 | let mut raw_axis_states = hid_info.state.raw_axis_states.clone(); 62 | let mut hatswitch: Option = None; 63 | 64 | let mut value: ULONG = MaybeUninit::uninit().assume_init(); 65 | let mut derived_value: f64; 66 | for value_caps in vec_value_caps { 67 | let usage_index = value_caps.u.Range().UsageMin; 68 | 69 | let mut logical_max = value_caps.LogicalMax; 70 | let mut logical_min = value_caps.LogicalMin; 71 | 72 | // Xbox Axes 73 | if logical_max == -1 && logical_min == 0 && hid_info.is_360_controller { 74 | logical_max = 65535; 75 | logical_min = 0; 76 | } 77 | 78 | let usage_value_result = HidP_GetUsageValue( 79 | HidP_Input, 80 | value_caps.UsagePage, 81 | 0, 82 | usage_index, 83 | &mut value, 84 | hid_info.preparsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA, 85 | transmute::<_, PCHAR>(raw_data.bRawData.as_ptr()), 86 | raw_data.dwSizeHid, 87 | ); 88 | // If the usage does not match the usage page reported by the device we ignore the result 89 | // (see https://github.com/Jonesey13/multiinput-rust/issues/3) 90 | assert!( 91 | (usage_value_result == HIDP_STATUS_SUCCESS) 92 | || (usage_value_result == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) 93 | ); 94 | if value as i32 > logical_max { 95 | derived_value = (value as i32 - (logical_max - logical_min + 1)) as f64; 96 | } else { 97 | derived_value = value as f64; 98 | } 99 | derived_value = 2f64 * (derived_value - logical_min as f64) 100 | / (logical_max - logical_min) as f64 101 | - 1f64; 102 | if usage_index == 0x30 { 103 | axis_states.x = Some(derived_value); 104 | raw_axis_states.x = value; 105 | } 106 | if usage_index == 0x31 { 107 | axis_states.y = Some(-derived_value); 108 | raw_axis_states.y = value; 109 | } 110 | if usage_index == 0x32 { 111 | axis_states.z = Some(-derived_value); 112 | raw_axis_states.z = value; 113 | } 114 | if usage_index == 0x33 { 115 | axis_states.rx = Some(derived_value); 116 | raw_axis_states.rx = value; 117 | } 118 | if usage_index == 0x34 { 119 | axis_states.ry = Some(derived_value); 120 | raw_axis_states.ry = value; 121 | } 122 | if usage_index == 0x35 { 123 | axis_states.rz = Some(derived_value); 124 | raw_axis_states.rz = value; 125 | } 126 | if usage_index == 0x36 { 127 | axis_states.slider = Some(derived_value); 128 | raw_axis_states.slider = value; 129 | } 130 | if usage_index == 0x39 { 131 | hatswitch = match value as LONG - value_caps.LogicalMin { 132 | 0 => Some(HatSwitch::Up), 133 | 1 => Some(HatSwitch::UpRight), 134 | 2 => Some(HatSwitch::Right), 135 | 3 => Some(HatSwitch::DownRight), 136 | 4 => Some(HatSwitch::Down), 137 | 5 => Some(HatSwitch::DownLeft), 138 | 6 => Some(HatSwitch::Left), 139 | 7 => Some(HatSwitch::UpLeft), 140 | _ => Some(HatSwitch::Center), 141 | }; 142 | } 143 | } 144 | 145 | let newstate = JoystickState { 146 | button_states: button_states, 147 | axis_states: axis_states, 148 | hatswitch: hatswitch, 149 | raw_axis_states: raw_axis_states, 150 | }; 151 | let new_events = hid_info.state.compare_states(newstate.clone(), id); 152 | output.extend(new_events); 153 | hid_info.state = newstate; 154 | } 155 | output 156 | } 157 | -------------------------------------------------------------------------------- /src/keyboard.rs: -------------------------------------------------------------------------------- 1 | use event::{KeyId, RawEvent, State}; 2 | use winapi::um::winuser::*; 3 | 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | enum KeyPos { 6 | Left, 7 | Right, 8 | } 9 | 10 | pub fn process_keyboard_data(raw_data: &RAWKEYBOARD, id: usize) -> Vec { 11 | let mut output: Vec = Vec::new(); 12 | let flags = raw_data.Flags as u32; 13 | let key = raw_data.VKey as i32; 14 | let mut key_opt: Option = None; 15 | let key_state: State; 16 | let key_pos: KeyPos; 17 | if flags & RI_KEY_BREAK != 0 { 18 | key_state = State::Released; 19 | } else { 20 | key_state = State::Pressed; 21 | } 22 | if flags & RI_KEY_E0 == 0 { 23 | key_pos = KeyPos::Left; 24 | } else { 25 | key_pos = KeyPos::Right; 26 | } 27 | if key == VK_TAB { 28 | key_opt = Some(KeyId::Tab); 29 | } 30 | if key == VK_SHIFT { 31 | key_opt = Some(KeyId::Shift); 32 | } 33 | if key == VK_CONTROL && key_pos == KeyPos::Left { 34 | key_opt = Some(KeyId::LeftCtrl); 35 | } 36 | if key == VK_CONTROL && key_pos == KeyPos::Right { 37 | key_opt = Some(KeyId::RightCtrl); 38 | } 39 | if key == VK_MENU && key_pos == KeyPos::Left { 40 | key_opt = Some(KeyId::LeftAlt); 41 | } 42 | if key == VK_MENU && key_pos == KeyPos::Right { 43 | key_opt = Some(KeyId::RightAlt); 44 | } 45 | if key == VK_ESCAPE { 46 | key_opt = Some(KeyId::Escape); 47 | } 48 | if key == VK_RETURN { 49 | key_opt = Some(KeyId::Return); 50 | } 51 | if key == VK_BACK { 52 | key_opt = Some(KeyId::Backspace); 53 | } 54 | if key == VK_LEFT { 55 | key_opt = Some(KeyId::Left); 56 | } 57 | if key == VK_RIGHT { 58 | key_opt = Some(KeyId::Right); 59 | } 60 | if key == VK_UP { 61 | key_opt = Some(KeyId::Up); 62 | } 63 | if key == VK_DOWN { 64 | key_opt = Some(KeyId::Down); 65 | } 66 | if key == VK_SPACE { 67 | key_opt = Some(KeyId::Space); 68 | } 69 | if key == VK_LCONTROL { 70 | key_opt = Some(KeyId::LeftCtrl); 71 | } 72 | if key == VK_RCONTROL { 73 | key_opt = Some(KeyId::RightCtrl); 74 | } 75 | if key == VK_LMENU { 76 | key_opt = Some(KeyId::LeftAlt); 77 | } 78 | if key == VK_RMENU { 79 | key_opt = Some(KeyId::RightAlt); 80 | } 81 | if key == 0x30 { 82 | key_opt = Some(KeyId::Zero); 83 | } 84 | if key == 0x31 { 85 | key_opt = Some(KeyId::One); 86 | } 87 | if key == 0x32 { 88 | key_opt = Some(KeyId::Two); 89 | } 90 | if key == 0x33 { 91 | key_opt = Some(KeyId::Three); 92 | } 93 | if key == 0x34 { 94 | key_opt = Some(KeyId::Four); 95 | } 96 | if key == 0x35 { 97 | key_opt = Some(KeyId::Five); 98 | } 99 | if key == 0x36 { 100 | key_opt = Some(KeyId::Six); 101 | } 102 | if key == 0x37 { 103 | key_opt = Some(KeyId::Seven); 104 | } 105 | if key == 0x38 { 106 | key_opt = Some(KeyId::Eight); 107 | } 108 | if key == 0x39 { 109 | key_opt = Some(KeyId::Nine); 110 | } 111 | if key == 0x41 { 112 | key_opt = Some(KeyId::A); 113 | } 114 | if key == 0x42 { 115 | key_opt = Some(KeyId::B); 116 | } 117 | if key == 0x43 { 118 | key_opt = Some(KeyId::C); 119 | } 120 | if key == 0x44 { 121 | key_opt = Some(KeyId::D); 122 | } 123 | if key == 0x45 { 124 | key_opt = Some(KeyId::E); 125 | } 126 | if key == 0x46 { 127 | key_opt = Some(KeyId::F); 128 | } 129 | if key == 0x47 { 130 | key_opt = Some(KeyId::G); 131 | } 132 | if key == 0x48 { 133 | key_opt = Some(KeyId::H); 134 | } 135 | if key == 0x49 { 136 | key_opt = Some(KeyId::I); 137 | } 138 | if key == 0x4A { 139 | key_opt = Some(KeyId::J); 140 | } 141 | if key == 0x4B { 142 | key_opt = Some(KeyId::K); 143 | } 144 | if key == 0x4C { 145 | key_opt = Some(KeyId::L); 146 | } 147 | if key == 0x4D { 148 | key_opt = Some(KeyId::M); 149 | } 150 | if key == 0x4E { 151 | key_opt = Some(KeyId::N); 152 | } 153 | if key == 0x4F { 154 | key_opt = Some(KeyId::O); 155 | } 156 | if key == 0x50 { 157 | key_opt = Some(KeyId::P); 158 | } 159 | if key == 0x51 { 160 | key_opt = Some(KeyId::Q); 161 | } 162 | if key == 0x52 { 163 | key_opt = Some(KeyId::R); 164 | } 165 | if key == 0x53 { 166 | key_opt = Some(KeyId::S); 167 | } 168 | if key == 0x54 { 169 | key_opt = Some(KeyId::T); 170 | } 171 | if key == 0x55 { 172 | key_opt = Some(KeyId::U); 173 | } 174 | if key == 0x56 { 175 | key_opt = Some(KeyId::V); 176 | } 177 | if key == 0x57 { 178 | key_opt = Some(KeyId::W); 179 | } 180 | if key == 0x58 { 181 | key_opt = Some(KeyId::X); 182 | } 183 | if key == 0x59 { 184 | key_opt = Some(KeyId::Y); 185 | } 186 | if key == 0x5A { 187 | key_opt = Some(KeyId::Z); 188 | } 189 | if key == VK_CAPITAL { 190 | key_opt = Some(KeyId::CapsLock); 191 | } 192 | if key == VK_PAUSE { 193 | key_opt = Some(KeyId::Pause); 194 | } 195 | if key == VK_NEXT { 196 | key_opt = Some(KeyId::PageUp); 197 | } 198 | if key == VK_PRIOR { 199 | key_opt = Some(KeyId::PageDown); 200 | } 201 | if key == VK_SNAPSHOT { 202 | key_opt = Some(KeyId::PrintScreen); 203 | } 204 | if key == VK_INSERT { 205 | key_opt = Some(KeyId::Insert); 206 | } 207 | if key == VK_END { 208 | key_opt = Some(KeyId::End); 209 | } 210 | if key == VK_HOME { 211 | key_opt = Some(KeyId::Home); 212 | } 213 | if key == VK_DELETE { 214 | key_opt = Some(KeyId::Delete); 215 | } 216 | if key == VK_ADD { 217 | key_opt = Some(KeyId::Add); 218 | } 219 | if key == VK_SUBTRACT { 220 | key_opt = Some(KeyId::Subtract); 221 | } 222 | if key == VK_MULTIPLY { 223 | key_opt = Some(KeyId::Multiply); 224 | } 225 | if key == VK_DIVIDE { 226 | key_opt = Some(KeyId::Divide); 227 | } 228 | if key == VK_SEPARATOR { 229 | key_opt = Some(KeyId::Separator); 230 | } 231 | if key == VK_DECIMAL { 232 | key_opt = Some(KeyId::Decimal); 233 | } 234 | if key == VK_F1 { 235 | key_opt = Some(KeyId::F1); 236 | } 237 | if key == VK_F2 { 238 | key_opt = Some(KeyId::F2); 239 | } 240 | if key == VK_F3 { 241 | key_opt = Some(KeyId::F3); 242 | } 243 | if key == VK_F4 { 244 | key_opt = Some(KeyId::F4); 245 | } 246 | if key == VK_F5 { 247 | key_opt = Some(KeyId::F5); 248 | } 249 | if key == VK_F6 { 250 | key_opt = Some(KeyId::F6); 251 | } 252 | if key == VK_F7 { 253 | key_opt = Some(KeyId::F7); 254 | } 255 | if key == VK_F8 { 256 | key_opt = Some(KeyId::F8); 257 | } 258 | if key == VK_F9 { 259 | key_opt = Some(KeyId::F9); 260 | } 261 | if key == VK_F10 { 262 | key_opt = Some(KeyId::F10); 263 | } 264 | if key == VK_F11 { 265 | key_opt = Some(KeyId::F11); 266 | } 267 | if key == VK_F12 { 268 | key_opt = Some(KeyId::F12); 269 | } 270 | if key == VK_NUMLOCK { 271 | key_opt = Some(KeyId::Numlock); 272 | } 273 | if key == VK_OEM_PLUS { 274 | key_opt = Some(KeyId::Plus); 275 | } 276 | if key == VK_OEM_MINUS { 277 | key_opt = Some(KeyId::Minus); 278 | } 279 | if key == VK_OEM_COMMA { 280 | key_opt = Some(KeyId::Comma); 281 | } 282 | if key == VK_OEM_PERIOD { 283 | key_opt = Some(KeyId::FullStop); 284 | } 285 | if key == VK_OEM_1 { 286 | key_opt = Some(KeyId::SemiColon); 287 | } 288 | if key == VK_OEM_2 { 289 | key_opt = Some(KeyId::ForwardSlash); 290 | } 291 | if key == VK_OEM_3 { 292 | key_opt = Some(KeyId::Apostrophe); 293 | } 294 | if key == VK_OEM_4 { 295 | key_opt = Some(KeyId::LeftSquareBracket); 296 | } 297 | if key == VK_OEM_5 { 298 | key_opt = Some(KeyId::BackSlash); 299 | } 300 | if key == VK_OEM_6 { 301 | key_opt = Some(KeyId::RightSquareBracket); 302 | } 303 | if key == VK_OEM_7 { 304 | key_opt = Some(KeyId::Hash); 305 | } 306 | if key == VK_OEM_8 { 307 | key_opt = Some(KeyId::BackTick); 308 | } 309 | 310 | if let Some(key_id) = key_opt { 311 | output.push(RawEvent::KeyboardEvent(id, key_id, key_state)); 312 | } 313 | output 314 | } 315 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | rawinput library for rust development on windows 3 | 4 | # Usage Example 5 | 6 | ```no_run 7 | extern crate multiinput; 8 | 9 | use multiinput::*; 10 | fn main() { 11 | let mut manager = RawInputManager::new().unwrap(); 12 | manager.register_devices(DeviceType::Joysticks(XInputInclude::True)); 13 | manager.register_devices(DeviceType::Keyboards); 14 | manager.register_devices(DeviceType::Mice); 15 | 'outer: loop{ 16 | if let Some(event) = manager.get_event(){ 17 | match event{ 18 | RawEvent::KeyboardEvent(_, KeyId::Escape, State::Pressed) 19 | => break 'outer, 20 | _ => (), 21 | } 22 | println!("{:?}", event); 23 | } 24 | } 25 | println!("Finishing"); 26 | } 27 | ``` 28 | */ 29 | 30 | extern crate winapi; 31 | 32 | pub mod devices; 33 | pub mod event; 34 | mod joystick; 35 | mod keyboard; 36 | pub mod manager; 37 | mod mouse; 38 | mod rawinput; 39 | mod registrar; 40 | 41 | pub use devices::*; 42 | pub use event::*; 43 | pub use manager::*; 44 | -------------------------------------------------------------------------------- /src/manager.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc::TryIter; 2 | use devices::DevicesDisplayInfo; 3 | use devices::{Devices, JoystickState}; 4 | use event::RawEvent; 5 | use std::sync::mpsc::TryRecvError; 6 | use rawinput::{get_event, get_joystick_state}; 7 | use registrar; 8 | use std::time::{SystemTime, UNIX_EPOCH}; 9 | use winapi::shared::minwindef::UINT; 10 | use winapi::shared::windef::HWND; 11 | use winapi::um::libloaderapi::GetModuleHandleW; 12 | use winapi::um::winuser::{ 13 | CreateWindowExW, DefWindowProcW, RegisterClassExW, CW_USEDEFAULT, HWND_MESSAGE, WNDCLASSEXW, 14 | }; 15 | 16 | use std::collections::VecDeque; 17 | use std::ffi::OsStr; 18 | use std::mem; 19 | use std::os::windows::ffi::OsStrExt; 20 | use std::ptr; 21 | use std::thread; 22 | use std::thread::JoinHandle; 23 | use std::collections::HashSet; 24 | use std::iter::FromIterator; 25 | 26 | use std::sync::mpsc::{channel, Receiver, Sender}; 27 | 28 | enum Command { 29 | Register(DeviceType), 30 | FilterDevices(Vec), 31 | UnfilterDevices, 32 | GetEvent, 33 | GetJoystickState(usize), 34 | Finish, 35 | PrintDeviceList, 36 | GetDeviceList, 37 | GetDeviceStats, 38 | } 39 | 40 | /// Types of Raw Input Device 41 | #[derive(PartialEq, Eq, Clone, Hash)] 42 | pub enum DeviceType { 43 | Mice, 44 | Keyboards, 45 | Joysticks(XInputInclude), 46 | } 47 | 48 | /// Denotes if Xbox360 controllers should be used 49 | /// Please Note: Rawinput support for official Xbox360 controllers 50 | /// is very limited (triggers share same axis, no support for 51 | /// rumble or the central X button) 52 | /// Please see https://en.wikipedia.org/wiki/DirectInput#Xbox_360_Controller_support 53 | /// for more details 54 | #[derive(PartialEq, Eq, Clone, Hash)] 55 | pub enum XInputInclude { 56 | True, 57 | False, 58 | } 59 | 60 | #[derive(Default)] 61 | pub struct DeviceStats { 62 | pub number_of_mice: usize, 63 | pub number_of_keyboards: usize, 64 | pub number_of_joysticks: usize, 65 | } 66 | 67 | /// Manages Raw Input Processing 68 | pub struct RawInputManager { 69 | joiner: Option>, 70 | sender: Sender, 71 | receiver: Receiver, 72 | joystick_receiver: Receiver>, 73 | device_info_receiver: Receiver, 74 | device_stats_receiver: Receiver, 75 | } 76 | 77 | impl RawInputManager { 78 | pub fn new() -> Result { 79 | let (tx, rx) = channel(); 80 | let (tx2, rx2) = channel(); 81 | let (tx_joy, rx_joy) = channel(); 82 | let (tx_devices, rx_devices) = channel(); 83 | let (tx_stats, rx_stats) = channel(); 84 | 85 | let joiner = thread::spawn(move || { 86 | let hwnd = setup_message_window(); 87 | let mut event_queue = VecDeque::new(); 88 | let mut devices = Devices::new(); 89 | let mut exit = false; 90 | let mut registrar = registrar::RawInputRegistrar::new(); 91 | while !exit { 92 | match rx.try_recv() { 93 | Err(TryRecvError::Disconnected) => { 94 | panic!("Multinput Thread Unexpectedly Disconnected!") 95 | } 96 | Err(TryRecvError::Empty) => { 97 | std::thread::sleep(std::time::Duration::from_nanos(1)); 98 | } 99 | Ok(Command::Register(thing)) => { 100 | devices = registrar.register_devices(hwnd, thing).unwrap(); 101 | } 102 | Ok(Command::FilterDevices(strings)) => { 103 | devices.filter_device_map(HashSet::from_iter(strings.into_iter())); 104 | } 105 | Ok(Command::UnfilterDevices) => { 106 | devices.reset_device_map(); 107 | } 108 | Ok(Command::GetEvent) => { 109 | if let Some(event) = get_event(&mut event_queue, &mut devices) { 110 | tx2.send(event).unwrap() 111 | } 112 | } 113 | Ok(Command::Finish) => { 114 | exit = true; 115 | } 116 | Ok(Command::GetJoystickState(id)) => { 117 | tx_joy.send(get_joystick_state(&devices, id)).unwrap() 118 | } 119 | Ok(Command::PrintDeviceList) => print_raw_device_list(&devices), 120 | Ok(Command::GetDeviceList) => tx_devices.send(devices.clone().into()).unwrap(), 121 | Ok(Command::GetDeviceStats) => tx_stats.send(get_device_stats(&devices)).unwrap(), 122 | }; 123 | } 124 | }); 125 | Ok(RawInputManager { 126 | joiner: Some(joiner), 127 | sender: tx, 128 | receiver: rx2, 129 | joystick_receiver: rx_joy, 130 | device_stats_receiver: rx_stats, 131 | device_info_receiver: rx_devices 132 | }) 133 | } 134 | 135 | /// Allows Raw Input devices of type device_type to be received from the Input Manager 136 | pub fn register_devices(&mut self, device_type: DeviceType) { 137 | self.sender.send(Command::Register(device_type)).unwrap(); 138 | } 139 | 140 | /// Filters events returned to the list of names provided by the device_names list 141 | /// Warning: you still need to register the corresponding device types beforehand for this to work! 142 | pub fn filter_devices(&mut self, device_names: Vec) { 143 | self.sender.send(Command::FilterDevices(device_names)).unwrap(); 144 | } 145 | 146 | /// Undoes the application of filter_devices() 147 | pub fn unfilter_devices(&mut self) { 148 | self.sender.send(Command::UnfilterDevices).unwrap(); 149 | } 150 | 151 | /// Get Event from the Input Manager 152 | pub fn get_event(&mut self) -> Option { 153 | self.sender.send(Command::GetEvent).unwrap(); 154 | match self.receiver.try_recv() { 155 | Ok(event) => Some(event), 156 | _ => None 157 | } 158 | } 159 | 160 | /// Get All Events from the Input Manager 161 | pub fn get_events(&mut self) -> TryIter { 162 | self.sender.send(Command::GetEvent).unwrap(); 163 | self.receiver.try_iter() 164 | } 165 | 166 | /// Get Joystick State from the Input Manager 167 | pub fn get_joystick_state(&mut self, id: usize) -> Option { 168 | self.sender.send(Command::GetJoystickState(id)).unwrap(); 169 | self.joystick_receiver.recv().unwrap() 170 | } 171 | 172 | /// Print List of Potential Input Devices 173 | pub fn print_device_list(&self) { 174 | self.sender.send(Command::PrintDeviceList).unwrap(); 175 | } 176 | 177 | /// Get Device Stats (number of connected devices) 178 | pub fn get_device_stats(&self) -> DeviceStats { 179 | self.sender.send(Command::GetDeviceStats).unwrap(); 180 | self.device_stats_receiver.recv().unwrap() 181 | } 182 | 183 | /// Get Device list 184 | pub fn get_device_list(&self) -> DevicesDisplayInfo { 185 | self.sender.send(Command::GetDeviceList).unwrap(); 186 | self.device_info_receiver.recv().unwrap() 187 | } 188 | } 189 | 190 | impl Drop for RawInputManager { 191 | fn drop(&mut self) { 192 | self.sender.send(Command::Finish).unwrap(); 193 | self.joiner.take().unwrap().join().unwrap(); 194 | } 195 | } 196 | 197 | fn setup_message_window() -> HWND { 198 | let hwnd: HWND; 199 | unsafe { 200 | let hinstance = GetModuleHandleW(ptr::null()); 201 | if hinstance == ptr::null_mut() { 202 | panic!("Instance Generation Failed"); 203 | } 204 | 205 | let current_time = SystemTime::now(); 206 | let classname_str = format!( 207 | "RawInput Hidden Window - {:?}", 208 | current_time.duration_since(UNIX_EPOCH).unwrap() 209 | ); 210 | 211 | let classname = OsStr::new(&classname_str) 212 | .encode_wide() 213 | .chain(Some(0).into_iter()) 214 | .collect::>(); 215 | 216 | let wcex = WNDCLASSEXW { 217 | cbSize: (mem::size_of::()) as UINT, 218 | cbClsExtra: 0, 219 | cbWndExtra: 0, 220 | hbrBackground: ptr::null_mut(), 221 | hCursor: ptr::null_mut(), 222 | hIcon: ptr::null_mut(), 223 | hIconSm: ptr::null_mut(), 224 | hInstance: hinstance, 225 | lpfnWndProc: Some(DefWindowProcW), 226 | lpszClassName: classname.as_ptr(), 227 | lpszMenuName: ptr::null_mut(), 228 | style: 0, 229 | }; 230 | let a = RegisterClassExW(&wcex); 231 | if a == 0 { 232 | panic!("Registering WindowClass Failed!"); 233 | } 234 | 235 | hwnd = CreateWindowExW( 236 | 0, 237 | classname.as_ptr(), 238 | classname.as_ptr(), 239 | 0, 240 | CW_USEDEFAULT, 241 | CW_USEDEFAULT, 242 | CW_USEDEFAULT, 243 | CW_USEDEFAULT, 244 | HWND_MESSAGE, 245 | ptr::null_mut(), 246 | hinstance, 247 | ptr::null_mut(), 248 | ); 249 | if hwnd.is_null() { 250 | panic!("Window Creation Failed!"); 251 | } 252 | } 253 | hwnd 254 | } 255 | 256 | /// Prints a list of all available raw input devices 257 | fn print_raw_device_list(devices: &Devices) { 258 | println!("Mice:"); 259 | for mouse in &devices.mice { 260 | println!("{:?}", mouse); 261 | } 262 | println!("Keyboards:"); 263 | for keyboard in &devices.keyboards { 264 | println!("{:?}", keyboard); 265 | } 266 | println!("Hids:"); 267 | for joystick in &devices.joysticks { 268 | println!("{:?}", joystick); 269 | } 270 | } 271 | 272 | fn get_device_stats(devices: &Devices) -> DeviceStats { 273 | DeviceStats { 274 | number_of_mice: devices.mice.len(), 275 | number_of_keyboards: devices.keyboards.len(), 276 | number_of_joysticks: devices.joysticks.len(), 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/mouse.rs: -------------------------------------------------------------------------------- 1 | use event::{MouseButton, RawEvent, State}; 2 | use std::mem::transmute_copy; 3 | use winapi::um::winuser::{ 4 | RAWMOUSE, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, RI_MOUSE_BUTTON_5_DOWN, 5 | RI_MOUSE_BUTTON_5_UP, RI_MOUSE_LEFT_BUTTON_DOWN, RI_MOUSE_LEFT_BUTTON_UP, 6 | RI_MOUSE_MIDDLE_BUTTON_DOWN, RI_MOUSE_MIDDLE_BUTTON_UP, RI_MOUSE_RIGHT_BUTTON_DOWN, 7 | RI_MOUSE_RIGHT_BUTTON_UP, RI_MOUSE_WHEEL, 8 | }; 9 | 10 | pub fn process_mouse_data(raw_data: &RAWMOUSE, id: usize) -> Vec { 11 | let cursor = (raw_data.lLastX, raw_data.lLastY); 12 | let buttons = raw_data.usButtonFlags; 13 | let mut output: Vec = Vec::new(); 14 | if buttons & RI_MOUSE_LEFT_BUTTON_DOWN != 0 { 15 | output.push(RawEvent::MouseButtonEvent( 16 | id, 17 | MouseButton::Left, 18 | State::Pressed, 19 | )); 20 | } 21 | if buttons & RI_MOUSE_LEFT_BUTTON_UP != 0 { 22 | output.push(RawEvent::MouseButtonEvent( 23 | id, 24 | MouseButton::Left, 25 | State::Released, 26 | )); 27 | } 28 | if buttons & RI_MOUSE_RIGHT_BUTTON_DOWN != 0 { 29 | output.push(RawEvent::MouseButtonEvent( 30 | id, 31 | MouseButton::Right, 32 | State::Pressed, 33 | )); 34 | } 35 | if buttons & RI_MOUSE_RIGHT_BUTTON_UP != 0 { 36 | output.push(RawEvent::MouseButtonEvent( 37 | id, 38 | MouseButton::Right, 39 | State::Released, 40 | )); 41 | } 42 | if buttons & RI_MOUSE_MIDDLE_BUTTON_DOWN != 0 { 43 | output.push(RawEvent::MouseButtonEvent( 44 | id, 45 | MouseButton::Middle, 46 | State::Pressed, 47 | )); 48 | } 49 | if buttons & RI_MOUSE_MIDDLE_BUTTON_UP != 0 { 50 | output.push(RawEvent::MouseButtonEvent( 51 | id, 52 | MouseButton::Middle, 53 | State::Released, 54 | )); 55 | } 56 | if buttons & RI_MOUSE_BUTTON_4_DOWN != 0 { 57 | output.push(RawEvent::MouseButtonEvent( 58 | id, 59 | MouseButton::Button4, 60 | State::Pressed, 61 | )); 62 | } 63 | if buttons & RI_MOUSE_BUTTON_4_UP != 0 { 64 | output.push(RawEvent::MouseButtonEvent( 65 | id, 66 | MouseButton::Button4, 67 | State::Released, 68 | )); 69 | } 70 | if buttons & RI_MOUSE_BUTTON_5_DOWN != 0 { 71 | output.push(RawEvent::MouseButtonEvent( 72 | id, 73 | MouseButton::Button5, 74 | State::Pressed, 75 | )); 76 | } 77 | if buttons & RI_MOUSE_BUTTON_5_UP != 0 { 78 | output.push(RawEvent::MouseButtonEvent( 79 | id, 80 | MouseButton::Button5, 81 | State::Released, 82 | )); 83 | } 84 | if buttons & RI_MOUSE_WHEEL != 0 { 85 | let wheel_data = raw_data.usButtonData; 86 | let wheel_value = unsafe { (transmute_copy::(&wheel_data) as f32) / 120f32 }; 87 | output.push(RawEvent::MouseWheelEvent(id, wheel_value)); 88 | } 89 | if (cursor.0 != 0) || (cursor.1 != 0) { 90 | output.push(RawEvent::MouseMoveEvent(id, cursor.0, cursor.1)); 91 | } 92 | output 93 | } 94 | -------------------------------------------------------------------------------- /src/rawinput.rs: -------------------------------------------------------------------------------- 1 | use devices::{DeviceInfo, Devices, JoystickInfo, JoystickState, KeyboardInfo, MouseInfo}; 2 | use event::RawEvent; 3 | use joystick::{garbage_vec, process_joystick_data}; 4 | use keyboard::process_keyboard_data; 5 | use mouse::process_mouse_data; 6 | use std::collections::VecDeque; 7 | use std::ffi::OsStr; 8 | use std::ffi::OsString; 9 | use std::mem::MaybeUninit; 10 | use std::os::windows::ffi::OsStrExt; 11 | use std::os::windows::ffi::OsStringExt; 12 | use std::{mem, ptr}; 13 | use winapi::shared::hidpi::{ 14 | HidP_GetButtonCaps, HidP_GetCaps, HidP_GetValueCaps, HidP_Input, HIDP_BUTTON_CAPS, HIDP_CAPS, 15 | HIDP_STATUS_SUCCESS, HIDP_VALUE_CAPS, PHIDP_BUTTON_CAPS, PHIDP_PREPARSED_DATA, 16 | PHIDP_VALUE_CAPS, 17 | }; 18 | use winapi::shared::hidsdi::HidD_GetSerialNumberString; 19 | use winapi::shared::minwindef::{INT, LPVOID, UINT}; 20 | use winapi::um::fileapi::{CreateFileW, OPEN_EXISTING}; 21 | use winapi::um::handleapi::INVALID_HANDLE_VALUE; 22 | use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, PVOID}; 23 | use winapi::um::winuser::{ 24 | GetRawInputBuffer, GetRawInputDeviceInfoW, GetRawInputDeviceList, PRAWINPUT, 25 | PRAWINPUTDEVICELIST, RAWINPUT, RAWINPUTDEVICELIST, RAWINPUTHEADER, RIDI_DEVICEINFO, 26 | RIDI_DEVICENAME, RIDI_PREPARSEDDATA, RID_DEVICE_INFO, RIM_TYPEHID, RIM_TYPEKEYBOARD, 27 | RIM_TYPEMOUSE, 28 | }; 29 | 30 | /// Follows the NEXTRAWINPUTBLOCK macro 31 | unsafe fn next_raw_input_block(array_ptr: &mut *mut u8) { 32 | use std::mem::{size_of, transmute}; 33 | 34 | // Shift by size of RAWINPUT data 35 | let dw_size = (*(*array_ptr as *mut RAWINPUT)).header.dwSize; 36 | (*array_ptr) = (*array_ptr).offset(dw_size as isize); 37 | 38 | // Correct for bit allignment 39 | let mut array_int: usize = transmute(*array_ptr); 40 | array_int = (array_int + size_of::() - 1) & !(size_of::() - 1); 41 | (*array_ptr) = transmute(array_int); 42 | } 43 | 44 | fn read_input_buffer(event_queue: &mut VecDeque, devices: &mut Devices) { 45 | unsafe { 46 | let mut array_alloc: [u8; 16000] = MaybeUninit::uninit().assume_init(); 47 | let mut buffer_size: UINT = 0; 48 | 49 | let mut numberofelements: i32 = GetRawInputBuffer( 50 | ptr::null_mut(), 51 | &mut buffer_size, 52 | mem::size_of::() as UINT, 53 | ) as INT; 54 | 55 | if numberofelements == -1 { 56 | panic!("GetRawInputBuffer Gave Error on First Call!"); 57 | } 58 | buffer_size = 1024; 59 | numberofelements = GetRawInputBuffer( 60 | array_alloc.as_mut_ptr() as PRAWINPUT, 61 | &mut buffer_size, 62 | mem::size_of::() as UINT, 63 | ) as INT; 64 | 65 | if numberofelements == -1 { 66 | panic!("GetRawInputBuffer Gave Error on Second Call!"); 67 | } 68 | 69 | let mut array_ptr = array_alloc.as_mut_ptr(); 70 | 71 | for _ in 0..numberofelements as u32 { 72 | let header = (*(array_ptr as *mut RAWINPUT)).header; 73 | let raw_input = *(array_ptr as *mut RAWINPUT); 74 | next_raw_input_block(&mut array_ptr); 75 | let pos = match devices.device_map.get(&header.hDevice) { 76 | Some(item) => (*item).clone(), 77 | None => continue, 78 | }; 79 | match raw_input.header.dwType { 80 | RIM_TYPEMOUSE => { 81 | event_queue.extend(process_mouse_data(&raw_input.data.mouse(), pos)); 82 | } 83 | RIM_TYPEKEYBOARD => { 84 | event_queue.extend(process_keyboard_data(&raw_input.data.keyboard(), pos)); 85 | } 86 | RIM_TYPEHID => { 87 | event_queue.extend(process_joystick_data( 88 | &raw_input.data.hid(), 89 | pos, 90 | &mut devices.joysticks[pos], 91 | )); 92 | } 93 | _ => (), 94 | } 95 | } 96 | } 97 | } 98 | 99 | pub fn get_event(event_queue: &mut VecDeque, devices: &mut Devices) -> Option { 100 | if event_queue.is_empty() { 101 | read_input_buffer(event_queue, devices); 102 | } 103 | let event = event_queue.pop_front(); 104 | event 105 | } 106 | 107 | pub fn get_joystick_state(devices: &Devices, id: usize) -> Option { 108 | match (&devices.joysticks).get(id) { 109 | None => None, 110 | Some(joy) => Some(joy.state.clone()), 111 | } 112 | } 113 | 114 | /// Produces a Device struct containing ID's to all available raw input Devices 115 | pub fn produce_raw_device_list(incl_360_devices: bool) -> Devices { 116 | let mut device_list = Devices::new(); 117 | unsafe { 118 | let mut buffer: [RAWINPUTDEVICELIST; 1000] = MaybeUninit::uninit().assume_init(); 119 | let mut num_devices: UINT = 0; 120 | let device_list_size = mem::size_of::(); 121 | let mut result = 122 | GetRawInputDeviceList(ptr::null_mut(), &mut num_devices, device_list_size as UINT); 123 | if result == -1i32 as UINT { 124 | panic!("Failed to Get Raw Device List!"); 125 | } 126 | result = GetRawInputDeviceList( 127 | buffer.as_mut_ptr() as PRAWINPUTDEVICELIST, 128 | &mut num_devices, 129 | device_list_size as UINT, 130 | ); 131 | if result == -1i32 as UINT { 132 | panic!("Failed to Get Raw Device List!"); 133 | } 134 | 135 | for pos in 0..result as usize { 136 | let device_ptr = (&mut buffer[pos..(pos + 1)]).as_mut_ptr() as PRAWINPUTDEVICELIST; 137 | let device = *device_ptr; 138 | let device_handle = device.hDevice; 139 | let device_type = device.dwType; 140 | let name = raw_handle_to_name(device_handle); 141 | let hid_handle = match raw_name_to_hid(name.clone()) { 142 | Ok(handle) => handle, 143 | Err(_) => continue, 144 | }; 145 | let serial = get_serial_number(hid_handle); 146 | let device_info_option = get_device_info(device_handle, name, serial); 147 | match device_info_option { 148 | None => continue, 149 | _ => (), 150 | } 151 | let device_info = device_info_option.unwrap(); 152 | match device_type { 153 | RIM_TYPEMOUSE => { 154 | if let DeviceInfo::Mouse(info) = device_info { 155 | device_list 156 | .device_map 157 | .insert(device_handle, device_list.mice.len()); 158 | device_list.mice.push(info); 159 | } else { 160 | panic!("Unreachable!"); 161 | } 162 | } 163 | RIM_TYPEKEYBOARD => { 164 | if let DeviceInfo::Keyboard(info) = device_info { 165 | device_list 166 | .device_map 167 | .insert(device_handle, device_list.keyboards.len()); 168 | device_list.keyboards.push(info); 169 | } else { 170 | panic!("Unreachable!"); 171 | } 172 | } 173 | RIM_TYPEHID => { 174 | if let DeviceInfo::Joystick(info) = device_info { 175 | if info.is_360_controller && !incl_360_devices { 176 | continue; 177 | } 178 | device_list 179 | .device_map 180 | .insert(device_handle, device_list.joysticks.len()); 181 | device_list.joysticks.push(info); 182 | } else { 183 | panic!("Unreachable!"); 184 | } 185 | } 186 | _ => (), 187 | } 188 | } 189 | } 190 | device_list.original_device_map = device_list.device_map.clone(); 191 | device_list 192 | } 193 | 194 | pub unsafe fn raw_handle_to_name(device_handle: HANDLE) -> String { 195 | let mut name_buffer: [u16; 1024] = MaybeUninit::uninit().assume_init(); 196 | let mut name_buffer_size: UINT = 1024; 197 | let result_2 = GetRawInputDeviceInfoW( 198 | device_handle, 199 | RIDI_DEVICENAME, 200 | name_buffer.as_mut_ptr() as LPVOID, 201 | &mut name_buffer_size, 202 | ); 203 | if result_2 == -1i32 as UINT { 204 | return "Cannot obtain device name, continuing...".to_string(); 205 | } 206 | let name_slice = &name_buffer[0..result_2 as usize]; 207 | match OsString::from_wide(name_slice).into_string() { 208 | Ok(something) => something, 209 | Err(_) => panic!("String Conversion Failed"), 210 | } 211 | } 212 | 213 | pub unsafe fn raw_name_to_hid(name: String) -> Result { 214 | let os_name: &OsStr = name.as_ref(); 215 | let mut classname = os_name 216 | .encode_wide() 217 | .chain(Some(0).into_iter()) 218 | .collect::>(); 219 | let hid_handle = CreateFileW( 220 | classname.as_mut_ptr(), 221 | 0, 222 | FILE_SHARE_READ | FILE_SHARE_WRITE, 223 | ptr::null_mut(), 224 | OPEN_EXISTING, 225 | 0, 226 | ptr::null_mut(), 227 | ); 228 | if hid_handle != INVALID_HANDLE_VALUE { 229 | return Ok(hid_handle); 230 | } else { 231 | return Err("Could not get the HID handle of device ".to_string() + &name); 232 | } 233 | } 234 | 235 | pub unsafe fn get_device_info( 236 | handle: HANDLE, 237 | name: String, 238 | serial: Option, 239 | ) -> Option { 240 | let mut data_buffer: [RID_DEVICE_INFO; 1] = MaybeUninit::uninit().assume_init(); 241 | let mut data_buffer_size = mem::size_of::() as u32; 242 | data_buffer[0].cbSize = data_buffer_size; 243 | let result = GetRawInputDeviceInfoW( 244 | handle, 245 | RIDI_DEVICEINFO, 246 | data_buffer.as_mut_ptr() as LPVOID, 247 | &mut data_buffer_size, 248 | ); 249 | assert!(result as INT != -1); 250 | let raw_info = data_buffer[0]; 251 | 252 | return match raw_info.dwType { 253 | RIM_TYPEMOUSE => Some(DeviceInfo::Mouse(MouseInfo { 254 | name: name, 255 | handle: handle, 256 | serial: serial, 257 | info: raw_info, 258 | })), 259 | RIM_TYPEKEYBOARD => Some(DeviceInfo::Keyboard(KeyboardInfo { 260 | name: name, 261 | handle: handle, 262 | serial: serial, 263 | info: raw_info, 264 | })), 265 | RIM_TYPEHID => { 266 | if raw_info.u.hid().usUsagePage != 0x01 267 | || !(raw_info.u.hid().usUsage == 0x04 || raw_info.u.hid().usUsage == 0x05) 268 | { 269 | return None; 270 | } 271 | 272 | let mut preparsed_data_size: UINT = 1024; 273 | assert!( 274 | GetRawInputDeviceInfoW( 275 | handle, 276 | RIDI_PREPARSEDDATA, 277 | ptr::null_mut(), 278 | &mut preparsed_data_size 279 | ) == 0 280 | ); 281 | let mut preparsed_data: Vec = garbage_vec(preparsed_data_size as usize); 282 | assert!( 283 | GetRawInputDeviceInfoW( 284 | handle, 285 | RIDI_PREPARSEDDATA, 286 | preparsed_data.as_mut_ptr() as LPVOID, 287 | &mut preparsed_data_size 288 | ) as i32 289 | >= 0 290 | ); 291 | let mut caps: HIDP_CAPS = MaybeUninit::uninit().assume_init(); 292 | assert!( 293 | HidP_GetCaps( 294 | preparsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA, 295 | &mut caps 296 | ) == HIDP_STATUS_SUCCESS 297 | ); 298 | 299 | let mut caps_length = caps.NumberInputButtonCaps; 300 | let mut p_button_caps: Vec = garbage_vec(caps_length as usize); 301 | 302 | if caps_length != 0 { 303 | assert!( 304 | HidP_GetButtonCaps( 305 | HidP_Input, 306 | p_button_caps.as_mut_ptr() as PHIDP_BUTTON_CAPS, 307 | &mut caps_length, 308 | preparsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA 309 | ) == HIDP_STATUS_SUCCESS 310 | ); 311 | } 312 | 313 | caps_length = caps.NumberInputValueCaps; 314 | let mut p_value_caps: Vec = garbage_vec(caps_length as usize); 315 | 316 | if caps_length != 0 { 317 | assert!( 318 | HidP_GetValueCaps( 319 | HidP_Input, 320 | p_value_caps.as_mut_ptr() as PHIDP_VALUE_CAPS, 321 | &mut caps_length, 322 | preparsed_data.as_mut_ptr() as PHIDP_PREPARSED_DATA 323 | ) == HIDP_STATUS_SUCCESS 324 | ); 325 | } 326 | 327 | let is_360_controller = name.find("IG_") != None; 328 | 329 | Some(DeviceInfo::Joystick(JoystickInfo { 330 | name: name, 331 | handle: handle, 332 | serial: serial, 333 | info: raw_info, 334 | caps: caps, 335 | button_caps: p_button_caps.clone(), 336 | value_caps: p_value_caps.clone(), 337 | preparsed_data: preparsed_data, 338 | state: JoystickState::new(p_button_caps, p_value_caps), 339 | is_360_controller: is_360_controller, 340 | })) 341 | } 342 | _ => panic!("Unreachable!"), 343 | }; 344 | } 345 | 346 | pub unsafe fn get_serial_number(handle: HANDLE) -> Option { 347 | let mut string_buffer: [u16; 128] = [0u16; 128]; 348 | let string_buffer_size = 256; 349 | let result = HidD_GetSerialNumberString( 350 | handle, 351 | string_buffer.as_mut_ptr() as PVOID, 352 | string_buffer_size, 353 | ); 354 | let serial_string_unparsed = match OsString::from_wide(&string_buffer[0..128]).into_string() { 355 | Ok(something) => something, 356 | Err(_) => panic!("String Conversion Failed {}", result), 357 | }; 358 | let mut serial_string: Option = None; 359 | if result == 1 { 360 | let string_front = serial_string_unparsed.find("\0"); 361 | if let Some(string_index) = string_front { 362 | if string_index > 10 { 363 | serial_string = Some(String::from(&serial_string_unparsed[0..string_index])); 364 | } 365 | } 366 | } 367 | serial_string 368 | } 369 | -------------------------------------------------------------------------------- /src/registrar.rs: -------------------------------------------------------------------------------- 1 | use devices::Devices; 2 | use manager::{DeviceType, XInputInclude}; 3 | use rawinput::produce_raw_device_list; 4 | use winapi::shared::minwindef::UINT; 5 | use winapi::shared::windef::HWND; 6 | use winapi::um::winuser::{RegisterRawInputDevices, RAWINPUTDEVICE, RIDEV_INPUTSINK}; 7 | 8 | use std::mem; 9 | 10 | #[derive(Default)] 11 | pub struct RawInputRegistrar { 12 | include_xinput: bool, 13 | } 14 | 15 | impl RawInputRegistrar { 16 | pub fn new() -> Self { 17 | Self::default() 18 | } 19 | 20 | pub fn register_devices( 21 | &mut self, 22 | hwnd: HWND, 23 | reg_type: DeviceType, 24 | ) -> Result { 25 | let mut rid_vec: Vec = Vec::new(); 26 | match reg_type { 27 | DeviceType::Mice => { 28 | let rid = RAWINPUTDEVICE { 29 | usUsagePage: 1, 30 | usUsage: 2, // Mice 31 | dwFlags: RIDEV_INPUTSINK, 32 | hwndTarget: hwnd, 33 | }; 34 | rid_vec.push(rid); 35 | } 36 | DeviceType::Joysticks(include_xinput) => { 37 | self.include_xinput = match include_xinput { 38 | XInputInclude::True => true, 39 | XInputInclude::False => false, 40 | }; 41 | let rid = RAWINPUTDEVICE { 42 | usUsagePage: 1, 43 | usUsage: 4, // Joysticks 44 | dwFlags: RIDEV_INPUTSINK, 45 | hwndTarget: hwnd, 46 | }; 47 | rid_vec.push(rid); 48 | let rid = RAWINPUTDEVICE { 49 | usUsagePage: 1, 50 | usUsage: 5, // Xbox Controllers 51 | dwFlags: RIDEV_INPUTSINK, 52 | hwndTarget: hwnd, 53 | }; 54 | rid_vec.push(rid); 55 | } 56 | DeviceType::Keyboards => { 57 | let rid = RAWINPUTDEVICE { 58 | usUsagePage: 1, 59 | usUsage: 6, // Keyboards 60 | dwFlags: RIDEV_INPUTSINK, 61 | hwndTarget: hwnd, 62 | }; 63 | rid_vec.push(rid); 64 | } 65 | }; 66 | unsafe { 67 | if RegisterRawInputDevices( 68 | rid_vec.as_ptr(), 69 | rid_vec.len() as UINT, 70 | mem::size_of::() as UINT, 71 | ) == 0 72 | { 73 | return Err("Registration of Controller Failed"); 74 | } 75 | } 76 | Ok(produce_raw_device_list(self.include_xinput)) 77 | } 78 | } 79 | --------------------------------------------------------------------------------