//! Enigo lets you simulate mouse and keyboard input-events as if they were //! made by the actual hardware. The goal is to make it available on different //! operating systems like Linux, macOS and Windows – possibly many more but //! [Redox](https://redox-os.org/) and *BSD are planned. Please see the //! [Repo](https://github.com/enigo-rs/enigo) for the current status. //! //! I consider this library in an early alpha status, the API will change in //! in the future. The keyboard handling is far from being very usable. I plan //! to build a simple //! [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) //! that will resemble something like: //! //! `"hello {+SHIFT}world{-SHIFT} and break line{ENTER}"` //! //! The current status is that you can just print //! [unicode](http://unicode.org/) //! characters like [emoji](http://getemoji.com/) without the `{+SHIFT}` //! [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) //! or any other "special" key on the Linux, macOS and Windows operating system. //! //! Possible use cases could be for testing user interfaces on different //! plattforms, //! building remote control applications or just automating tasks for user //! interfaces unaccessible by a public API or scripting laguage. //! //! For the keyboard there are currently two modes you can use. The first mode //! is represented by the [key_sequence]() function //! its purpose is to simply write unicode characters. This is independent of //! the keyboardlayout. Please note that //! you're not be able to use modifier keys like Control //! to influence the outcome. If you want to use modifier keys to e.g. //! copy/paste //! use the Layout variant. Please note that this is indeed layout dependent. //! # Examples //! ```no_run //! use enigo::*; //! let mut enigo = Enigo::new(); //! //paste //! enigo.key_down(Key::Control); //! enigo.key_click(Key::Layout('v')); //! enigo.key_up(Key::Control); //! ``` //! //! ```no_run //! use enigo::*; //! let mut enigo = Enigo::new(); //! enigo.mouse_move_to(500, 200); //! enigo.mouse_down(MouseButton::Left); //! enigo.mouse_move_relative(100, 100); //! enigo.mouse_up(MouseButton::Left); //! enigo.key_sequence("hello world"); //! ``` #![deny(missing_docs)] #[cfg(target_os = "macos")] #[macro_use] extern crate objc; // TODO(dustin) use interior mutability not &mut self #[cfg(target_os = "windows")] mod win; #[cfg(target_os = "windows")] pub use win::Enigo; #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] pub use macos::Enigo; #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "linux")] pub use crate::linux::Enigo; /// DSL parser module pub mod dsl; #[cfg(feature = "with_serde")] #[macro_use] extern crate serde_derive; #[cfg(feature = "with_serde")] extern crate serde; /// pub type ResultType = std::result::Result<(), Box>; #[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq)] /// MouseButton represents a mouse button, /// and is used in for example /// [mouse_click](trait.MouseControllable.html#tymethod.mouse_click). /// WARNING: Types with the prefix Scroll /// IS NOT intended to be used, and may not work on /// all operating systems. pub enum MouseButton { /// Left mouse button Left, /// Middle mouse button Middle, /// Right mouse button Right, /// Scroll up button ScrollUp, /// Left right button ScrollDown, /// Left right button ScrollLeft, /// Left right button ScrollRight, } /// Representing an interface and a set of mouse functions every /// operating system implementation _should_ implement. pub trait MouseControllable { /// Lets the mouse cursor move to the specified x and y coordinates. /// /// The topleft corner of your monitor screen is x=0 y=0. Move /// the cursor down the screen by increasing the y and to the right /// by increasing x coordinate. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_move_to(500, 200); /// ``` fn mouse_move_to(&mut self, x: i32, y: i32); /// Lets the mouse cursor move the specified amount in the x and y /// direction. /// /// The amount specified in the x and y parameters are added to the /// current location of the mouse cursor. A positive x values lets /// the mouse cursor move an amount of `x` pixels to the right. A negative /// value for `x` lets the mouse cursor go to the left. A positive value /// of y /// lets the mouse cursor go down, a negative one lets the mouse cursor go /// up. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_move_relative(100, 100); /// ``` fn mouse_move_relative(&mut self, x: i32, y: i32); /// Push down one of the mouse buttons /// /// Push down the mouse button specified by the parameter `button` of /// type [MouseButton](enum.MouseButton.html) /// and holds it until it is released by /// [mouse_up](trait.MouseControllable.html#tymethod.mouse_up). /// Calls to [mouse_move_to](trait.MouseControllable.html#tymethod. /// mouse_move_to) or /// [mouse_move_relative](trait.MouseControllable.html#tymethod. /// mouse_move_relative) /// will work like expected and will e.g. drag widgets or highlight text. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_down(MouseButton::Left); /// ``` fn mouse_down(&mut self, button: MouseButton) -> ResultType; /// Lift up a pushed down mouse button /// /// Lift up a previously pushed down button (by invoking /// [mouse_down](trait.MouseControllable.html#tymethod.mouse_down)). /// If the button was not pushed down or consecutive calls without /// invoking [mouse_down](trait.MouseControllable.html#tymethod.mouse_down) /// will emit lift up events. It depends on the /// operating system whats actually happening – my guess is it will just /// get ignored. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_up(MouseButton::Right); /// ``` fn mouse_up(&mut self, button: MouseButton); /// Click a mouse button /// /// it's esentially just a consecutive invokation of /// [mouse_down](trait.MouseControllable.html#tymethod.mouse_down) followed /// by a [mouse_up](trait.MouseControllable.html#tymethod.mouse_up). Just /// for /// convenience. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_click(MouseButton::Right); /// ``` fn mouse_click(&mut self, button: MouseButton); /// Scroll the mouse (wheel) left or right /// /// Positive numbers for length lets the mouse wheel scroll to the right /// and negative ones to the left. The value that is specified translates /// to `lines` defined by the operating system and is essentially one 15° /// (click)rotation on the mouse wheel. How many lines it moves depends /// on the current setting in the operating system. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_scroll_x(2); /// ``` fn mouse_scroll_x(&mut self, length: i32); /// Scroll the mouse (wheel) up or down /// /// Positive numbers for length lets the mouse wheel scroll down /// and negative ones up. The value that is specified translates /// to `lines` defined by the operating system and is essentially one 15° /// (click)rotation on the mouse wheel. How many lines it moves depends /// on the current setting in the operating system. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.mouse_scroll_y(2); /// ``` fn mouse_scroll_y(&mut self, length: i32); } /// A key on the keyboard. /// For alphabetical keys, use Key::Layout for a system independent key. /// If a key is missing, you can use the raw keycode with Key::Raw. #[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Key { /// alt key on Linux and Windows (option key on macOS) Alt, /// backspace key Backspace, /// caps lock key CapsLock, #[deprecated(since = "0.0.12", note = "now renamed to Meta")] /// command key on macOS (super key on Linux, windows key on Windows) Command, /// control key Control, /// delete key Delete, /// down arrow key DownArrow, /// end key End, /// escape key (esc) Escape, /// F1 key F1, /// F10 key F10, /// F11 key F11, /// F12 key F12, /// F2 key F2, /// F3 key F3, /// F4 key F4, /// F5 key F5, /// F6 key F6, /// F7 key F7, /// F8 key F8, /// F9 key F9, /// home key Home, /// left arrow key LeftArrow, /// meta key (also known as "windows", "super", and "command") Meta, /// option key on macOS (alt key on Linux and Windows) Option, // deprecated, use Alt instead /// page down key PageDown, /// page up key PageUp, /// return key Return, /// right arrow key RightArrow, /// shift key Shift, /// space key Space, #[deprecated(since = "0.0.12", note = "now renamed to Meta")] /// super key on linux (command key on macOS, windows key on Windows) Super, /// tab key (tabulator) Tab, /// up arrow key UpArrow, #[deprecated(since = "0.0.12", note = "now renamed to Meta")] /// windows key on Windows (super key on Linux, command key on macOS) Windows, /// Numpad0, /// Numpad1, /// Numpad2, /// Numpad3, /// Numpad4, /// Numpad5, /// Numpad6, /// Numpad7, /// Numpad8, /// Numpad9, /// Cancel, /// Clear, /// Pause, /// Kana, /// Hangul, /// Junja, /// Final, /// Hanja, /// Kanji, /// Convert, /// Select, /// Print, /// Execute, /// Snapshot, /// Insert, /// Help, /// Sleep, /// Separator, /// VolumeUp, /// VolumeDown, /// Mute, /// Scroll, /// scroll lock NumLock, /// RWin, /// Apps, /// Multiply, /// Add, /// Subtract, /// Decimal, /// Divide, /// Equals, /// NumpadEnter, /// RightShift, /// RightControl, /// RightAlt, /// /// Function, /// mac /// keyboard layout dependent key Layout(char), /// raw keycode eg 0x38 Raw(u16), } /// Representing an interface and a set of keyboard functions every /// operating system implementation _should_ implement. pub trait KeyboardControllable { /// Types the string parsed with DSL. /// /// Typing {+SHIFT}hello{-SHIFT} becomes HELLO. /// TODO: Full documentation fn key_sequence_parse(&mut self, sequence: &str) where Self: Sized, { self.key_sequence_parse_try(sequence) .expect("Could not parse sequence"); } /// Same as key_sequence_parse except returns any errors fn key_sequence_parse_try(&mut self, sequence: &str) -> Result<(), dsl::ParseError> where Self: Sized, { dsl::eval(self, sequence) } /// Types the string /// /// Emits keystrokes such that the given string is inputted. /// /// You can use many unicode here like: ❤️. This works /// regadless of the current keyboardlayout. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// enigo.key_sequence("hello world ❤️"); /// ``` fn key_sequence(&mut self, sequence: &str); /// presses a given key down fn key_down(&mut self, key: Key) -> ResultType; /// release a given key formally pressed down by /// [key_down](trait.KeyboardControllable.html#tymethod.key_down) fn key_up(&mut self, key: Key); /// Much like the /// [key_down](trait.KeyboardControllable.html#tymethod.key_down) and /// [key_up](trait.KeyboardControllable.html#tymethod.key_up) /// function they're just invoked consecutively fn key_click(&mut self, key: Key); /// fn get_key_state(&mut self, key: Key) -> bool; } #[cfg(any(target_os = "android", target_os = "ios"))] struct Enigo; impl Enigo { /// Constructs a new `Enigo` instance. /// /// # Example /// /// ```no_run /// use enigo::*; /// let mut enigo = Enigo::new(); /// ``` pub fn new() -> Self { #[cfg(any(target_os = "android", target_os = "ios"))] return Enigo {}; #[cfg(not(any(target_os = "android", target_os = "ios")))] Self::default() } } use std::fmt; impl fmt::Debug for Enigo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Enigo") } } #[cfg(test)] mod tests { use super::*; #[test] fn test_get_key_state() { let mut enigo = Enigo::new(); let keys = [Key::CapsLock, Key::NumLock]; for k in keys.iter() { enigo.key_click(k.clone()); let a = enigo.get_key_state(k.clone()); enigo.key_click(k.clone()); let b = enigo.get_key_state(k.clone()); assert!(a != b); } let keys = [Key::Control, Key::Alt, Key::Shift]; for k in keys.iter() { enigo.key_down(k.clone()).ok(); let a = enigo.get_key_state(k.clone()); enigo.key_up(k.clone()); let b = enigo.get_key_state(k.clone()); assert!(a != b); } } }