├── .gitignore ├── Cargo.toml ├── LICENSE.md ├── readme.md ├── rustfmt.toml └── src ├── error.rs ├── input.rs ├── keylike.rs ├── lib.rs ├── message_loop.rs ├── mouse.rs └── vk.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winput" 3 | version = "0.2.5" 4 | authors = ["Gymore "] 5 | edition = "2018" 6 | description = "A high-level interface to Windows' input system." 7 | categories = ["os::windows-apis", "api-bindings"] 8 | license = "MIT" 9 | keywords = ["windows", "input", "simulation", "keyboard", "mouse"] 10 | repository = "https://github.com/gymore-io/winput" 11 | documentation = "https://docs.rs/winput/" 12 | 13 | [lib] 14 | # Most of doctests involve simulating keystrokes/mouse motion. 15 | # It is quite troublesome to see random things getting typed when executing tests. 16 | doctest = false 17 | 18 | [features] 19 | default = ["message_loop"] 20 | 21 | # Disables non-Windows related features. 22 | minimal = [] 23 | 24 | # Enables the `message_loop` module. 25 | message_loop = [ 26 | "winapi/libloaderapi", 27 | "winapi/windef", 28 | "winapi/minwindef", 29 | "winapi/hidusage" 30 | ] 31 | 32 | [dependencies.winapi] 33 | version = "0.3" 34 | default-features = false 35 | features = ["winuser", "errhandlingapi", "winbase"] 36 | 37 | [dependencies.serde] 38 | version = "1" 39 | optional = true 40 | features = [ "derive" ] 41 | 42 | [badges.maintenance] 43 | status = "passively-maintained" 44 | 45 | [package.metadata.docs.rs] 46 | default-target = "x86_64-pc-windows-msvc" -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Gymore 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Documentation](https://docs.rs/winput/badge.svg)](https://docs.rs/winput/) 2 | [![Crates.io](https://img.shields.io/crates/v/winput.svg)](https://crates.io/crates/winput) 3 | 4 | `winput` is a high-level interface to *Windows*' input system. 5 | 6 | ## Target 7 | 8 | This crate aims to be low-level and straightforward enough to be used as a backend for other, more general crates of the genre. For this purpose, the "minimal" feature disables most of the stuff that is not really part of *Windows*' input system (things like [`Keylike`], for example, that are mostly there for convenience). 9 | 10 | ## Features 11 | 12 | * `minimal`: This feature disables the [`Keylike`] structure as well as some shortcut functions. This feature has been made for people that want to use the straightforward api `winput` provides. 13 | * `message_loop`: This feature enables the [`message_loop`] module that gives a way to globally retreive keyboard and mouse events from Windows' message system. 14 | 15 | ## What is left to do? 16 | 17 | `winput` does not currently support any devices other than the mouse and the keyboard. I haven't really looked into how those work so if you know anything, feel free to submit an issue or a pull request! 18 | 19 | ## Examples 20 | 21 | The [`Keylike`] structure allows you to synthesize keystrokes on objects that can be used as keys. 22 | 23 | ```rust 24 | use winput::{Vk, Button}; 25 | 26 | // Synthesize keystrokes from a Virtual-Key Code 27 | winput::press(Vk::Shift); // press the shift key 28 | winput::send(Vk::A); // press then release the A key 29 | winput::release(Vk::Shift); // release the shift key 30 | 31 | // Synthesize keystrokes from characters 32 | winput::send('F'); 33 | winput::send('O'); 34 | winput::send('O'); 35 | 36 | // Synthesize keystrokes from mouse buttons 37 | winput::send(Button::Left); 38 | 39 | // You can synthesize keystrokes for the characters of a string 40 | winput::send_str("Hello, world!"); 41 | ``` 42 | 43 | The [`Mouse`] structure can be used to manipulate the mouse. 44 | 45 | ```rust 46 | use winput::Mouse; 47 | 48 | // Retrieve the position of the mouse. 49 | let (x, y) = Mouse::position(); 50 | 51 | // Set the mouse position 52 | // ... in screen coordinates 53 | Mouse::set_position(10, 10); 54 | // ... in normalized absolute coordinates 55 | Mouse::move_absolute(0.5, 0.5); 56 | // ... relatively to the current cursor's position 57 | Mouse::move_relative(100, 50); 58 | 59 | // Rotate the mouse wheel (vertically) 60 | Mouse::scroll(1.5); 61 | // ... or horizontally 62 | Mouse::scrollh(-1.5); 63 | ``` 64 | 65 | For more complicated input patterns, the [`Input`] structure can be used. 66 | 67 | ```rust 68 | use winput::{Input, Vk, Action, MouseMotion}; 69 | 70 | // There is multiple ways to create an `Input`: 71 | let inputs = [ 72 | // ... from a character 73 | Input::from_char('a', Action::Press).unwrap(), 74 | // ... from a Virtual-Key Code 75 | Input::from_vk(Vk::A, Action::Release), 76 | // ... from a mouse motion 77 | Input::from_motion(MouseMotion::Relative { x: 100, y: 100 }), 78 | 79 | // additional constructors are available 80 | ]; 81 | 82 | let number_of_inputs_inserted = winput::send_inputs(&inputs); 83 | 84 | assert_eq!(number_of_inputs_inserted, 3); 85 | ``` 86 | 87 | With the `message_loop` feature (enabled by default), keyboard keystrokes and mouse inputs can be retreived. 88 | 89 | ```rust 90 | use winput::{Vk, Action}; 91 | use winput::message_loop; 92 | 93 | let receiver = message_loop::start().unwrap(); 94 | 95 | loop { 96 | match receiver.next_event() { 97 | message_loop::Event::Keyboard { 98 | vk, 99 | action: Action::Press, 100 | .. 101 | } => { 102 | if vk == Vk::Escape { 103 | break; 104 | } else { 105 | println!("{:?} was pressed!", vk); 106 | } 107 | }, 108 | _ => (), 109 | } 110 | } 111 | ``` 112 | 113 | [`Keylike`]: https://docs.rs/winput/latest/winput/trait.Keylike.html 114 | [`Input`]: https://docs.rs/winput/latest/winput/struct.Input.html 115 | [`Mouse`]: https://docs.rs/winput/latest/winput/struct.Mouse.html 116 | [`Handler`]: https://docs.rs/winput/latest/winput/message_loop/trait.Handler.html 117 | [`message_loop`]: https://docs.rs/winput/latest/winput/message_loop/ -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | max_width = 90 -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | 4 | /// Represents a Windows error. 5 | #[derive(Clone, Debug)] 6 | pub struct WindowsError { 7 | code: u32, 8 | description: Option, 9 | } 10 | 11 | impl WindowsError { 12 | /// Creates a new [`WindowsError`] using the last error code retreived using the 13 | /// native `GetLastError` function. 14 | /// 15 | /// ## Example 16 | /// 17 | /// ```rust, ignore 18 | /// use winput::WindowsError; 19 | /// 20 | /// let error = WindowsError::from_last_error(); 21 | /// println!("{:?}", &error.description); 22 | /// ``` 23 | /// 24 | /// [`WindowsError`]: struct.WindowsError.html 25 | pub fn from_last_error() -> Self { 26 | use winapi::um::errhandlingapi::GetLastError; 27 | 28 | let last_error_code = unsafe { GetLastError() }; 29 | Self::from_error_code(last_error_code) 30 | } 31 | 32 | /// Creates a new [`WindowsError`] using the given error code. The description of the 33 | /// error is retreived using the native `FormatMessageW` function. 34 | /// 35 | /// ## Example 36 | /// 37 | /// ```rust, ignore 38 | /// use winput::WindowsError; 39 | /// 40 | /// let error = WindowsError::from_error_code(101); 41 | /// println!("{:?}", &error.description); 42 | /// ``` 43 | /// 44 | /// [`WindowsError`]: struct.WindowsError.html 45 | pub fn from_error_code(error_code: u32) -> Self { 46 | use std::{mem, ptr, slice}; 47 | use winapi::um::winbase; 48 | 49 | let mut buffer_ptr: *mut u16 = ptr::null_mut(); 50 | 51 | // Calling C code 52 | // 53 | // The function returns the number of `u16` characters that were written. 54 | let len = unsafe { 55 | winbase::FormatMessageW( 56 | winbase::FORMAT_MESSAGE_IGNORE_INSERTS 57 | | winbase::FORMAT_MESSAGE_ALLOCATE_BUFFER 58 | | winbase::FORMAT_MESSAGE_FROM_SYSTEM, 59 | ptr::null(), 60 | error_code, 61 | 0, 62 | &mut buffer_ptr as *mut *mut u16 as _, 63 | 0, 64 | ptr::null_mut(), 65 | ) 66 | }; 67 | 68 | if len == 0 { 69 | // The allocation failed / an invalid error code was provided 70 | return WindowsError { 71 | code: error_code, 72 | description: None, 73 | }; 74 | } 75 | 76 | assert!(!buffer_ptr.is_null()); 77 | assert_eq!(buffer_ptr as usize % mem::align_of::(), 0); 78 | 79 | // SAFETY: 80 | // * `buffer_ptr` is non-null and well-aligned 81 | // * `buffer_ptr` is not aliased anywere in the code 82 | // * `buffer_ptr` is pointing to `len` properly initialized `u16`s. 83 | let slice = unsafe { slice::from_raw_parts_mut(buffer_ptr, len as _) }; 84 | 85 | let description = String::from_utf16(slice).ok(); 86 | 87 | // The message is now copied into the rust world. 88 | // We can free the allocated buffer. 89 | unsafe { winbase::LocalFree(buffer_ptr as _) }; 90 | 91 | WindowsError { 92 | code: error_code, 93 | description, 94 | } 95 | } 96 | } 97 | 98 | impl fmt::Display for WindowsError { 99 | #[inline(always)] 100 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 101 | if let Some(ref desc) = self.description { 102 | write!(f, "\"{}\" (code: {})", desc, self.code) 103 | } else { 104 | write!(f, "WindowsError(code: {})", self.code) 105 | } 106 | } 107 | } 108 | 109 | impl Error for WindowsError {} 110 | -------------------------------------------------------------------------------- /src/input.rs: -------------------------------------------------------------------------------- 1 | use crate::vk::Vk; 2 | 3 | use winapi::um::winuser; 4 | 5 | /// This structure is used by [`send_inputs`] to store information for synthesizing input 6 | /// events such as keystrokes, mouse movement, and mouse clicks. 7 | /// 8 | /// ## Example 9 | /// 10 | /// ```rust, ignore 11 | /// use winput::{Input, Action}; 12 | /// 13 | /// let input = Input::from_char('A', Action::Press); 14 | /// winput::send_inputs(&[input]); 15 | /// ``` 16 | /// 17 | /// [`send_inputs`]: fn.send_inputs.html 18 | #[derive(Clone)] 19 | #[repr(transparent)] 20 | pub struct Input(winuser::INPUT); 21 | 22 | impl Input { 23 | /// Creates an [`Input`] that causes the given action to be taken on the given 24 | /// character. If the given character is above `0x0000ffff`, `None` is returned. 25 | /// 26 | /// ## Example 27 | /// 28 | /// ```rust, ignore 29 | /// use winput::{Input, Action}; 30 | /// 31 | /// let input = Input::from_char('A', Action::Press).unwrap(); 32 | /// winput::send_inputs(&[input]); 33 | /// ``` 34 | /// 35 | /// [`Input`]: struct.Input.html 36 | pub fn from_char(c: char, action: Action) -> Option { 37 | let c_n = c as u32; 38 | if c_n > 0x0000ffff { 39 | return None; 40 | } 41 | 42 | unsafe { 43 | let mut input: winuser::INPUT = std::mem::zeroed(); 44 | input.type_ = winuser::INPUT_KEYBOARD; 45 | let ki = input.u.ki_mut(); 46 | ki.wVk = 0; // must be 0 for a unicode event 47 | ki.wScan = c as u16; 48 | ki.dwFlags = match action { 49 | Action::Release => winuser::KEYEVENTF_KEYUP | winuser::KEYEVENTF_UNICODE, 50 | Action::Press => winuser::KEYEVENTF_UNICODE, 51 | }; 52 | ki.time = 0; // let the system provide a time stamp 53 | 54 | Some(Self(input)) 55 | } 56 | } 57 | 58 | /// Creates an [`Input`] that causes the given action to be taken on the given 59 | /// Virtual-Key Code. 60 | /// 61 | /// ## Example 62 | /// 63 | /// ```rust, ignore 64 | /// use winput::{Input, Action, Vk}; 65 | /// 66 | /// let input = Input::from_vk(Vk::Enter, Action::Press); 67 | /// winput::send_inputs(&[input]); 68 | /// ``` 69 | /// 70 | /// [`Input`]: struct.Input.html 71 | pub fn from_vk(vk: Vk, action: Action) -> Input { 72 | unsafe { 73 | let mut input: winuser::INPUT = std::mem::zeroed(); 74 | input.type_ = winuser::INPUT_KEYBOARD; 75 | let ki = input.u.ki_mut(); 76 | ki.wVk = vk as u16; 77 | ki.wScan = 0; // we are using the Virtual-Key Code 78 | ki.dwFlags = match action { 79 | Action::Release => winuser::KEYEVENTF_KEYUP, 80 | Action::Press => 0, 81 | }; 82 | ki.time = 0; // let the system provide a time stamp 83 | 84 | Self(input) 85 | } 86 | } 87 | 88 | /// Creates an [`Input`] that causes the given action to be taken on the given mouse 89 | /// button. 90 | /// 91 | /// ## Example 92 | /// 93 | /// ```rust, ignore 94 | /// use winput::{Button, Action, Input}; 95 | /// 96 | /// let input = Input::from_button(Button::Left, Action::Press); 97 | /// winput::send_inputs(&[input]); 98 | /// ``` 99 | /// 100 | /// [`Input`]: struct.Input.html 101 | pub fn from_button(button: Button, action: Action) -> Input { 102 | unsafe { 103 | let mut input: winuser::INPUT = std::mem::zeroed(); 104 | input.type_ = winuser::INPUT_MOUSE; 105 | let mi = input.u.mi_mut(); 106 | mi.dx = 0; // the mouse is not going to move 107 | mi.dy = 0; 108 | mi.mouseData = match button { 109 | Button::X1 => 1, 110 | Button::X2 => 2, 111 | _ => 0, 112 | }; 113 | mi.dwFlags = match button { 114 | Button::Left => match action { 115 | Action::Press => winuser::MOUSEEVENTF_LEFTDOWN, 116 | Action::Release => winuser::MOUSEEVENTF_LEFTUP, 117 | }, 118 | Button::Right => match action { 119 | Action::Press => winuser::MOUSEEVENTF_RIGHTDOWN, 120 | Action::Release => winuser::MOUSEEVENTF_RIGHTUP, 121 | }, 122 | Button::Middle => match action { 123 | Action::Press => winuser::MOUSEEVENTF_MIDDLEDOWN, 124 | Action::Release => winuser::MOUSEEVENTF_MIDDLEUP, 125 | }, 126 | Button::X1 | Button::X2 => match action { 127 | Action::Press => winuser::MOUSEEVENTF_XDOWN, 128 | Action::Release => winuser::MOUSEEVENTF_XUP, 129 | }, 130 | }; 131 | 132 | mi.time = 0; // let the system provide a time stamp 133 | mi.dwExtraInfo = 0; // no extra information 134 | 135 | Self(input) 136 | } 137 | } 138 | 139 | /// Creates an [`Input`] that causes the mouse to move according to the given 140 | /// [`MouseMotion`]. 141 | /// 142 | /// ## Example 143 | /// 144 | /// ```rust, ignore 145 | /// use winput::{MouseMotion, Input}; 146 | /// 147 | /// let motion = MouseMotion::Relative { 148 | /// dx: 100, // 100 pixels right 149 | /// dy: 50, // 50 pixels down 150 | /// }; 151 | /// 152 | /// let input = Input::from_motion(motion); 153 | /// 154 | /// winput::send_inputs(&[input]); 155 | /// ``` 156 | /// 157 | /// [`Input`]: struct.Input.html 158 | /// [`MouseMotion`]: enum.MouseMotion.html 159 | pub fn from_motion(motion: MouseMotion) -> Self { 160 | unsafe { 161 | let mut input: winuser::INPUT = std::mem::zeroed(); 162 | input.type_ = winuser::INPUT_MOUSE; 163 | let mi = input.u.mi_mut(); 164 | 165 | mi.mouseData = 0; // no additional data 166 | 167 | // in any case, the mouse is goign to move 168 | mi.dwFlags = winuser::MOUSEEVENTF_MOVE; 169 | 170 | match motion { 171 | MouseMotion::Relative { dx, dy } => { 172 | mi.dx = dx; 173 | mi.dy = dy; 174 | } 175 | MouseMotion::Absolute { x, y, virtual_desk } => { 176 | const NORMAL_FACTOR: f32 = 65535.0; 177 | 178 | mi.dx = (x * NORMAL_FACTOR) as i32; 179 | mi.dy = (y * NORMAL_FACTOR) as i32; 180 | 181 | if virtual_desk { 182 | mi.dwFlags |= winuser::MOUSEEVENTF_VIRTUALDESK; 183 | } 184 | 185 | mi.dwFlags |= winuser::MOUSEEVENTF_ABSOLUTE; 186 | } 187 | } 188 | 189 | mi.time = 0; // let the system provide a time stamp 190 | mi.dwExtraInfo = 0; // no extra information 191 | 192 | Self(input) 193 | } 194 | } 195 | 196 | /// Creates an [`Input`] that causes the mouse wheel to rotate by the given amount and 197 | /// in the given [`WheelDirection`]. 198 | /// 199 | /// When the given direction is vertical, a positive motion means the wheel rotates 200 | /// forward, away from the user; a negative value means the wheel rotates backward, 201 | /// toward the user. 202 | /// 203 | /// When the given direction is horizontal, a positive motion means the wheel rotates 204 | /// to the right; a negative value means the wheel rotates to the left. 205 | /// 206 | /// ## Example 207 | /// 208 | /// ```rust, ignore 209 | /// use winput::{WheelDirection, Input}; 210 | /// 211 | /// let input = Input::from_wheel(100.0, WheelDirection::Vertical); 212 | /// winput::send_inputs(&[input]); 213 | /// ``` 214 | /// 215 | /// [`Input`]: struct.Input.html 216 | /// [`WheelDirection`]: enum.WheelDirection.html 217 | pub fn from_wheel(motion: f32, direction: WheelDirection) -> Self { 218 | unsafe { 219 | let mut input: winuser::INPUT = std::mem::zeroed(); 220 | input.type_ = winuser::INPUT_MOUSE; 221 | let mi = input.u.mi_mut(); 222 | mi.dx = 0; // there is no mouse movement 223 | mi.dy = 0; 224 | 225 | const MOUSE_DELTA: f32 = 120.0; 226 | mi.mouseData = (motion * MOUSE_DELTA) as i32 as u32; 227 | 228 | mi.dwFlags = match direction { 229 | WheelDirection::Vertical => winuser::MOUSEEVENTF_WHEEL, 230 | WheelDirection::Horizontal => winuser::MOUSEEVENTF_HWHEEL, 231 | }; 232 | 233 | mi.time = 0; // let the system provide a time stamp 234 | mi.dwExtraInfo = 0; // no extra information 235 | 236 | Self(input) 237 | } 238 | } 239 | } 240 | 241 | /// Synthesizes keystrokes, mouse motions, and button clicks. 242 | /// 243 | /// ## Returns 244 | /// 245 | /// This function returns the number of events that were successfully inserted onto the 246 | /// keyboard or mouse input stream. 247 | /// 248 | /// If no events were successfully sent, the input stream was already blocked by another 249 | /// thread. You can use [`winput::WindowsError::from_last_error`] to retrieve additional 250 | /// information about this function failing to send events. 251 | /// 252 | /// ## Example 253 | /// 254 | /// ```rust, ignore 255 | /// use winput::{Vk, Input, Action}; 256 | /// 257 | /// let inputs = [ 258 | /// Input::from_vk(Vk::Shift, Action::Press), 259 | /// Input::from_vk(Vk::A, Action::Press), 260 | /// Input::from_vk(Vk::A, Action::Release), 261 | /// Input::from_vk(Vk::Shift, Action::Release), 262 | /// ]; 263 | /// 264 | /// winput::send_inputs(&inputs); 265 | /// ``` 266 | /// 267 | /// [`winput::WindowsError::from_last_error`]: struct.WindowsError.html#method.from_last_error 268 | pub fn send_inputs(inputs: impl AsRef<[Input]>) -> u32 { 269 | use std::mem; 270 | 271 | // Calling C code 272 | unsafe { 273 | winuser::SendInput( 274 | inputs.as_ref().len() as _, 275 | inputs.as_ref().as_ptr() as _, 276 | mem::size_of::() as _, 277 | ) 278 | } 279 | } 280 | 281 | /// Represents an action that can be taken on a key or button. 282 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 283 | pub enum Action { 284 | /// The action of pressing the key. 285 | Press, 286 | /// The action of releasing the key. 287 | Release, 288 | } 289 | 290 | impl Action { 291 | /// Creates a new [`Action`] from the given `bool`. 292 | /// 293 | /// * If `is_press` is `true`, `Action::Press` is returned. 294 | /// * If `is_press` is `false`, `Action::Release` is returned. 295 | /// 296 | /// ## Example 297 | /// 298 | /// ```rust 299 | /// use winput::Action; 300 | /// 301 | /// assert_eq!(Action::from_press(true), Action::Press); 302 | /// assert_eq!(Action::from_press(false), Action::Release); 303 | /// ``` 304 | /// 305 | /// [`Action`]: enum.Action.html 306 | pub fn from_press(is_press: bool) -> Self { 307 | if is_press { 308 | Self::Press 309 | } else { 310 | Self::Release 311 | } 312 | } 313 | 314 | /// Creates a new [`Action`] from the given `bool`. 315 | /// 316 | /// * If `is_release` is `true`, `Action::Release` is returned. 317 | /// * If `is_release` is `false`, `Action::Press` is returned. 318 | /// 319 | /// ## Example 320 | /// 321 | /// ```rust 322 | /// use winput::Action; 323 | /// 324 | /// assert_eq!(Action::from_release(true), Action::Release); 325 | /// assert_eq!(Action::from_release(false), Action::Press); 326 | /// ``` 327 | /// 328 | /// [`Action`]: enum.Action.html 329 | pub fn from_release(is_release: bool) -> Self { 330 | if is_release { 331 | Self::Release 332 | } else { 333 | Self::Press 334 | } 335 | } 336 | } 337 | 338 | /// A mouse button. 339 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 340 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 341 | pub enum Button { 342 | /// The left mouse button. 343 | Left, 344 | /// The right mouse button. 345 | Right, 346 | /// The middle mouse button. 347 | Middle, 348 | /// The X1 mouse button. 349 | X1, 350 | /// The X2 mouse button. 351 | X2, 352 | } 353 | 354 | /// Describes a mouse motion. 355 | #[derive(Clone, Copy, PartialEq, Debug)] 356 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 357 | pub enum MouseMotion { 358 | /// Describes a relative mouse motion, in pixels. 359 | /// 360 | /// Relative mouse motion is subject to the effects of the mouse speed and the 361 | /// two-mouse threshold values. A user sets these three values with the Pointer Speed 362 | /// slider of the Control Panel's Mouse Properties sheet. 363 | Relative { 364 | /// The number of pixels the mouse should move, on the horizontal axis. 365 | dx: i32, 366 | /// The number of pixels the mouse should move, on the vertical axis. A positive 367 | /// value makes the mouse go down. 368 | dy: i32, 369 | }, 370 | /// Describes an absolute mouse motion, in normalized coordinates. 371 | Absolute { 372 | /// The normalized position of the mouse on the horizontal axis. A value of 373 | /// `0.0` maps to the left of the screen and a value of `1.0` maps to the 374 | /// right of the screen. 375 | x: f32, 376 | /// The normalized position of the mouse on the vertical axis. A value of `0.0` 377 | /// maps to the top of the screen and a value of `1.0` maps to the bottom on the 378 | /// screen. 379 | y: f32, 380 | /// Whether the given normalized coordinates should map to the entier virtual 381 | /// desktop (if multiple monitors are used, for example). 382 | virtual_desk: bool, 383 | }, 384 | } 385 | 386 | /// Describes the direction of a mouse wheel. 387 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 388 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 389 | pub enum WheelDirection { 390 | Vertical, 391 | Horizontal, 392 | } 393 | -------------------------------------------------------------------------------- /src/keylike.rs: -------------------------------------------------------------------------------- 1 | use crate::input::{send_inputs, Action, Button, Input}; 2 | use crate::vk::Vk; 3 | 4 | /// A trait for objects that can be used as keys. For example a [`Vk`] or a `char` can be 5 | /// used as a key. 6 | /// 7 | /// ## Example 8 | /// 9 | /// ```rust, ignore 10 | /// use winput::Vk; 11 | /// 12 | /// // Print `A` 13 | /// winput::press(Vk::Shift); 14 | /// winput::send(Vk::A); 15 | /// winput::release(Vk::Shift); 16 | /// 17 | /// // Do the same with one line 18 | /// winput::send('A'); 19 | /// ``` 20 | /// 21 | /// [`Vk`]: enum.Vk.html 22 | pub trait Keylike: Copy { 23 | /// Produces an `Input` that causes the given action to be taken on `self`. 24 | /// 25 | /// ## Panics 26 | /// 27 | /// This function panics if `self` was not a valid key. For example, any `char` that 28 | /// is above `0x0000ffff` cannot be turned into an `Input`. 29 | /// 30 | /// ## Example 31 | /// 32 | /// ```rust, ignore 33 | /// use winput::{Keylike, Action}; 34 | /// 35 | /// let input = 'A'.produce_input(Action::Press); 36 | /// winput::send_inputs(&[input]); 37 | /// ``` 38 | fn produce_input(self, action: Action) -> Input; 39 | } 40 | 41 | impl Keylike for char { 42 | #[inline(always)] 43 | fn produce_input(self, action: Action) -> Input { 44 | Input::from_char(self, action).expect("character above 0x0000ffff") 45 | } 46 | } 47 | 48 | impl Keylike for Vk { 49 | #[inline(always)] 50 | fn produce_input(self, action: Action) -> Input { 51 | Input::from_vk(self, action) 52 | } 53 | } 54 | 55 | impl Keylike for Button { 56 | fn produce_input(self, action: Action) -> Input { 57 | Input::from_button(self, action) 58 | } 59 | } 60 | 61 | /// Synthesize an event that presses the key. 62 | /// 63 | /// If the function fails to synthesize the input, no error is emited and the 64 | /// function fails silently. If you wish to retreive an eventual error, use 65 | /// `send_inputs` instead. 66 | /// 67 | /// ## Panics 68 | /// 69 | /// This function panics if `key` was not a valid key. For example, any `char` that 70 | /// is above `0x0000ffff` cannot be turned into an `Input`. 71 | /// 72 | /// ## Example 73 | /// 74 | /// ```rust, ignore 75 | /// winput::press('A').unwrap(); 76 | /// ``` 77 | #[inline] 78 | pub fn press(key: K) { 79 | let input = key.produce_input(Action::Press); 80 | crate::input::send_inputs(&[input]); 81 | } 82 | 83 | /// Synthesizes an event that releases the key. 84 | /// 85 | /// If the function fails to synthesize the input, no error is emited and the 86 | /// function fails silently. If you wish to retreive an eventual error, use 87 | /// `send_inputs` instead. 88 | /// 89 | /// ## Panics 90 | /// 91 | /// This function panics if `key` was not a valid key. For example, any `char` that 92 | /// is above `0x0000ffff` cannot be turned into an `Input`. 93 | /// 94 | /// ## Example 95 | /// 96 | /// ```rust, ignore 97 | /// winput::release('B').unwrap(); 98 | /// ``` 99 | #[inline(always)] 100 | pub fn release(key: K) { 101 | let input = key.produce_input(Action::Release); 102 | crate::input::send_inputs(&[input]); 103 | } 104 | 105 | /// Synthesizes two events. One that presses the key, one that releases the key. 106 | /// 107 | /// If the function fails to synthesize the input, no error is emited and the 108 | /// function fails silently. If you wish to retreive an eventual error, use 109 | /// `send_inputs` instead. 110 | /// 111 | /// ## Panics 112 | /// 113 | /// This function panics if `key` was not a valid value. For example, any `char` that 114 | /// is above `0x0000ffff` cannot be turned into an `Input`. 115 | /// 116 | /// ## Example 117 | /// 118 | /// ```rust, ignore 119 | /// winput::send('C').unwrap(); 120 | /// ``` 121 | #[inline(always)] 122 | pub fn send(key: K) { 123 | let inputs = [ 124 | key.produce_input(Action::Press), 125 | key.produce_input(Action::Release), 126 | ]; 127 | 128 | crate::input::send_inputs(&inputs); 129 | } 130 | 131 | /// Synthesizes keystrokes according to the given iterator of keys. 132 | /// 133 | /// Note that this function needs to allocate a buffer to store the inputs produced by the 134 | /// given keys. 135 | /// 136 | /// The function returns the number of inputs that were successfully inserted into the 137 | /// keyboard input stream. 138 | /// 139 | /// If no events were successfully sent, the input stream was already blocked by another 140 | /// thread. You can use `winput::WindowsError::from_last_error` to retrieve additional 141 | /// information about this function failing to send events. 142 | /// 143 | /// ## Panics 144 | /// 145 | /// This function panics if the buffer used to store the produced inputs fails to 146 | /// allocate or if any of the given keys is unable to produce an `Input`. 147 | /// 148 | /// ## Example 149 | /// 150 | /// ```rust, ignore 151 | /// use winput::Vk; 152 | /// 153 | /// let keys = vec![ Vk::A, Vk::B, Vk::C ]; 154 | /// 155 | /// winput::send_keys(keys); 156 | /// ``` 157 | pub fn send_keys(keys: I) -> u32 158 | where 159 | I: IntoIterator, 160 | I::Item: Keylike, 161 | { 162 | let iter = keys.into_iter(); 163 | let mut buffer = Vec::with_capacity(iter.size_hint().0 * 2); 164 | 165 | for key in iter { 166 | buffer.push(key.produce_input(Action::Press)); 167 | buffer.push(key.produce_input(Action::Release)); 168 | } 169 | 170 | send_inputs(&buffer) 171 | } 172 | 173 | /// Synthesizes keystrokes following the given string reference. 174 | /// 175 | /// Note that this function needs to allocate a buffer to store the inputs produced by 176 | /// the characters. 177 | /// 178 | /// The function returns the number of inputs that were successfully inserted into the 179 | /// keyboard input stream. 180 | /// 181 | /// If no events were successfully sent, the input stream was already blocked by another 182 | /// thread. You can use `winput::WindowsError::from_last_error` to retreive additional 183 | /// information about this function failing to send events. 184 | /// 185 | /// ## Panics 186 | /// 187 | /// This function panics if the buffer fails to allocate or if any of the given character 188 | /// fails to produce an `Input`. 189 | /// 190 | /// ## Example 191 | /// 192 | /// ```rust, ignore 193 | /// winput::send_str("Hello, world"); 194 | /// ``` 195 | #[inline(always)] 196 | pub fn send_str(s: &str) -> u32 { 197 | send_keys(s.chars()) 198 | } 199 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | pub use error::WindowsError; 3 | 4 | mod vk; 5 | pub use vk::Vk; 6 | 7 | mod input; 8 | pub use input::{send_inputs, Action, Button, Input, MouseMotion, WheelDirection}; 9 | 10 | #[cfg(not(feature = "minimal"))] 11 | mod keylike; 12 | #[cfg(not(feature = "minimal"))] 13 | pub use keylike::{press, release, send, send_keys, send_str, Keylike}; 14 | 15 | mod mouse; 16 | pub use mouse::Mouse; 17 | 18 | #[cfg(feature = "message_loop")] 19 | pub mod message_loop; 20 | -------------------------------------------------------------------------------- /src/message_loop.rs: -------------------------------------------------------------------------------- 1 | //! The `message_loop` module provides a way to retreive keyboard and mouse 2 | //! input messages directly from the system. 3 | //! 4 | //! Internally, a [message-only window](https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows) 5 | //! is created to receive the messages. 6 | //! 7 | //! ## Examples 8 | //! 9 | //! ```rust, ignore 10 | //! use winput::{Vk, Action}; 11 | //! use winput::message_loop; 12 | //! 13 | //! let receiver = message_loop::start().unwrap(); 14 | //! 15 | //! loop { 16 | //! match receiver.next_event() { 17 | //! message_loop::Event::Keyboard { 18 | //! vk, 19 | //! action: Action::Press, 20 | //! .. 21 | //! } => { 22 | //! if vk == Vk::Escape { 23 | //! break; 24 | //! } else { 25 | //! println!("{:?} was pressed!", vk); 26 | //! } 27 | //! }, 28 | //! _ => (), 29 | //! } 30 | //! } 31 | //! ``` 32 | 33 | use std::ffi::OsStr; 34 | use std::mem::MaybeUninit; 35 | use std::os::windows::ffi::OsStrExt; 36 | use std::sync::atomic::{AtomicU8, Ordering}; 37 | use std::sync::mpsc; 38 | use std::time::Duration; 39 | use std::{iter, mem, ptr}; 40 | 41 | use winapi::shared::{hidusage, minwindef, windef}; 42 | use winapi::um::{libloaderapi, winuser}; 43 | 44 | use crate::input::{Action, Button}; 45 | use crate::vk::Vk; 46 | use crate::{WheelDirection, WindowsError}; 47 | 48 | /// The current state of the message loop. 49 | /// 50 | /// * 0 -> The message loop is not active. 51 | /// * 1 -> The `start` function has been called. 52 | /// The message loop is now starting. 53 | /// * 2 -> The message loop has successfully started. 54 | /// * 3 -> The message loop is now exiting. 55 | static STATE: AtomicU8 = AtomicU8::new(0); 56 | 57 | // This value initialized if `STATE` is `2`. It is uninitialized if `STATE` is `0`. 58 | // `SENDER` must only be used on the message loop's thread. 59 | static mut SENDER: MaybeUninit> = MaybeUninit::uninit(); 60 | 61 | /// A buffer that must only be used on the message loop's thread. This buffer must 62 | /// be properly initialized when the message loop's thread is started. 63 | static mut BUFFER: MaybeUninit> = MaybeUninit::uninit(); 64 | 65 | /// Checks whether `short` contains all the bits of `mask`. 66 | #[inline] 67 | fn has_flags(short: u16, mask: u16) -> bool { 68 | short & mask == mask 69 | } 70 | 71 | /// A callback function called by the system on the message loop thread. 72 | unsafe extern "system" fn window_proc( 73 | hwnd: windef::HWND, 74 | msg: minwindef::UINT, 75 | w_param: minwindef::WPARAM, 76 | l_param: minwindef::LPARAM, 77 | ) -> minwindef::LRESULT { 78 | match msg { 79 | // Note: This loop is only here to break from the scope early. 80 | winuser::WM_INPUT => loop { 81 | // Determine how big should our buffer be. 82 | let mut size = 0; 83 | let mut result = winuser::GetRawInputData( 84 | l_param as winuser::HRAWINPUT, 85 | winuser::RID_INPUT, 86 | ptr::null_mut(), 87 | &mut size, 88 | mem::size_of::() as _, 89 | ); 90 | 91 | if result == -1i32 as u32 { 92 | break; 93 | } 94 | 95 | // SAFETY: 96 | // The buffer must be initialized because we are on the message loop's 97 | // thread. 98 | let buffer = &mut *BUFFER.as_mut_ptr(); 99 | buffer.clear(); 100 | buffer.reserve(size as _); 101 | 102 | // Actually write to the buffer. 103 | result = winuser::GetRawInputData( 104 | l_param as winuser::HRAWINPUT, 105 | winuser::RID_INPUT, 106 | buffer.as_mut_ptr() as _, 107 | &mut size, 108 | mem::size_of::() as _, 109 | ); 110 | 111 | if result != size { 112 | // We failed to write to the buffer. 113 | break; 114 | } 115 | 116 | // SAFETY: 117 | // The `GetRawInputData` function did not failed. 118 | let raw_input = &*(buffer.as_mut_ptr() as winuser::PRAWINPUT); 119 | 120 | // SAFETY: 121 | // We are on the message loop's thread, `SENDER` must be initialized. 122 | let sender = &mut *SENDER.as_mut_ptr(); 123 | 124 | match raw_input.header.dwType { 125 | winuser::RIM_TYPEMOUSE => { 126 | // Mouse event 127 | let data = raw_input.data.mouse(); 128 | 129 | if has_flags(data.usFlags, winuser::MOUSE_MOVE_RELATIVE) { 130 | sender 131 | .send(Event::MouseMoveRelative { 132 | x: data.lLastX, 133 | y: data.lLastY, 134 | }) 135 | .unwrap(); 136 | } 137 | 138 | if has_flags(data.usFlags, winuser::MOUSE_MOVE_ABSOLUTE) { 139 | sender 140 | .send(Event::MouseMoveAbsolute { 141 | x: data.lLastX as f32 / 65535.0, 142 | y: data.lLastY as f32 / 65535.0, 143 | virtual_desk: data.usFlags 144 | & winuser::MOUSE_VIRTUAL_DESKTOP 145 | == winuser::MOUSE_VIRTUAL_DESKTOP, 146 | }) 147 | .unwrap(); 148 | } 149 | 150 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_LEFT_BUTTON_DOWN) { 151 | sender 152 | .send(Event::MouseButton { 153 | action: Action::Press, 154 | button: Button::Left, 155 | }) 156 | .unwrap(); 157 | } 158 | 159 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_LEFT_BUTTON_UP) { 160 | sender 161 | .send(Event::MouseButton { 162 | action: Action::Release, 163 | button: Button::Left, 164 | }) 165 | .unwrap(); 166 | } 167 | 168 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_RIGHT_BUTTON_DOWN) 169 | { 170 | sender 171 | .send(Event::MouseButton { 172 | action: Action::Press, 173 | button: Button::Right, 174 | }) 175 | .unwrap(); 176 | } 177 | 178 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_RIGHT_BUTTON_UP) { 179 | sender 180 | .send(Event::MouseButton { 181 | action: Action::Release, 182 | button: Button::Right, 183 | }) 184 | .unwrap(); 185 | } 186 | 187 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_MIDDLE_BUTTON_DOWN) 188 | { 189 | sender 190 | .send(Event::MouseButton { 191 | action: Action::Press, 192 | button: Button::Middle, 193 | }) 194 | .unwrap(); 195 | } 196 | 197 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_MIDDLE_BUTTON_UP) { 198 | sender 199 | .send(Event::MouseButton { 200 | action: Action::Release, 201 | button: Button::Middle, 202 | }) 203 | .unwrap(); 204 | } 205 | 206 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_BUTTON_4_DOWN) { 207 | sender 208 | .send(Event::MouseButton { 209 | action: Action::Press, 210 | button: Button::X1, 211 | }) 212 | .unwrap(); 213 | } 214 | 215 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_BUTTON_4_UP) { 216 | sender 217 | .send(Event::MouseButton { 218 | action: Action::Release, 219 | button: Button::X1, 220 | }) 221 | .unwrap(); 222 | } 223 | 224 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_BUTTON_5_DOWN) { 225 | sender 226 | .send(Event::MouseButton { 227 | action: Action::Press, 228 | button: Button::X2, 229 | }) 230 | .unwrap(); 231 | } 232 | 233 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_BUTTON_5_UP) { 234 | sender 235 | .send(Event::MouseButton { 236 | action: Action::Release, 237 | button: Button::X2, 238 | }) 239 | .unwrap(); 240 | } 241 | 242 | if has_flags(data.usButtonFlags, winuser::RI_MOUSE_WHEEL) { 243 | sender 244 | .send(Event::MouseWheel { 245 | delta: data.usButtonData as i16 as f32 / 120.0, 246 | direction: WheelDirection::Vertical, 247 | }) 248 | .unwrap(); 249 | } 250 | 251 | if has_flags(data.usButtonFlags, 0x0800) { 252 | sender 253 | .send(Event::MouseWheel { 254 | delta: data.usButtonData as i16 as f32 / 120.0, 255 | direction: WheelDirection::Horizontal, 256 | }) 257 | .unwrap(); 258 | } 259 | } 260 | winuser::RIM_TYPEKEYBOARD => { 261 | // Keyboard event 262 | let data = raw_input.data.keyboard(); 263 | 264 | sender 265 | .send(Event::Keyboard { 266 | vk: Vk::from_u8(data.VKey as u8), 267 | scan_code: data.MakeCode as u32, 268 | action: Action::from_press(data.Flags & 1 == 0), 269 | }) 270 | .unwrap(); 271 | } 272 | 2 => (), 273 | _ => unreachable!("Invalid message"), 274 | } 275 | 276 | break; 277 | }, 278 | 279 | _ => (), 280 | } 281 | 282 | winuser::DefWindowProcW(hwnd, msg, w_param, l_param) 283 | } 284 | 285 | /// An error that can be produced by the [`start`] function. 286 | /// 287 | /// [`start`]: fn.start.html 288 | #[derive(Clone, Debug)] 289 | pub enum MessageLoopError { 290 | /// Only one message loop can be created at any given time. This error 291 | /// is produced when [`start`] is called even though the message loop 292 | /// was already active. 293 | AlreadyActive, 294 | 295 | /// Windows raised an error. 296 | OsError(WindowsError), 297 | } 298 | 299 | /// Checks if the message loop is currently active. When this function returns 300 | /// `true`, calling `start` always produces an error. 301 | /// 302 | /// ## Examples 303 | /// 304 | /// ```rust, ignore 305 | /// let _ = winput::messgage_loop::start(); 306 | /// assert!(winput::message_loop::is_active()); 307 | /// 308 | /// ``` 309 | #[inline] 310 | pub fn is_active() -> bool { 311 | STATE.load(Ordering::Acquire) != 0 312 | } 313 | 314 | /// Starts the message loop on a new thread. 315 | /// 316 | /// ## Returns 317 | /// 318 | /// This function returns an error if the message loop is already active: only one 319 | /// message loop can be started at any given time. Be carfull if another library is 320 | /// also using the message loop. 321 | /// 322 | /// You can check if the message loop is currently active by calling [`is_active`]. 323 | /// 324 | /// ## Example 325 | /// 326 | /// ```rust, ignore 327 | /// use winput::message_loop; 328 | /// 329 | /// let receiver = message_loop::start().unwrap(); 330 | /// 331 | /// loop { 332 | /// println!("{:?}", receiver.next_event()); 333 | /// } 334 | /// ``` 335 | /// 336 | /// [`is_active`]: fn.is_active.html 337 | pub fn start() -> Result { 338 | loop { 339 | match STATE.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst) { 340 | Ok(0) => break, 341 | 342 | // If the message loop is shutting down, we can just wait 343 | // a bit until we can start it again. 344 | Err(3) => (), 345 | _ => return Err(MessageLoopError::AlreadyActive), 346 | } 347 | 348 | std::hint::spin_loop(); 349 | } 350 | 351 | // The message loop is now starting. 352 | // This channel is used to receive the messages of the message loop. 353 | let (s, r) = mpsc::channel(); 354 | 355 | // We have to initialize `SENDER` and `BUFFER`. 356 | unsafe { 357 | SENDER = MaybeUninit::new(s); 358 | BUFFER = MaybeUninit::new(Vec::new()); 359 | } 360 | 361 | // This channel is used to retreive a potential error from the message loop's 362 | // thread. 363 | let (error_s, error_r) = mpsc::channel(); 364 | 365 | std::thread::spawn(move || { 366 | unsafe { 367 | // Retreives the module handle of the application. 368 | let h_instance = libloaderapi::GetModuleHandleW(ptr::null()); 369 | 370 | // Create the window. 371 | let class_name = OsStr::new("winput_message_loop") 372 | .encode_wide() 373 | .chain(iter::once(0)) 374 | .collect::>(); 375 | 376 | let mut wnd_class: winuser::WNDCLASSW = mem::zeroed(); 377 | wnd_class.hInstance = h_instance; 378 | wnd_class.lpszClassName = class_name.as_ptr(); 379 | wnd_class.lpfnWndProc = Some(window_proc); 380 | 381 | let class = winuser::RegisterClassW(&wnd_class); 382 | 383 | if class == 0 { 384 | error_s 385 | .send(Err(MessageLoopError::OsError( 386 | WindowsError::from_last_error(), 387 | ))) 388 | .unwrap(); 389 | return; 390 | } 391 | 392 | let h_wnd = winuser::CreateWindowExW( 393 | 0, 394 | class_name.as_ptr(), 395 | class_name.as_ptr(), 396 | 0, 397 | 0, 398 | 0, 399 | 0, 400 | 0, 401 | winuser::HWND_MESSAGE, 402 | ptr::null_mut(), 403 | h_instance, 404 | ptr::null_mut(), 405 | ); 406 | 407 | if h_wnd.is_null() { 408 | error_s 409 | .send(Err(MessageLoopError::OsError( 410 | WindowsError::from_last_error(), 411 | ))) 412 | .unwrap(); 413 | return; 414 | } 415 | 416 | // Tell the system we want to receive inputs. 417 | let mut rid: [winuser::RAWINPUTDEVICE; 2] = mem::zeroed(); 418 | // Keyboard 419 | rid[0].dwFlags = winuser::RIDEV_NOLEGACY | winuser::RIDEV_INPUTSINK; 420 | rid[0].usUsagePage = hidusage::HID_USAGE_PAGE_GENERIC; 421 | rid[0].usUsage = hidusage::HID_USAGE_GENERIC_KEYBOARD; 422 | rid[0].hwndTarget = h_wnd; 423 | // Mouse 424 | rid[1].dwFlags = winuser::RIDEV_NOLEGACY | winuser::RIDEV_INPUTSINK; 425 | rid[1].usUsagePage = hidusage::HID_USAGE_PAGE_GENERIC; 426 | rid[1].usUsage = hidusage::HID_USAGE_GENERIC_MOUSE; 427 | rid[1].hwndTarget = h_wnd; 428 | 429 | let result = winuser::RegisterRawInputDevices( 430 | rid.as_ptr(), 431 | rid.len() as _, 432 | mem::size_of::() as _, 433 | ); 434 | 435 | if result == 0 { 436 | error_s 437 | .send(Err(MessageLoopError::OsError( 438 | WindowsError::from_last_error(), 439 | ))) 440 | .unwrap(); 441 | return; 442 | } 443 | 444 | // The message loop has now started. 445 | // It is ready to receive events. 446 | STATE.store(2, Ordering::SeqCst); 447 | 448 | // Notify the main thread that the initialisation is a success. 449 | error_s.send(Ok(())).unwrap(); 450 | // After this point, the `start` function will return and the receiver 451 | // will be dropped. Using the `error_s` after this will always return an error. 452 | drop(error_s); 453 | 454 | // Start the message loop. 455 | let mut msg = mem::zeroed(); 456 | while STATE.load(Ordering::SeqCst) == 2 { 457 | let result = winuser::GetMessageW(&mut msg, h_wnd, 0, 0); 458 | 459 | if result == -1 { 460 | // An error occured in the message loop. 461 | break; 462 | } else { 463 | winuser::TranslateMessage(&msg); 464 | winuser::DispatchMessageW(&msg); 465 | } 466 | } 467 | 468 | // The message loop is now exiting. 469 | 470 | // Deinitialize the sender and the buffer. 471 | // TODO: Use `MaybeUninit::assume_init_drop` when stable. 472 | ptr::drop_in_place(SENDER.as_mut_ptr()); 473 | ptr::drop_in_place(BUFFER.as_mut_ptr()); 474 | 475 | // The message loop is now shut down. 476 | STATE.store(0, Ordering::SeqCst); 477 | } 478 | }); 479 | 480 | error_r 481 | .recv() 482 | .unwrap() 483 | .map(|()| EventReceiver { receiver: r }) 484 | } 485 | 486 | /// An event of any kind. 487 | #[derive(Clone, Copy, Debug)] 488 | pub enum Event { 489 | Keyboard { 490 | /// The virtual keycode of the key that was pressed. 491 | vk: Vk, 492 | /// The scan code of that key. 493 | scan_code: u32, 494 | /// The action that was taken on the key. 495 | action: Action, 496 | }, 497 | MouseMoveRelative { 498 | /// The x coordinate of the mouse, in [per-monitor-aware] screen coordinates. 499 | /// 500 | /// [per-monitor-aware]: https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness 501 | x: i32, 502 | /// The y coordinate of the mouse, in [per-monitor-aware] screen coordinates. 503 | /// 504 | /// [per-monitor-aware]: https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/ne-shellscalingapi-process_dpi_awareness 505 | y: i32, 506 | }, 507 | MouseMoveAbsolute { 508 | /// The x coordinate of the mouse in screen coordinates. 509 | x: f32, 510 | /// The y coordinate of the mouse in screen coordinates. 511 | y: f32, 512 | /// If this flag is set to `true`, the `x` and `y` coordinates map to the entier 513 | /// virtual desktop (this is relevent if multiple monitors are used). 514 | virtual_desk: bool, 515 | }, 516 | MouseButton { 517 | /// The action that was taken on the mouse button. 518 | action: Action, 519 | /// The mouse button involved in the event. 520 | button: Button, 521 | }, 522 | MouseWheel { 523 | /// The amount of rotation of the wheel. Positive values indicate that the wheel 524 | /// was rotated forward, away from the user; a negative value means that the wheel 525 | /// was rotated backward, toward the user. 526 | delta: f32, 527 | /// The direction of the wheel. 528 | direction: WheelDirection, 529 | }, 530 | } 531 | 532 | // Only one instance of `EventReceiver` can be created at any given time. 533 | // That only instance relies on `STATE` and `SENDER` that is only initialized 534 | // when `STATE` is `2`. 535 | // 536 | /// The result of the [`start`] function. This structure receives the messages 537 | /// received by the message loop. 538 | /// 539 | /// The message loop is automatically stopped when this structure is dropped. 540 | /// 541 | /// [`start`]: fn.start.html 542 | pub struct EventReceiver { 543 | receiver: mpsc::Receiver, 544 | } 545 | 546 | impl EventReceiver { 547 | /// Discard all the events stored in the receiver. 548 | #[inline] 549 | pub fn clear(&self) { 550 | if is_active() { 551 | while let Some(_) = self.try_next_event() {} 552 | } 553 | } 554 | 555 | /// Blocks the current thread until an event is received. 556 | #[inline] 557 | pub fn next_event(&self) -> Event { 558 | self.receiver 559 | .recv() 560 | .expect("The message loop is not active") 561 | } 562 | 563 | /// Blocks the current thread until an event is received or the given 564 | /// duration is reached. 565 | #[inline] 566 | pub fn next_event_timeout(&self, timeout: Duration) -> Option { 567 | match self.receiver.recv_timeout(timeout) { 568 | Ok(val) => Some(val), 569 | Err(mpsc::RecvTimeoutError::Timeout) => None, 570 | Err(mpsc::RecvTimeoutError::Disconnected) => { 571 | panic!("The message loop is not active") 572 | } 573 | } 574 | } 575 | 576 | /// Tries to receive an event without blocking the thread. 577 | #[inline] 578 | pub fn try_next_event(&self) -> Option { 579 | match self.receiver.try_recv() { 580 | Ok(val) => Some(val), 581 | Err(mpsc::TryRecvError::Empty) => None, 582 | Err(mpsc::TryRecvError::Disconnected) => { 583 | panic!("The message loop is not active") 584 | } 585 | } 586 | } 587 | 588 | // TODO: add `next_event_deadline` when `Reciever::recv_deadline` is stable. 589 | } 590 | 591 | impl Drop for EventReceiver { 592 | fn drop(&mut self) { 593 | // Stop the message loop. 594 | stop(); 595 | } 596 | } 597 | 598 | /// Stops the message loop. 599 | /// 600 | /// After calling this function, using the `EventReceiver` will always result 601 | /// in a panic. 602 | /// 603 | /// Be careful, if another libary already created a message loop, this function will 604 | /// still stop it. 605 | pub fn stop() { 606 | if !is_active() { 607 | return; 608 | } 609 | 610 | // If the `EventReceiver` was able to be constructed, 611 | // that means that `STATE` is currently `2`. 612 | STATE.store(3, Ordering::SeqCst); 613 | 614 | // Cleaning up the static variables is up to the message loop thread. 615 | // We just have to wait until it finishes. 616 | while STATE.load(Ordering::Acquire) != 0 { 617 | std::hint::spin_loop(); 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /src/mouse.rs: -------------------------------------------------------------------------------- 1 | use crate::error::WindowsError; 2 | 3 | #[cfg(not(feature = "minimal"))] 4 | use crate::input::{send_inputs, Input, MouseMotion, WheelDirection}; 5 | 6 | use winapi::shared::windef; 7 | use winapi::um::winuser; 8 | 9 | /// A zero-sized structure that wraps functions related to the mouse. 10 | pub struct Mouse; 11 | 12 | impl Mouse { 13 | /// Retrieve the current position of the mouse, in screen coordinates. 14 | /// 15 | /// ## Example 16 | /// 17 | /// ```rust, ignore 18 | /// use winput::Mouse; 19 | /// 20 | /// println!("The mouse is at {:?}", Mouse::position()); 21 | /// ``` 22 | pub fn position() -> Result<(i32, i32), WindowsError> { 23 | unsafe { 24 | let mut point: windef::POINT = std::mem::zeroed(); 25 | 26 | // Calling C code 27 | if winuser::GetCursorPos(&mut point) != 0 { 28 | Ok((point.x, point.y)) 29 | } else { 30 | Err(WindowsError::from_last_error()) 31 | } 32 | } 33 | } 34 | 35 | /// Sets the position of the mouse, in screen coordinates. 36 | /// 37 | /// ## Example 38 | /// 39 | /// ```rust, ignore 40 | /// use winput::{Vk, Mouse}; 41 | /// 42 | /// Mouse::set_position(50, 50).unwrap(); 43 | /// ``` 44 | pub fn set_position(x: i32, y: i32) -> Result<(), WindowsError> { 45 | unsafe { 46 | // Calling C code 47 | if winuser::SetCursorPos(x, y) == 0 { 48 | Err(WindowsError::from_last_error()) 49 | } else { 50 | Ok(()) 51 | } 52 | } 53 | } 54 | 55 | /// Synthesizes a vertical scroll event. 56 | /// 57 | /// If the function fails to synthesize the input, no error is emited and the 58 | /// function fails silently. If you wish to retreive an eventual error, use 59 | /// `send_inputs` instead. 60 | /// 61 | /// ## Example 62 | /// 63 | /// ```rust, ignore 64 | /// use winput::Mouse; 65 | /// 66 | /// Mouse::scroll(1.0).unwrap(); 67 | /// ``` 68 | #[cfg(not(feature = "minimal"))] 69 | pub fn scroll(amount: f32) { 70 | let input = Input::from_wheel(amount, WheelDirection::Vertical); 71 | send_inputs(&[input]); 72 | } 73 | 74 | /// Synthesizes a horizontal scroll event. 75 | /// 76 | /// If the function fails to synthesize the input, no error is emited and the 77 | /// function fails silently. If you wish to retreive an eventual error, use 78 | /// `send_inputs` instead. 79 | /// 80 | /// ## Example 81 | /// 82 | /// ```rust, ignore 83 | /// use winput::Mouse; 84 | /// 85 | /// Mouse::scrollh(1.0).unwrap(); 86 | /// ``` 87 | #[cfg(not(feature = "minimal"))] 88 | pub fn scrollh(amount: f32) { 89 | let input = Input::from_wheel(amount, WheelDirection::Horizontal); 90 | send_inputs(&[input]); 91 | } 92 | 93 | /// Moves the mouse relatively to its current position, in screen coordinates. 94 | /// 95 | /// If the function fails to synthesize the input, no error is emited and the 96 | /// function fails silently. If you wish to retreive an eventual error, use 97 | /// `send_inputs` instead. 98 | /// 99 | /// ## Example 100 | /// 101 | /// ```rust, ignore 102 | /// use winput::Mouse; 103 | /// 104 | /// Mouse::move_relative(100, 50).unwrap(); 105 | /// ``` 106 | #[cfg(not(feature = "minimal"))] 107 | pub fn move_relative(dx: i32, dy: i32) { 108 | let motion = MouseMotion::Relative { dx, dy }; 109 | let input = Input::from_motion(motion); 110 | send_inputs(&[input]); 111 | } 112 | 113 | /// Moves the mouse using absolute normalized coordinates. 114 | /// 115 | /// If the function fails to synthesize the input, no error is emited and the 116 | /// function fails silently. If you wish to retreive an eventual error, use 117 | /// `send_inputs` instead. 118 | /// 119 | /// ## Example 120 | /// 121 | /// ```rust, ignore 122 | /// use winput::Mouse; 123 | /// 124 | /// // Move the mouse in the center of the main monitor. 125 | /// Mouse::move_absolute(0.5, 0.5).unwrap(); 126 | /// ``` 127 | #[cfg(not(feature = "minimal"))] 128 | pub fn move_absolute(x: f32, y: f32) { 129 | let motion = MouseMotion::Absolute { 130 | x, 131 | y, 132 | virtual_desk: false, 133 | }; 134 | 135 | let input = Input::from_motion(motion); 136 | send_inputs(&[input]); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/vk.rs: -------------------------------------------------------------------------------- 1 | /// A list of all available *Virtual-Key Codes*. 2 | /// 3 | /// The official definition can be found [here][vk_link]. 4 | /// 5 | /// [vk_link]: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes 6 | #[repr(u8)] 7 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 8 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 9 | pub enum Vk { 10 | /// Left mouse button 11 | /// 12 | /// **VK_LBUTTON** = 0x01 13 | MouseLeft = 0x01, 14 | /// Right mouse button 15 | /// 16 | /// **VK_RBUTTON** = 0x02 17 | MouseRight = 0x02, 18 | /// Control-break processing 19 | /// 20 | /// **CANCEL** = 0x03 21 | Cancel = 0x03, 22 | /// Middle mouse button (three-button mouse) 23 | /// 24 | /// **VK_MBUTTON** = 0x04 25 | MouseMiddle = 0x04, 26 | /// X1 mouse button 27 | /// 28 | /// **VK_XBUTTON1** = 0x05 29 | MouseX1 = 0x05, 30 | /// X2 mouse button 31 | /// 32 | /// **VK_XBUTTON1** = 0x06 33 | MouseX2 = 0x06, 34 | /// BACKSPACE key 35 | /// 36 | /// **VK_BACK** = 0x08 37 | Backspace = 0x08, 38 | /// TAB key 39 | /// 40 | /// **VK_TAB** = 0x09 41 | Tab = 0x09, 42 | /// CLEAR key 43 | /// 44 | /// **VK_CLEAR** = 0x0c 45 | Clear = 0x0c, 46 | /// ENTER key 47 | /// 48 | /// **VK_RETURN** = 0x0d 49 | Enter = 0x0d, 50 | /// SHIFT key 51 | /// 52 | /// **VK_SHIFT** = 0x10 53 | Shift = 0x10, 54 | /// CTRL key 55 | /// 56 | /// **VK_CONTROL** = 0x11 57 | Control = 0x11, 58 | /// ALT key 59 | /// 60 | /// **VK_MENU** = 0x12 61 | Alt = 0x12, 62 | /// PAUSE key 63 | /// 64 | /// **VK_PAUSE** = 0x13 65 | Pause = 0x13, 66 | /// CAPS LOCK key 67 | /// 68 | /// **VK_CAPITAL** = 0x14 69 | CapsLock = 0x14, 70 | /// IME Kana mode & IME Hangul mode 71 | /// 72 | /// **VK_KANA** = **VK_HANGUL** = 0x15 73 | Kana = 0x15, 74 | /// IME On 75 | /// 76 | /// **VK_IME_ON** = 0x16 77 | ImeOn = 0x16, 78 | /// IME Junja mode 79 | /// 80 | /// **VK_JUNJA** = 0x17 81 | Junja = 0x17, 82 | /// IME final mode 83 | /// 84 | /// **VK_FINAL** = 0x18 85 | Final = 0x18, 86 | /// IME Kanji mode & IME Hanja mode 87 | /// 88 | /// **VK_KANJI** = **VK_HANJA** = 0x19 89 | Kanji = 0x19, 90 | /// IME Off 91 | /// 92 | /// **VK_Ime** = 0x1a 93 | ImeOff = 0x1a, 94 | /// ESC key 95 | /// 96 | /// **VK_ESCAPE** = 0x1b 97 | Escape = 0x1b, 98 | /// IME convert 99 | /// 100 | /// **VK_CONVERT** = 0x1c 101 | Convert = 0x1c, 102 | /// IME nonconvert 103 | /// 104 | /// **VK_NONCONVERT** = 0x1d 105 | NonConvert = 0x1d, 106 | /// IME accept 107 | /// 108 | /// **VK_ACCEPT** = 0x1e 109 | Accept = 0x1e, 110 | /// IME mode change request 111 | /// 112 | /// **VK_MODECHANGE** = 0x1f 113 | ModeChange = 0x1f, 114 | /// SPACEBAR 115 | /// 116 | /// **VK_SPACE** = 0x20 117 | Space = 0x20, 118 | /// PAGE UP key 119 | /// 120 | /// **VK_PRIOR** = 0x21 121 | PageUp = 0x21, 122 | /// PAGE DOWN key 123 | /// 124 | /// **VK_NEXT** = 0x22 125 | PageDown = 0x22, 126 | /// END key 127 | /// 128 | /// **VK_END** = 0x23 129 | End = 0x23, 130 | /// HOME key 131 | /// 132 | /// **VK_HOME** = 0x24 133 | Home = 0x24, 134 | /// LEFT ARROW key 135 | /// 136 | /// **VK_LEFT** = 0x25, 137 | LeftArrow = 0x25, 138 | /// UP ARROW key 139 | /// 140 | /// **VK_UP** = 0x26 141 | UpArrow = 0x26, 142 | /// RIGHT ARROW key 143 | /// 144 | /// **VK_RIGHT** = 0x27 145 | RightArrow = 0x27, 146 | /// DOWN ARROW key 147 | /// 148 | /// **VK_DOWN** = 0x28 149 | DownArrow = 0x28, 150 | /// SELECT key 151 | /// 152 | /// **VK_SELECT** = 0x29 153 | Select = 0x29, 154 | /// PRINT key 155 | /// 156 | /// **VK_PRINT** = 0x2a 157 | Print = 0x2a, 158 | /// EXECUTE key 159 | /// 160 | /// **VK_EXECUTE** = 0x2b 161 | Execute = 0x2b, 162 | /// PRINT SCREEN key 163 | /// 164 | /// **VK_SNAPSHOT** = 0x2c 165 | PrintScreen = 0x2c, 166 | /// INS key 167 | /// 168 | /// **VK_INSERT** = 0x2d 169 | Insert = 0x2d, 170 | /// DEL key 171 | /// 172 | /// **VK_DELETE** = 0x2e 173 | Delete = 0x2e, 174 | /// HELP key 175 | /// 176 | /// **VK_HELP** = 0x2f, 177 | Help = 0x2f, 178 | /// 0 key 179 | _0 = b'0', 180 | /// 1 key 181 | _1 = b'1', 182 | /// 2 key 183 | _2 = b'2', 184 | /// 3 key 185 | _3 = b'3', 186 | /// 4 key 187 | _4 = b'4', 188 | /// 5 key 189 | _5 = b'5', 190 | /// 6 key 191 | _6 = b'6', 192 | /// 7 key 193 | _7 = b'7', 194 | /// 8 key 195 | _8 = b'8', 196 | /// 9 key 197 | _9 = b'9', 198 | /// A key 199 | A = b'A', 200 | /// B key 201 | B = b'B', 202 | /// C key 203 | C = b'C', 204 | /// D key 205 | D = b'D', 206 | /// E key 207 | E = b'E', 208 | /// F key 209 | F = b'F', 210 | /// G key 211 | G = b'G', 212 | /// H key 213 | H = b'H', 214 | /// I key 215 | I = b'I', 216 | /// J key 217 | J = b'J', 218 | /// K key 219 | K = b'K', 220 | /// L key 221 | L = b'L', 222 | /// M key 223 | M = b'M', 224 | /// N key 225 | N = b'N', 226 | /// O key 227 | O = b'O', 228 | /// P key 229 | P = b'P', 230 | /// Q key 231 | Q = b'Q', 232 | /// R key 233 | R = b'R', 234 | /// S key 235 | S = b'S', 236 | /// T key 237 | T = b'T', 238 | /// U key 239 | U = b'U', 240 | /// V key 241 | V = b'V', 242 | /// W key 243 | W = b'W', 244 | /// X key 245 | X = b'X', 246 | /// Y key 247 | Y = b'Y', 248 | /// Z key 249 | Z = b'Z', 250 | /// Left Windows key (Natural keyboard) 251 | /// 252 | /// **VK_LWIN** = 0x5b, 253 | LeftWin = 0x5b, 254 | /// Right Windows key (Natural keyboard) 255 | /// 256 | /// **VK_RWIN** = 0x5c 257 | RightWin = 0x5c, 258 | /// Applications key (Natural keyboard) 259 | /// 260 | /// **VK_APPS** = 0x5d 261 | Apps = 0x5d, 262 | /// Computer Sleep key 263 | /// 264 | /// **VK_SLEEP** = 0x5f 265 | Sleep = 0x5f, 266 | /// Numeric keypad 0 key 267 | /// 268 | /// **VK_NUMPAD0** = 0x60 269 | Numpad0 = 0x60, 270 | /// Numeric keypad 1 key 271 | /// 272 | /// **VK_NUMPAD1** = 0x61 273 | Numpad1 = 0x61, 274 | /// Numeric keypad 2 key 275 | /// 276 | /// **VK_NUMPAD2** = 0x62 277 | Numpad2 = 0x62, 278 | /// Numeric keypad 3 key 279 | /// 280 | /// **VK_NUMPAD3** = 0x63 281 | Numpad3 = 0x63, 282 | /// Numeric keypad 4 key 283 | /// 284 | /// **VK_NUMPAD4** = 0x64 285 | Numpad4 = 0x64, 286 | /// Numeric keypad 5 key 287 | /// 288 | /// **VK_NUMPAD5** = 0x65 289 | Numpad5 = 0x65, 290 | /// Numeric keypad 6 key 291 | /// 292 | /// **VK_NUMPAD6** = 0x66 293 | Numpad6 = 0x66, 294 | /// Numeric keypad 7 key 295 | /// 296 | /// **VK_NUMPAD7** = 0x67 297 | Numpad7 = 0x67, 298 | /// Numeric keyapd 8 key 299 | /// 300 | /// **VK_NUMPAD8** = 0x68 301 | Numpad8 = 0x68, 302 | /// Numeric keypad 9 key 303 | /// 304 | /// **VK_NUMPAD9** = 0x69 305 | Numpad9 = 0x69, 306 | /// Multiply key 307 | /// 308 | /// **VK_MULTIPLY** = 0x6a 309 | Multiply = 0x6a, 310 | /// Add key 311 | /// 312 | /// **VK_ADD** = 0x6b, 313 | Add = 0x6b, 314 | /// Separator key 315 | /// 316 | /// **VK_SEPARATOR** = 0x6c, 317 | Separator = 0x6c, 318 | /// Subtract key 319 | /// 320 | /// **VK_SUBTRACT** = 0x6d 321 | Subtract = 0x6d, 322 | /// Decimal key 323 | /// 324 | /// **VK_DECIMAL** = 0x6e 325 | Decimal = 0x6e, 326 | /// Divide key 327 | /// 328 | /// **VK_DIVIDE** = 0x6f 329 | Divide = 0x6f, 330 | /// F1 key 331 | /// 332 | /// **VK_F1** = 0x70 333 | F1 = 0x70, 334 | /// F2 key 335 | /// 336 | /// **VK_F2** = 0x71 337 | F2 = 0x71, 338 | /// F3 key 339 | /// 340 | /// **VK_F3** = 0x72 341 | F3 = 0x72, 342 | /// F4 key 343 | /// 344 | /// **VK_F4** = 0x73 345 | F4 = 0x73, 346 | /// F5 key 347 | /// 348 | /// **VK_F5** = 0x74 349 | F5 = 0x74, 350 | /// F6 key 351 | /// 352 | /// **VK_F6** = 0x75 353 | F6 = 0x75, 354 | /// F7 key 355 | /// 356 | /// **VK_F7** = 0x76 357 | F7 = 0x76, 358 | /// F8 key 359 | /// 360 | /// **VK_F8** = 0x77, 361 | F8 = 0x77, 362 | /// F9 key 363 | /// 364 | /// **VK_F9** = 0x78, 365 | F9 = 0x78, 366 | /// F10 key 367 | /// 368 | /// **VK_F10** = 0x79, 369 | F10 = 0x79, 370 | /// F11 key 371 | /// 372 | /// **VK_F11** = 0x7a 373 | F11 = 0x7a, 374 | /// F12 key 375 | /// 376 | /// **VK_F12** = 0x7b 377 | F12 = 0x7b, 378 | /// F13 key 379 | /// 380 | /// **VK_F13** = 0x7c 381 | F13 = 0x7c, 382 | /// F14 key 383 | /// 384 | /// **VK_F14** = 0x7d 385 | F14 = 0x7d, 386 | /// F15 key 387 | /// 388 | /// **VK_F15** = 0x7e 389 | F15 = 0x7e, 390 | /// F16 key 391 | /// 392 | /// **VK_F16** = 0x7f 393 | F16 = 0x7f, 394 | /// F17 key 395 | /// 396 | /// **VK_F17** = 0x80 397 | F17 = 0x80, 398 | /// F18 key 399 | /// 400 | /// **VK_F18** = 0x81, 401 | F18 = 0x81, 402 | /// F19 key 403 | /// 404 | /// **VK_F19** = 0x82, 405 | F19 = 0x82, 406 | /// F20 key 407 | /// 408 | /// **VK_F20** = 0x83, 409 | F20 = 0x83, 410 | /// F21 key 411 | /// 412 | /// **VK_F21** = 0x84, 413 | F21 = 0x84, 414 | /// F22 key 415 | /// 416 | /// **VK_F22** = 0x85, 417 | F22 = 0x85, 418 | /// F23 key 419 | /// 420 | /// **VK_F23** = 0x86, 421 | F23 = 0x86, 422 | /// F24 key 423 | /// 424 | /// **VK_F24** = 0x87, 425 | F24 = 0x87, 426 | /// NUM LOCK key 427 | /// 428 | /// **VK_NUMLOCK** = 0x90 429 | Numlock = 0x90, 430 | /// SCROLL LOCK key 431 | /// 432 | /// **VK_SCROLL** = 0x91 433 | Scroll = 0x91, 434 | /// Left SHIFT key 435 | /// 436 | /// **VK_LSHIFT** = 0xa0 437 | LeftShift = 0xa0, 438 | /// Right SHIFT key 439 | /// 440 | /// **VK_RSHIFT** = 0xa1 441 | RightShift = 0xa1, 442 | /// Left CONTROL key 443 | /// 444 | /// **VK_LCONTROL** = 0xa2 445 | LeftControl = 0xa2, 446 | /// Right CONTROL key 447 | /// 448 | /// **VK_RCONTROL** = 0xa3 449 | RightControl = 0xa3, 450 | /// Left MENU key 451 | /// 452 | /// **VK_LMENU** = 0xa4 453 | LeftMenu = 0xa4, 454 | /// Right MENU key 455 | /// 456 | /// **VK_RMENU** = 0xa5 457 | RightMenu = 0xa5, 458 | /// Browser Back key 459 | /// 460 | /// **VK_BROWSER_BACK** = 0xa6 461 | BrowserBack = 0xa6, 462 | /// Browser Forward key 463 | /// 464 | /// **VK_BROWSER_FORWARD** = 0xa7 465 | BrowserForward = 0xa7, 466 | /// Browser Refresh key 467 | /// 468 | /// **VK_BROWSER_REFRESH** = 0xa8 469 | BrowserRefresh = 0xa8, 470 | /// Browser Stop key 471 | /// 472 | /// **VK_BROWSER_STOP** = 0xa9 473 | BrowserStop = 0xa9, 474 | /// Browser Search key 475 | /// 476 | /// **VK_BROWSER_SEARCH** = 0xaa 477 | BrowserSearch = 0xaa, 478 | /// Browser Favorites key 479 | /// 480 | /// **VK_BROWSER_FAVORITES** = 0xab 481 | BrowserFavorites = 0xab, 482 | /// Browser Start and Home key 483 | /// 484 | /// **VK_BROWSER_HOME** = 0xac 485 | BrowserHome = 0xac, 486 | /// Volume Mute key 487 | /// 488 | /// **VK_VOLUME_MUTE** = 0xad 489 | VolumeMute = 0xad, 490 | /// Volume Down key 491 | /// 492 | /// **VK_VOLUME_DOWN** = 0xae 493 | VolumeDown = 0xae, 494 | /// Volume Up key 495 | /// 496 | /// **VK_VOLUME_UP** = 0xaf 497 | VolumeUp = 0xaf, 498 | /// Next Track key 499 | /// 500 | /// **VK_MEDIA_NEXT_TRACK** = 0xb0 501 | NextTrack = 0xb0, 502 | /// Prev Track key 503 | /// 504 | /// **VK_MEDIA_PREV_TRACK** = 0xb1 505 | PrevTrack = 0xb1, 506 | /// Stop Media key 507 | /// 508 | /// **VK_MEDIA_STOP** = 0xb2 509 | MediaStop = 0xb2, 510 | /// Play/Pause Media key 511 | /// 512 | /// **VK_MEDIA_PLAY_PAUSE** = 0xb3 513 | MediaPlayPause = 0xb3, 514 | /// Start Mail key 515 | /// 516 | /// **VK_LAUNCH_MAIL** = 0xb4 517 | StartMail = 0xb4, 518 | /// Select Media key 519 | /// 520 | /// **VK_LAUNCH_MEDIA_SELECT** = 0xb5 521 | SelectMedia = 0xb5, 522 | /// Start Application 1 key 523 | /// 524 | /// **VK_LAUNCH_APP1** = 0xb6 525 | StartApp1 = 0xb6, 526 | /// Start Application 2 key 527 | /// 528 | /// **VK_LAUNCH_APP2** = 0xb7 529 | StartApp2 = 0xb7, 530 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 531 | /// keyboard, the `;:` key. 532 | /// 533 | /// **VK_OEM_1** = 0xba 534 | Oem1 = 0xba, 535 | /// For any country/region, the `+` key. 536 | /// 537 | /// **VK_OEM_PLUS** = 0xbb 538 | Plus = 0xbb, 539 | /// For any country/region, the `,` key. 540 | /// 541 | /// **VK_OEM_COMMA** = 0xbc 542 | Comma = 0xbc, 543 | /// For any country/region, the `-` key. 544 | /// 545 | /// **VK_OEM_MINUS** = 0xbd 546 | Minus = 0xbd, 547 | /// For any country/region, the `.` key. 548 | /// 549 | /// **VK_OEM_PERIOD** = 0xbe 550 | Period = 0xbe, 551 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 552 | /// keyboard, the `\?` key. 553 | /// 554 | /// **VK_OEM_2** = 0xbf 555 | Oem2 = 0xbf, 556 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 557 | /// keyboard, the ``~` key. 558 | /// 559 | /// **VK_OEM_3** = 0xc0 560 | Oem3 = 0xc0, 561 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 562 | /// keyboard, the `[{` key. 563 | /// 564 | /// **VK_OEM_4** = 0xdb 565 | Oem4 = 0xdb, 566 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 567 | /// keyboard, the `\|` key. 568 | /// 569 | /// **VK_OEM_5** = 0xdc 570 | Oem5 = 0xdc, 571 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 572 | /// keyboard, the `]}` key. 573 | /// 574 | /// **VK_OEM_6** = 0xdd 575 | Oem6 = 0xdd, 576 | /// Used for miscellaneous characters; it can vary by keyboard. For the US standard 577 | /// keyboard, the `'"` key. 578 | /// 579 | /// **VK_OEM_7** = 0xde 580 | Oem7 = 0xde, 581 | /// Used for miscellaneous characters; it can vary by keyboard. 582 | /// 583 | /// **VK_OEM_8** = 0xdd 584 | Oem8 = 0xdf, 585 | /// Either the angle bracket key or the backslash key on the RT 102-key keyboard. 586 | /// 587 | /// **VK_OEM_102** = 0xe2 588 | Oem102 = 0xe2, 589 | /// IME PROCESS key 590 | /// 591 | /// **VK_PROCESSKEY** = 0xe5 592 | ImeProcess = 0xe5, 593 | /// Attn key 594 | /// 595 | /// **VK_ATTN** = 0xf6 596 | Attn = 0xf6, 597 | /// CrSel key 598 | /// 599 | /// **VK_CRSEL** = 0xf7 600 | CrSel = 0xf7, 601 | /// ExSel key 602 | /// 603 | /// **VK_EXSEL** = 0xf8 604 | ExSel = 0xf8, 605 | /// Erase EOF key 606 | /// 607 | /// **VK_EREOR** = 0xf9 608 | EraseEof = 0xf9, 609 | /// Play key 610 | /// 611 | /// **VK_PLAY** = 0xfa 612 | Play = 0xfa, 613 | /// Zoom key 614 | /// 615 | /// **VK_ZOOM** = 0xfb 616 | Zoom = 0xfb, 617 | /// PA1 key 618 | /// 619 | /// **VK_PA1** = 0xfd 620 | Pa1 = 0xfd, 621 | /// Clear key 622 | /// 623 | /// **VK_OEM_CLEAR** = 0xfe 624 | OemClear = 0xfe, 625 | } 626 | 627 | macro_rules! from_vk_for_num { 628 | ($($t:ty)+) => { 629 | $( 630 | impl From for $t { 631 | #[inline(always)] 632 | fn from(vk: Vk) -> Self { 633 | vk as Self 634 | } 635 | } 636 | )+ 637 | }; 638 | } 639 | 640 | from_vk_for_num!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128); 641 | 642 | impl Vk { 643 | /// Creates a Virtual-Key Code from the given `u8`. 644 | /// 645 | /// ## Safety 646 | /// 647 | /// This function is safe as long as the given number `n` is a valid Virtual-Key Code. 648 | /// Providing a invalid number is *undefined behaviour*. 649 | /// 650 | /// ## Example 651 | /// 652 | /// ```rust 653 | /// use winput::Vk; 654 | /// 655 | /// // SAFETY: `0x0d` is a valid Virtual-Key Code. 656 | /// let vk = unsafe { Vk::from_u8(0x0d) }; 657 | /// assert_eq!(vk, Vk::Enter); 658 | /// ``` 659 | /// 660 | /// A safe way to use this function is to convert a Virtual-Key Code into a number. 661 | /// 662 | /// ```rust 663 | /// use winput::Vk; 664 | /// 665 | /// let n = Vk::Escape.into_u8(); 666 | /// 667 | /// // SAFETY: `n` is a valid Virtual-Key Code. 668 | /// let vk = unsafe { Vk::from_u8(n) }; 669 | /// assert_eq!(vk, Vk::Escape); 670 | /// ``` 671 | #[inline(always)] 672 | pub unsafe fn from_u8(n: u8) -> Self { 673 | // SAFETY: The caller must ensure that the given `u8` represents a valid 674 | // Virtual-Key Code. 675 | std::mem::transmute(n) 676 | } 677 | 678 | /// Converts this Virtual-Key Code into a `u8`. 679 | /// 680 | /// ## Example 681 | /// 682 | /// ```rust 683 | /// use winput::Vk; 684 | /// 685 | /// let value = Vk::Enter.into_u8(); 686 | /// assert_eq!(value, 0x0d); 687 | /// ``` 688 | #[inline(always)] 689 | pub fn into_u8(self) -> u8 { 690 | self.into() 691 | } 692 | 693 | /// Checks if this Virtual-Key Code is currently being pressed. 694 | /// 695 | /// ## Example 696 | /// 697 | /// ```rust, ignore 698 | /// use winput::Vk; 699 | /// 700 | /// if Vk::Z.is_down() { 701 | /// println!("The Z key is down!"); 702 | /// } else { 703 | /// println!("The Z key is not down :("); 704 | /// } 705 | /// ``` 706 | pub fn is_down(self) -> bool { 707 | use winapi::um::winuser::GetAsyncKeyState; 708 | 709 | const MASK: u16 = 0x8000; 710 | 711 | // Calling C code 712 | let state = unsafe { GetAsyncKeyState(self.into()) } as u16; 713 | state & MASK == MASK 714 | } 715 | 716 | /// Checks if the given key is currently toggled. 717 | /// 718 | /// For example, the `Vk::CapsLock` can be either on or off (appart from being 719 | /// down or up). 720 | /// 721 | /// ## Example 722 | /// 723 | /// ```rust, ignore 724 | /// use winput::Vk; 725 | /// 726 | /// if Vk::CapsLock.is_toggled() { 727 | /// println!("Do you like writing in all caps?"); 728 | /// } else { 729 | /// println!("I knew it! No one ever uses this key!"); 730 | /// } 731 | /// ``` 732 | pub fn is_toggled(self) -> bool { 733 | use winapi::um::winuser::GetKeyState; 734 | 735 | const MASK: u16 = 0x0001; 736 | 737 | // Calling C code 738 | let state = unsafe { GetKeyState(self.into()) } as u16; 739 | state & MASK == MASK 740 | } 741 | } 742 | --------------------------------------------------------------------------------