From 9fdf43f83d50353dc8d1716c18f754e2cb4ba941 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 22:23:43 +0200 Subject: [PATCH 1/9] started refactoring input --- src/input.rs | 90 +-------- src/input/input.rs | 158 --------------- src/input/unix_input.rs | 11 +- src/input/windows_input.rs | 36 ++-- src/lib.rs | 395 ++++++++++++++++++++++++++++++++++++- 5 files changed, 420 insertions(+), 270 deletions(-) delete mode 100644 src/input/input.rs diff --git a/src/input.rs b/src/input.rs index 4f7cf38..d1f515d 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,18 +1,12 @@ //! A module that contains all the actions related to reading input from the terminal. //! Like reading a line, reading a character and reading asynchronously. -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - use crossterm_utils::Result; -pub use self::input::{input, TerminalInput}; #[cfg(unix)] -pub use self::unix_input::{AsyncReader, SyncReader}; +pub use self::unix_input::{AsyncReader, SyncReader, UnixInput}; #[cfg(windows)] -pub use self::windows_input::{AsyncReader, SyncReader}; - -mod input; +pub use self::windows_input::{AsyncReader, SyncReader, WindowsInput}; #[cfg(unix)] mod unix_input; @@ -27,7 +21,7 @@ mod windows_input; /// /// This trait is implemented for Windows and UNIX systems. /// Unix is using the 'TTY' and windows is using 'libc' C functions to read the input. -trait ITerminalInput { +pub(crate) trait ITerminalInput { /// Read one character from the user input fn read_char(&self) -> Result; /// Read the input asynchronously from the user. @@ -39,81 +33,3 @@ trait ITerminalInput { fn enable_mouse_mode(&self) -> Result<()>; fn disable_mouse_mode(&self) -> Result<()>; } - -/// Enum to specify which input event has occurred. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone)] -pub enum InputEvent { - /// A single key or a combination is pressed. - Keyboard(KeyEvent), - /// A mouse event occurred. - Mouse(MouseEvent), - /// A unsupported event has occurred. - Unsupported(Vec), - /// An unknown event has occurred. - Unknown, -} - -/// Enum to specify which mouse event has occurred. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Copy)] -pub enum MouseEvent { - /// A mouse press has occurred, this contains the pressed button and the position of the press. - Press(MouseButton, u16, u16), - /// A mouse button was released. - Release(u16, u16), - /// A mouse button was hold. - Hold(u16, u16), - /// An unknown mouse event has occurred. - Unknown, -} - -/// Enum to define mouse buttons. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Copy)] -pub enum MouseButton { - /// Left mouse button - Left, - /// Right mouse button - Right, - /// Middle mouse button - Middle, - /// Scroll up - WheelUp, - /// Scroll down - WheelDown, -} - -/// Enum with different key or key combinations. -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum KeyEvent { - Backspace, - Enter, - Left, - Right, - Up, - Down, - Home, - End, - PageUp, - PageDown, - Tab, - BackTab, - Delete, - Insert, - F(u8), - Char(char), - Alt(char), - Ctrl(char), - Null, - Esc, - CtrlUp, - CtrlDown, - CtrlRight, - CtrlLeft, - ShiftUp, - ShiftDown, - ShiftRight, - ShiftLeft, -} diff --git a/src/input/input.rs b/src/input/input.rs deleted file mode 100644 index aa027c4..0000000 --- a/src/input/input.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! A module that contains all the actions related to reading input from the terminal. -//! Like reading a line, reading a character and reading asynchronously. - -use std::io; - -use crossterm_utils::Result; - -#[cfg(unix)] -use super::unix_input::{AsyncReader, SyncReader, UnixInput}; -#[cfg(windows)] -use super::windows_input::{AsyncReader, SyncReader, WindowsInput}; -use super::ITerminalInput; - -/// Allows you to read user input. -/// -/// # Features: -/// -/// - Read character -/// - Read line -/// - Read async -/// - Read async until -/// - Read sync -/// - Wait for key event (terminal pause) -/// -/// Check `/examples/` in the library for more specific examples. -pub struct TerminalInput { - #[cfg(windows)] - input: WindowsInput, - #[cfg(unix)] - input: UnixInput, -} - -impl TerminalInput { - /// Create a new instance of `TerminalInput` whereon input related actions could be performed. - pub fn new() -> TerminalInput { - #[cfg(windows)] - let input = WindowsInput::new(); - - #[cfg(unix)] - let input = UnixInput::new(); - - TerminalInput { input } - } - - /// Read one line from the user input. - /// - /// # Remark - /// This function is not work when raw screen is turned on. - /// When you do want to read a line in raw mode please, checkout `read_async`, `read_async_until` or `read_sync`. - /// Not sure what 'raw mode' is, checkout the 'crossterm_screen' crate. - /// - /// # Example - /// ```ignore - /// let in = input(); - /// match in.read_line() { - /// Ok(s) => println!("string typed: {}", s), - /// Err(e) => println!("error: {}", e), - /// } - /// ``` - pub fn read_line(&self) -> Result { - let mut rv = String::new(); - io::stdin().read_line(&mut rv)?; - let len = rv.trim_end_matches(&['\r', '\n'][..]).len(); - rv.truncate(len); - Ok(rv) - } - - /// Read one character from the user input - /// - /// ```ignore - /// let in = input(); - /// match in.read_char() { - /// Ok(c) => println!("character pressed: {}", c), - /// Err(e) => println!("error: {}", e), - /// } - /// ``` - pub fn read_char(&self) -> Result { - self.input.read_char() - } - - /// Read the input asynchronously, which means that input events are gathered on the background and will be queued for you to read. - /// - /// If you want a blocking, or less resource consuming read to happen use `read_sync()`, this will leave a way all the thread and queueing and will be a blocking read. - /// - /// This is the same as `read_async()` but stops reading when a certain character is hit. - /// - /// # Remarks - /// - Readings won't be blocking calls. - /// A thread will be fired to read input, on unix systems from TTY and on windows WinApi - /// `ReadConsoleW` will be used. - /// - Input events read from the user will be queued on a MPSC-channel. - /// - The reading thread will be cleaned up when it drops. - /// - Requires 'raw screen to be enabled'. - /// Not sure what this is? Please checkout the 'crossterm_screen' crate. - /// - /// # Examples - /// Please checkout the example folder in the repository. - pub fn read_async(&self) -> AsyncReader { - self.input.read_async() - } - - /// Read the input asynchronously until a certain delimiter (character as byte) is hit, which means that input events are gathered on the background and will be queued for you to read. - /// - /// If you want a blocking or less resource consuming read to happen, use `read_sync()`. This will leave alone the background thread and queues and will be a blocking read. - /// - /// This is the same as `read_async()` but stops reading when a certain character is hit. - /// - /// # Remarks - /// - Readings won't be blocking calls. - /// A thread will be fired to read input, on unix systems from TTY and on windows WinApi - /// `ReadConsoleW` will be used. - /// - Input events read from the user will be queued on a MPSC-channel. - /// - The reading thread will be cleaned up when it drops. - /// - Requires 'raw screen to be enabled'. - /// Not sure what this is? Please checkout the 'crossterm_screen' crate. - /// - /// # Examples - /// Please checkout the example folder in the repository. - pub fn read_until_async(&self, delimiter: u8) -> AsyncReader { - self.input.read_until_async(delimiter) - } - - /// Read the input synchronously from the user, which means that reading calls will block. - /// It also uses less resources than the `AsyncReader` because the background thread and queues are left alone. - /// - /// Consider using `read_async` if you don't want the reading call to block your program. - /// - /// # Remark - /// - Readings will be blocking calls. - /// - /// # Examples - /// Please checkout the example folder in the repository. - pub fn read_sync(&self) -> SyncReader { - self.input.read_sync() - } - - /// Enable mouse events to be captured. - /// - /// When enabling mouse input, you will be able to capture mouse movements, pressed buttons, and locations. - /// - /// # Remark - /// - Mouse events will be send over the reader created with `read_async`, `read_async_until`, `read_sync`. - pub fn enable_mouse_mode(&self) -> Result<()> { - self.input.enable_mouse_mode() - } - - /// Disable mouse events to be captured. - /// - /// When disabling mouse input, you won't be able to capture mouse movements, pressed buttons, and locations anymore. - pub fn disable_mouse_mode(&self) -> Result<()> { - self.input.disable_mouse_mode() - } -} - -/// Get a `TerminalInput` instance whereon input related actions can be performed. -pub fn input() -> TerminalInput { - TerminalInput::new() -} diff --git a/src/input/unix_input.rs b/src/input/unix_input.rs index 7b54e89..3660874 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix_input.rs @@ -1,21 +1,20 @@ //! This is a UNIX specific implementation for input related action. -use std::char; use std::sync::{ + char, + io::{self, Read}, + str, thread, atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}, Arc, }; -use std::{ - io::{self, Read}, - str, thread, -}; use crossterm_utils::{csi, write_cout, ErrorKind, Result}; use crate::sys::unix::{get_tty, read_char_raw}; -use super::{ITerminalInput, InputEvent, KeyEvent, MouseButton, MouseEvent}; +use crate::input::ITerminalInput; +use crate::{InputEvent, KeyEvent, MouseButton, MouseEvent}; pub struct UnixInput; diff --git a/src/input/windows_input.rs b/src/input/windows_input.rs index 41a71d6..71ba0ab 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows_input.rs @@ -1,12 +1,17 @@ //! This is a WINDOWS specific implementation for input related action. -use std::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{self, Receiver, Sender}, - Arc, +use std:: +{ + char, + io, + thread, + time::Duration, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{self, Receiver, Sender}, + Arc, + } }; -use std::time::Duration; -use std::{char, io, thread}; use winapi::um::{ wincon::{ @@ -26,7 +31,8 @@ use crossterm_winapi::{ MouseEvent, }; -use super::{ITerminalInput, InputEvent, KeyEvent, MouseButton}; +use crate::input::ITerminalInput; +use crate::{InputEvent, KeyEvent, MouseButton}; pub struct WindowsInput; @@ -409,7 +415,7 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option { } } -fn parse_mouse_event_record(event: &MouseEvent) -> Option { +fn parse_mouse_event_record(event: &MouseEvent) -> Option { // NOTE (@imdaveho): xterm emulation takes the digits of the coords and passes them // individually as bytes into a buffer; the below cxbs and cybs replicates that and // mimicks the behavior; additionally, in xterm, mouse move is only handled when a @@ -424,10 +430,10 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Option { EventFlags::PressOrRelease => { // Single click match event.button_state { - ButtonState::Release => Some(super::MouseEvent::Release(xpos as u16, ypos as u16)), + ButtonState::Release => Some(crate::MouseEvent::Release(xpos as u16, ypos as u16)), ButtonState::FromLeft1stButtonPressed => { // left click - Some(super::MouseEvent::Press( + Some(crate::MouseEvent::Press( MouseButton::Left, xpos as u16, ypos as u16, @@ -435,7 +441,7 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Option { } ButtonState::RightmostButtonPressed => { // right click - Some(super::MouseEvent::Press( + Some(crate::MouseEvent::Press( MouseButton::Right, xpos as u16, ypos as u16, @@ -443,7 +449,7 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Option { } ButtonState::FromLeft2ndButtonPressed => { // middle click - Some(super::MouseEvent::Press( + Some(crate::MouseEvent::Press( MouseButton::Middle, xpos as u16, ypos as u16, @@ -456,7 +462,7 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Option { // Click + Move // NOTE (@imdaveho) only register when mouse is not released if event.button_state != ButtonState::Release { - Some(super::MouseEvent::Hold(xpos as u16, ypos as u16)) + Some(crate::MouseEvent::Hold(xpos as u16, ypos as u16)) } else { None } @@ -466,13 +472,13 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Option { // NOTE (@imdaveho) from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str // if `button_state` is negative then the wheel was rotated backward, toward the user. if event.button_state != ButtonState::Negative { - Some(super::MouseEvent::Press( + Some(crate::MouseEvent::Press( MouseButton::WheelUp, xpos as u16, ypos as u16, )) } else { - Some(super::MouseEvent::Press( + Some(crate::MouseEvent::Press( MouseButton::WheelDown, xpos as u16, ypos as u16, diff --git a/src/lib.rs b/src/lib.rs index 059c3c3..4b478d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,397 @@ +//! Crossterm provides a way to work with the terminal input. We will not cover the basic usage but instead asynchronous and synchronous reading of input. +//! Please check out these [examples](https://github.com/crossterm-rs/crossterm/blob/master/examples/input.rs) for reading a line or a character from the user. +//! +//! ## Differences Synchronous and Asynchronous +//! Crossterm provides two ways to read user input, synchronous and asynchronous. +//! +//! ### Synchronous reading +//! +//! Read the input synchronously from the user, the reads performed will be blocking calls. +//! Using synchronous over asynchronous reading has the benefit that it is using fewer resources than the asynchronous because background thread and queues are left away. +//! +//! You can get asynchronous event reader by calling: `TerminalInput::read_sync`. +//! +//! ### Asynchronous reading +//! +//! Read the input asynchronously, input events are gathered in the background and will be queued for you to read. +//! Using asynchronous reading has the benefit that input events are queued until you read them. You can poll for occurred events, and the reads won't block your program. +//! +//! You can get a synchronous event reader by calling: `TerminalInput::read_async`, `TerminalInput::read_async_until`. +//! +//! ### Technical details +//! On UNIX systems crossterm reads from the TTY, on Windows, it uses `ReadConsoleInputW`. +//! For asynchronous reading, a background thread will be fired up to read input events, +//! occurred events will be queued on an MPSC-channel, and the user can iterate over those events. +//! +//! The terminal has to be in raw mode, raw mode prevents the input of the user to be displayed on the terminal screen, see [screen](./screen.md) for more info. +//! +//! # Example +//! In the following example, we will create a small program that will listen for mouse and keyboard input. +//! On the press of the 'escape' key, the program will be stopped. +//! +//! So let's start by setting up the basics. +//! +//! ```no_run +//! use std::{thread, time::Duration}; +//! use crossterm_input::{input, InputEvent, KeyEvent}; +//! +//! fn main() { +//! println!("Press 'ESC' to quit."); +//! +//! /* next code here */ +//! } +//! ``` +//! +//! Next, we need to put the terminal into raw mode. We do this because we don't want the user input to be printed to the terminal screen. +//! +//! ```ignore +//! // enable raw mode +//! let screen = RawScreen::into_raw_mode(); +//! +//! // create a input from our screen +//! let input = input(); +//! +//! /* next code here */ +//! ``` +//! +//! Now that we constructed a `TerminalInput` instance we can go ahead an start the reading. +//! Do this by calling `input.read_async()`, which returns an [AsyncReader](https://docs.rs/crossterm/0.8.0/crossterm/struct.AsyncReader.html). +//! This is an iterator over the input events that you could as any other iterator. +//! +//! ```ignore +//! let mut async_stdin = input.read_async(); +//! +//! loop { +//! if let Some(key_event) = async_stdin.next() { +//! /* next code here */ +//! } +//! thread::sleep(Duration::from_millis(50)); +//! } +//! ``` +//! +//! The [AsyncReader](https://docs.rs/crossterm/0.8.0/crossterm/struct.AsyncReader.html) iterator will return `None` when nothing is there to read, `Some(InputEvent)` if there are events to read. +//! I use a thread delay to prevent spamming the iterator. +//! +//! Next up we can start pattern matching to see if there are input events we'd like to catch. +//! In our case, we want to catch the `Escape Key`. +//! +//! ```ignore +//! match key_event { +//! InputEvent::Keyboard(event) => match event { +//! KeyEvent::Esc => { +//! println!("Program closing ..."); +//! break; +//! } +//! _ => println!("Key {:?} was pressed!", event) +//! } +//! InputEvent::Mouse(event) => { /* Mouse Event */ } +//! _ => { } +//! } +//! ``` +//! +//! As you see, we check if the `KeyEvent::Esc` was pressed, if that's true we stop the program by breaking out of the loop. +//! +//! _final code_ +//! ```no_run +//! use std::{thread, time::Duration}; +//! use crossterm_input::{input, InputEvent, KeyEvent, RawScreen}; +//! +//! fn main() { +//! println!("Press 'ESC' to quit."); +//! +//! // enable raw mode +//! let screen = RawScreen::into_raw_mode(); +//! +//! // create a input from our screen. +//! let input = input(); +//! +//! // create async reader +//! let mut async_stdin = input.read_async(); +//! +//! loop { +//! // try to get the next input event. +//! if let Some(key_event) = async_stdin.next() { +//! match key_event { +//! InputEvent::Keyboard(event) => match event { +//! KeyEvent::Esc => { +//! println!("Program closing ..."); +//! break; +//! } +//! _ => println!("Key {:?} was pressed!", event) +//! } +//! InputEvent::Mouse(event) => { /* Mouse Event */ } +//! _ => { } +//! } +//! } +//! thread::sleep(Duration::from_millis(50)); +//! } +//! } // <=== background reader will be disposed when dropped.s +//! ``` +//! --------------------------------------------------------------------------------------------------------------------------------------------- +//! More robust and complete examples on all input aspects like mouse, keys could be found [here](https://github.com/crossterm-rs/crossterm/tree/master/examples/). + #![deny(unused_imports)] -pub use crossterm_screen::{IntoRawMode, RawScreen}; +use std::io; -pub use self::input::{ - input, AsyncReader, InputEvent, KeyEvent, MouseButton, MouseEvent, SyncReader, TerminalInput, -}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +pub use crossterm_screen::{IntoRawMode, RawScreen}; +pub use crossterm_utils::Result; +pub use self::input::{AsyncReader, SyncReader}; +use self::input::ITerminalInput; +#[cfg(unix)] +pub use self::input::UnixInput; +#[cfg(windows)] +pub use self::input::WindowsInput; mod input; mod sys; + +/// Enum to specify which input event has occurred. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Hash, Clone)] +pub enum InputEvent { + /// A single key or a combination is pressed. + Keyboard(KeyEvent), + /// A mouse event occurred. + Mouse(MouseEvent), + /// A unsupported event has occurred. + Unsupported(Vec), + /// An unknown event has occurred. + Unknown, +} + +/// Enum to specify which mouse event has occurred. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Copy)] +pub enum MouseEvent { + /// A mouse press has occurred, this contains the pressed button and the position of the press. + Press(MouseButton, u16, u16), + /// A mouse button was released. + Release(u16, u16), + /// A mouse button was hold. + Hold(u16, u16), + /// An unknown mouse event has occurred. + Unknown, +} + +/// Enum to define mouse buttons. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Copy)] +pub enum MouseButton { + /// Left mouse button + Left, + /// Right mouse button + Right, + /// Middle mouse button + Middle, + /// Scroll up + WheelUp, + /// Scroll down + WheelDown, +} + +/// Enum with different key or key combinations. +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum KeyEvent { + Backspace, + Enter, + Left, + Right, + Up, + Down, + Home, + End, + PageUp, + PageDown, + Tab, + BackTab, + Delete, + Insert, + F(u8), + Char(char), + Alt(char), + Ctrl(char), + Null, + Esc, + CtrlUp, + CtrlDown, + CtrlRight, + CtrlLeft, + ShiftUp, + ShiftDown, + ShiftRight, + ShiftLeft, +} + +/// Allows you to read user input. +/// +/// Check [examples](https://github.com/crossterm-rs/examples) in the library for more specific examples. +/// +/// ## Examples +/// +/// Basic usage: +/// +/// ```no_run +/// // You can replace the following line with `use crossterm::TerminalColor;` +/// // if you're using the `crossterm` crate with the `style` feature enabled. +/// use crossterm_input::{Result, TerminalInput, RawScreen}; +/// +/// fn main() -> Result<()> { +/// let color = TerminalInput::new(); +/// // read a single char +/// let char = color.read_char()?; +/// // read a single line +/// let line = color.read_line()?; +/// +/// // make sure to enable raw screen when reading input events. +/// let screen = RawScreen::into_raw_mode(); +/// +/// // create async reader +/// let mut async_stdin = color.read_async(); +/// +/// // create async reader +/// let mut sync_stdin = color.read_sync(); +/// +/// // enable mouse input events +/// color.enable_mouse_mode()?; +/// // disable mouse input events +/// color.disable_mouse_mode() +/// } +/// ``` +pub struct TerminalInput { + #[cfg(windows)] + input: WindowsInput, + #[cfg(unix)] + input: UnixInput, +} + +impl TerminalInput { + /// Create a new instance of `TerminalInput` whereon input related actions could be performed. + pub fn new() -> TerminalInput { + #[cfg(windows)] + let input = WindowsInput::new(); + + #[cfg(unix)] + let input = UnixInput::new(); + + TerminalInput { input } + } + + /// Read one line from the user input. + /// + /// # Remark + /// This function is not work when raw screen is turned on. + /// When you do want to read a line in raw mode please, checkout `read_async`, `read_async_until` or `read_sync`. + /// Not sure what 'raw mode' is, checkout the 'crossterm_screen' crate. + /// + /// # Example + /// ```no_run + /// let input = crossterm_input::input(); + /// match input.read_line() { + /// Ok(s) => println!("string typed: {}", s), + /// Err(e) => println!("error: {}", e), + /// } + /// ``` + pub fn read_line(&self) -> Result { + let mut rv = String::new(); + io::stdin().read_line(&mut rv)?; + let len = rv.trim_end_matches(&['\r', '\n'][..]).len(); + rv.truncate(len); + Ok(rv) + } + + /// Read one character from the user input + /// + /// ```no_run + /// let input = crossterm_input::input(); + /// match input.read_char() { + /// Ok(c) => println!("character pressed: {}", c), + /// Err(e) => println!("error: {}", e), + /// } + /// ``` + pub fn read_char(&self) -> Result { + self.input.read_char() + } + + /// Read the input asynchronously, which means that input events are gathered on the background and will be queued for you to read. + /// + /// If you want a blocking, or less resource consuming read to happen use `read_sync()`, this will leave a way all the thread and queueing and will be a blocking read. + /// + /// This is the same as `read_async()` but stops reading when a certain character is hit. + /// + /// # Remarks + /// - Readings won't be blocking calls. + /// A thread will be fired to read input, on unix systems from TTY and on windows WinApi + /// `ReadConsoleW` will be used. + /// - Input events read from the user will be queued on a MPSC-channel. + /// - The reading thread will be cleaned up when it drops. + /// - Requires 'raw screen to be enabled'. + /// Not sure what this is? Please checkout the 'crossterm_screen' crate. + /// + /// # Examples + /// Please checkout the example folder in the repository. + pub fn read_async(&self) -> AsyncReader { + self.input.read_async() + } + + /// Read the input asynchronously until a certain delimiter (character as byte) is hit, which means that input events are gathered on the background and will be queued for you to read. + /// + /// If you want a blocking or less resource consuming read to happen, use `read_sync()`. This will leave alone the background thread and queues and will be a blocking read. + /// + /// This is the same as `read_async()` but stops reading when a certain character is hit. + /// + /// # Remarks + /// - Readings won't be blocking calls. + /// A thread will be fired to read input, on unix systems from TTY and on windows WinApi + /// `ReadConsoleW` will be used. + /// - Input events read from the user will be queued on a MPSC-channel. + /// - The reading thread will be cleaned up when it drops. + /// - Requires 'raw screen to be enabled'. + /// Not sure what this is? Please checkout the 'crossterm_screen' crate. + /// + /// # Examples + /// Please checkout the example folder in the repository. + pub fn read_until_async(&self, delimiter: u8) -> AsyncReader { + self.input.read_until_async(delimiter) + } + + /// Read the input synchronously from the user, which means that reading calls will block. + /// It also uses less resources than the `AsyncReader` because the background thread and queues are left alone. + /// + /// Consider using `read_async` if you don't want the reading call to block your program. + /// + /// # Remark + /// - Readings will be blocking calls. + /// + /// # Examples + /// Please checkout the example folder in the repository. + pub fn read_sync(&self) -> SyncReader { + self.input.read_sync() + } + + /// Enable mouse events to be captured. + /// + /// When enabling mouse input, you will be able to capture mouse movements, pressed buttons, and locations. + /// + /// # Remark + /// - Mouse events will be send over the reader created with `read_async`, `read_async_until`, `read_sync`. + pub fn enable_mouse_mode(&self) -> Result<()> { + self.input.enable_mouse_mode() + } + + /// Disable mouse events to be captured. + /// + /// When disabling mouse input, you won't be able to capture mouse movements, pressed buttons, and locations anymore. + pub fn disable_mouse_mode(&self) -> Result<()> { + self.input.disable_mouse_mode() + } +} + +/// Get a `TerminalInput` instance whereon input related actions can be performed. +pub fn input() -> TerminalInput { + TerminalInput::new() +} From dea5f33b3e078bf768ac26e664c69d6526c6a65e Mon Sep 17 00:00:00 2001 From: Timon Post Date: Thu, 26 Sep 2019 22:46:32 +0200 Subject: [PATCH 2/9] fmt --- src/input/unix_input.rs | 7 +++---- src/input/windows_input.rs | 14 ++++++-------- src/lib.rs | 8 ++++---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/input/unix_input.rs b/src/input/unix_input.rs index 3660874..cc7940f 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix_input.rs @@ -1,12 +1,11 @@ //! This is a UNIX specific implementation for input related action. use std::sync::{ + atomic::{AtomicBool, Ordering}, char, io::{self, Read}, - str, thread, - atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}, - Arc, + str, thread, Arc, }; use crossterm_utils::{csi, write_cout, ErrorKind, Result}; @@ -16,7 +15,7 @@ use crate::sys::unix::{get_tty, read_char_raw}; use crate::input::ITerminalInput; use crate::{InputEvent, KeyEvent, MouseButton, MouseEvent}; -pub struct UnixInput; +pub(crate) struct UnixInput; impl UnixInput { pub fn new() -> UnixInput { diff --git a/src/input/windows_input.rs b/src/input/windows_input.rs index 71ba0ab..aecdf57 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows_input.rs @@ -1,16 +1,14 @@ //! This is a WINDOWS specific implementation for input related action. -use std:: -{ - char, - io, - thread, - time::Duration, +use std::{ + char, io, sync::{ atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}, Arc, - } + }, + thread, + time::Duration, }; use winapi::um::{ @@ -34,7 +32,7 @@ use crossterm_winapi::{ use crate::input::ITerminalInput; use crate::{InputEvent, KeyEvent, MouseButton}; -pub struct WindowsInput; +pub(crate) struct WindowsInput; impl WindowsInput { pub fn new() -> WindowsInput { diff --git a/src/lib.rs b/src/lib.rs index 4b478d9..9813922 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,17 +134,17 @@ use std::io; +pub use crossterm_screen::{IntoRawMode, RawScreen}; +pub use crossterm_utils::Result; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -pub use crossterm_screen::{IntoRawMode, RawScreen}; -pub use crossterm_utils::Result; -pub use self::input::{AsyncReader, SyncReader}; use self::input::ITerminalInput; #[cfg(unix)] pub use self::input::UnixInput; #[cfg(windows)] pub use self::input::WindowsInput; +pub use self::input::{AsyncReader, SyncReader}; mod input; mod sys; @@ -241,7 +241,7 @@ pub enum KeyEvent { /// use crossterm_input::{Result, TerminalInput, RawScreen}; /// /// fn main() -> Result<()> { -/// let color = TerminalInput::new(); +/// let color = TerminalInput::new(); /// // read a single char /// let char = color.read_char()?; /// // read a single line From 0361978c966e4b1d86c0b247d1194fdaa4d0f679 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 13:28:24 +0200 Subject: [PATCH 3/9] fixed compile errors and added comment examples --- src/input.rs | 8 +++---- src/lib.rs | 65 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/input.rs b/src/input.rs index d1f515d..fe2f86f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -4,14 +4,14 @@ use crossterm_utils::Result; #[cfg(unix)] -pub use self::unix_input::{AsyncReader, SyncReader, UnixInput}; +pub use self::unix_input::{AsyncReader, SyncReader}; #[cfg(windows)] -pub use self::windows_input::{AsyncReader, SyncReader, WindowsInput}; +pub use self::windows_input::{AsyncReader, SyncReader}; #[cfg(unix)] -mod unix_input; +pub(crate) mod unix_input; #[cfg(windows)] -mod windows_input; +pub(crate) mod windows_input; /// This trait defines the actions that can be performed with the terminal input. /// This trait can be implemented so that a concrete implementation of the ITerminalInput can fulfill diff --git a/src/lib.rs b/src/lib.rs index 9813922..116ed0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,12 +139,12 @@ pub use crossterm_utils::Result; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +pub use self::input::{AsyncReader, SyncReader}; use self::input::ITerminalInput; #[cfg(unix)] -pub use self::input::UnixInput; +use self::input::unix_input::UnixInput; #[cfg(windows)] -pub use self::input::WindowsInput; -pub use self::input::{AsyncReader, SyncReader}; +use self::input::windows_input::WindowsInput; mod input; mod sys; @@ -236,30 +236,30 @@ pub enum KeyEvent { /// Basic usage: /// /// ```no_run -/// // You can replace the following line with `use crossterm::TerminalColor;` +/// // You can replace the following line with `use crossterm::TerminalInput;` /// // if you're using the `crossterm` crate with the `style` feature enabled. /// use crossterm_input::{Result, TerminalInput, RawScreen}; /// /// fn main() -> Result<()> { -/// let color = TerminalInput::new(); +/// let input = TerminalInput::new(); /// // read a single char -/// let char = color.read_char()?; +/// let char = input.read_char()?; /// // read a single line -/// let line = color.read_line()?; +/// let line = input.read_line()?; /// /// // make sure to enable raw screen when reading input events. /// let screen = RawScreen::into_raw_mode(); /// /// // create async reader -/// let mut async_stdin = color.read_async(); +/// let mut async_stdin = input.read_async(); /// /// // create async reader -/// let mut sync_stdin = color.read_sync(); +/// let mut sync_stdin = input.read_sync(); /// /// // enable mouse input events -/// color.enable_mouse_mode()?; +/// input.enable_mouse_mode()?; /// // disable mouse input events -/// color.disable_mouse_mode() +/// input.disable_mouse_mode() /// } /// ``` pub struct TerminalInput { @@ -333,7 +333,20 @@ impl TerminalInput { /// Not sure what this is? Please checkout the 'crossterm_screen' crate. /// /// # Examples - /// Please checkout the example folder in the repository. + /// ```no_run + /// use std::{thread, time::Duration}; + /// use crossterm_input::input; + /// + /// let mut async_stdin = input().read_async(); + /// + /// loop { + /// if let Some(key_event) = async_stdin.next() { + /// /* check which event occurred here*/ + /// } + /// + /// thread::sleep(Duration::from_millis(50)); + /// } + /// ``` pub fn read_async(&self) -> AsyncReader { self.input.read_async() } @@ -354,7 +367,20 @@ impl TerminalInput { /// Not sure what this is? Please checkout the 'crossterm_screen' crate. /// /// # Examples - /// Please checkout the example folder in the repository. + /// ```no_run + /// use std::{thread, time::Duration}; + /// use crossterm_input::input; + /// + /// let mut async_stdin = input().read_until_async(b'x'); + /// + /// loop { + /// if let Some(key_event) = async_stdin.next() { + /// /* check which event occurred here */ + /// } + /// + /// thread::sleep(Duration::from_millis(50)); + /// } + /// ``` pub fn read_until_async(&self, delimiter: u8) -> AsyncReader { self.input.read_until_async(delimiter) } @@ -368,7 +394,18 @@ impl TerminalInput { /// - Readings will be blocking calls. /// /// # Examples - /// Please checkout the example folder in the repository. + /// ```no_run + /// use std::{thread, time::Duration}; + /// use crossterm_input::input; + /// + /// let mut sync_stdin = input().read_sync(); + /// + /// loop { + /// if let Some(key_event) = sync_stdin.next() { + /// /* check which event occurred here*/ + /// } + /// } + /// ``` pub fn read_sync(&self) -> SyncReader { self.input.read_sync() } From f5bb071b1fa8306a42acbce1b9d41bb2765bf531 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 13:31:50 +0200 Subject: [PATCH 4/9] ITerminalInput -> Input --- src/input.rs | 2 +- src/input/unix_input.rs | 4 ++-- src/input/windows_input.rs | 4 ++-- src/lib.rs | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/input.rs b/src/input.rs index fe2f86f..0eaeeda 100644 --- a/src/input.rs +++ b/src/input.rs @@ -21,7 +21,7 @@ pub(crate) mod windows_input; /// /// This trait is implemented for Windows and UNIX systems. /// Unix is using the 'TTY' and windows is using 'libc' C functions to read the input. -pub(crate) trait ITerminalInput { +pub(crate) trait Input { /// Read one character from the user input fn read_char(&self) -> Result; /// Read the input asynchronously from the user. diff --git a/src/input/unix_input.rs b/src/input/unix_input.rs index cc7940f..596912a 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix_input.rs @@ -12,7 +12,7 @@ use crossterm_utils::{csi, write_cout, ErrorKind, Result}; use crate::sys::unix::{get_tty, read_char_raw}; -use crate::input::ITerminalInput; +use crate::input::Input; use crate::{InputEvent, KeyEvent, MouseButton, MouseEvent}; pub(crate) struct UnixInput; @@ -23,7 +23,7 @@ impl UnixInput { } } -impl ITerminalInput for UnixInput { +impl Input for UnixInput { fn read_char(&self) -> Result { read_char_raw() } diff --git a/src/input/windows_input.rs b/src/input/windows_input.rs index aecdf57..75d17e6 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows_input.rs @@ -29,7 +29,7 @@ use crossterm_winapi::{ MouseEvent, }; -use crate::input::ITerminalInput; +use crate::input::Input; use crate::{InputEvent, KeyEvent, MouseButton}; pub(crate) struct WindowsInput; @@ -45,7 +45,7 @@ const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; // NOTE (@imdaveho): this global var is terrible -> move it elsewhere... static mut ORIG_MODE: u32 = 0; -impl ITerminalInput for WindowsInput { +impl Input for WindowsInput { fn read_char(&self) -> Result { // _getwch is without echo and _getwche is with echo let pressed_char = unsafe { _getwche() }; diff --git a/src/lib.rs b/src/lib.rs index 116ed0d..f2b5c25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,7 +130,7 @@ //! --------------------------------------------------------------------------------------------------------------------------------------------- //! More robust and complete examples on all input aspects like mouse, keys could be found [here](https://github.com/crossterm-rs/crossterm/tree/master/examples/). -#![deny(unused_imports)] +#![deny(unused_imports, unused_must_use)] use std::io; @@ -139,12 +139,12 @@ pub use crossterm_utils::Result; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -pub use self::input::{AsyncReader, SyncReader}; -use self::input::ITerminalInput; #[cfg(unix)] use self::input::unix_input::UnixInput; #[cfg(windows)] use self::input::windows_input::WindowsInput; +use self::input::Input; +pub use self::input::{AsyncReader, SyncReader}; mod input; mod sys; From dd66082f5edb2923c6bda4077bd9fe2627d971d3 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 13:40:05 +0200 Subject: [PATCH 5/9] import merge --- src/input/unix_input.rs | 3 +-- src/input/windows_input.rs | 3 +-- src/lib.rs | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/input/unix_input.rs b/src/input/unix_input.rs index 596912a..9ecda2f 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix_input.rs @@ -12,8 +12,7 @@ use crossterm_utils::{csi, write_cout, ErrorKind, Result}; use crate::sys::unix::{get_tty, read_char_raw}; -use crate::input::Input; -use crate::{InputEvent, KeyEvent, MouseButton, MouseEvent}; +use crate::{input::Input, InputEvent, KeyEvent, MouseButton, MouseEvent}; pub(crate) struct UnixInput; diff --git a/src/input/windows_input.rs b/src/input/windows_input.rs index 75d17e6..dc7ca0c 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows_input.rs @@ -29,8 +29,7 @@ use crossterm_winapi::{ MouseEvent, }; -use crate::input::Input; -use crate::{InputEvent, KeyEvent, MouseButton}; +use crate::{input::Input, InputEvent, KeyEvent, MouseButton}; pub(crate) struct WindowsInput; diff --git a/src/lib.rs b/src/lib.rs index f2b5c25..86a11e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,8 +233,6 @@ pub enum KeyEvent { /// /// ## Examples /// -/// Basic usage: -/// /// ```no_run /// // You can replace the following line with `use crossterm::TerminalInput;` /// // if you're using the `crossterm` crate with the `style` feature enabled. From bb5eb41d139d79b1eae5738a409d412ff5d6ce84 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Fri, 27 Sep 2019 13:42:07 +0200 Subject: [PATCH 6/9] fixed wrong import unix --- src/input/unix_input.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/input/unix_input.rs b/src/input/unix_input.rs index 9ecda2f..ee93ffc 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix_input.rs @@ -1,11 +1,15 @@ //! This is a UNIX specific implementation for input related action. -use std::sync::{ - atomic::{AtomicBool, Ordering}, +use std::{ char, io::{self, Read}, - mpsc::{self, Receiver, Sender}, - str, thread, Arc, + str, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{self, Receiver, Sender}, + Arc, + }, + thread, }; use crossterm_utils::{csi, write_cout, ErrorKind, Result}; From 7e64efd8cfbd4f788cdf2374e388aa09afb2a65f Mon Sep 17 00:00:00 2001 From: Timon Post Date: Sun, 29 Sep 2019 14:53:41 +0200 Subject: [PATCH 7/9] tab is send when shift is pressed --- src/input/windows_input.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/input/windows_input.rs b/src/input/windows_input.rs index dc7ca0c..2c20f6e 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows_input.rs @@ -394,16 +394,16 @@ fn parse_key_event_record(key_event: &KeyEventRecord) -> Option { } _ => None, } - } else if key_state.has_state(SHIFT_PRESSED) { - // Shift + key press, essentially the same as single key press - // Separating to be explicit about the Shift press. + } else if key_state.has_state(SHIFT_PRESSED) && character == '\t' { + Some(KeyEvent::BackTab) + } else { if character == '\t' { - Some(KeyEvent::BackTab) - } else { Some(KeyEvent::Tab) + } else { + // Shift + key press, essentially the same as single key press + // Separating to be explicit about the Shift press. + Some(KeyEvent::Char(character)) } - } else { - Some(KeyEvent::Char(character)) } } else { None From 1eda167c1885f27a079968124d36c07566516fe8 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Mon, 30 Sep 2019 17:29:31 +0200 Subject: [PATCH 8/9] events --- Cargo.toml | 1 + src/input.rs | 10 +++--- src/input/{unix_input.rs => unix.rs} | 1 - src/input/{windows_input.rs => windows.rs} | 37 ++++++++++++++++------ src/lib.rs | 4 +-- src/sys/unix.rs | 32 ++++++++++--------- 6 files changed, 54 insertions(+), 31 deletions(-) rename src/input/{unix_input.rs => unix.rs} (99%) rename src/input/{windows_input.rs => windows.rs} (94%) diff --git a/Cargo.toml b/Cargo.toml index 664b8d9..4421b5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,5 @@ libc = "0.2.51" [dependencies] crossterm_utils = { version = "0.3.1" } crossterm_screen = { version = "0.3.1" } +lazy_static = "1.4" serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/src/input.rs b/src/input.rs index 0eaeeda..7548491 100644 --- a/src/input.rs +++ b/src/input.rs @@ -4,14 +4,14 @@ use crossterm_utils::Result; #[cfg(unix)] -pub use self::unix_input::{AsyncReader, SyncReader}; +pub use self::unix::{AsyncReader, SyncReader}; #[cfg(windows)] -pub use self::windows_input::{AsyncReader, SyncReader}; +pub use self::windows::{AsyncReader, SyncReader}; #[cfg(unix)] -pub(crate) mod unix_input; +pub(crate) mod unix; #[cfg(windows)] -pub(crate) mod windows_input; +pub(crate) mod windows; /// This trait defines the actions that can be performed with the terminal input. /// This trait can be implemented so that a concrete implementation of the ITerminalInput can fulfill @@ -30,6 +30,8 @@ pub(crate) trait Input { fn read_until_async(&self, delimiter: u8) -> AsyncReader; /// Read the input synchronously from the user. fn read_sync(&self) -> SyncReader; + /// Start monitoring mouse events. fn enable_mouse_mode(&self) -> Result<()>; + /// Stop monitoring mouse events. fn disable_mouse_mode(&self) -> Result<()>; } diff --git a/src/input/unix_input.rs b/src/input/unix.rs similarity index 99% rename from src/input/unix_input.rs rename to src/input/unix.rs index ee93ffc..6a4f199 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix.rs @@ -15,7 +15,6 @@ use std::{ use crossterm_utils::{csi, write_cout, ErrorKind, Result}; use crate::sys::unix::{get_tty, read_char_raw}; - use crate::{input::Input, InputEvent, KeyEvent, MouseButton, MouseEvent}; pub(crate) struct UnixInput; diff --git a/src/input/windows_input.rs b/src/input/windows.rs similarity index 94% rename from src/input/windows_input.rs rename to src/input/windows.rs index 2c20f6e..212239a 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows.rs @@ -5,12 +5,13 @@ use std::{ sync::{ atomic::{AtomicBool, Ordering}, mpsc::{self, Receiver, Sender}, - Arc, + Arc, Mutex, }, thread, time::Duration, }; +use crossterm_utils::Result; use winapi::um::{ wincon::{ LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, SHIFT_PRESSED, @@ -23,11 +24,11 @@ use winapi::um::{ }, }; -use crossterm_utils::Result; use crossterm_winapi::{ ButtonState, Console, ConsoleMode, EventFlags, Handle, InputEventType, KeyEventRecord, MouseEvent, }; +use lazy_static::lazy_static; use crate::{input::Input, InputEvent, KeyEvent, MouseButton}; @@ -41,8 +42,27 @@ impl WindowsInput { const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; -// NOTE (@imdaveho): this global var is terrible -> move it elsewhere... -static mut ORIG_MODE: u32 = 0; +lazy_static! { + static ref ORIGINAL_CONSOLE_MODE: Mutex> = Mutex::new(None); +} + +/// Initializes the default console color. It will will be skipped if it has already been initialized. +fn init_original_console_mode(original_mode: u32) { + let mut lock = ORIGINAL_CONSOLE_MODE.lock().unwrap(); + + if lock.is_none() { + *lock = Some(original_mode); + } +} + +/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. +fn original_console_mode() -> u32 { + // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` + ORIGINAL_CONSOLE_MODE + .lock() + .unwrap() + .expect("Original console mode not set") +} impl Input for WindowsInput { fn read_char(&self) -> Result { @@ -109,16 +129,15 @@ impl Input for WindowsInput { fn enable_mouse_mode(&self) -> Result<()> { let mode = ConsoleMode::from(Handle::current_in_handle()?); - unsafe { - ORIG_MODE = mode.mode()?; - mode.set_mode(ENABLE_MOUSE_MODE)?; - } + init_original_console_mode(mode.mode()?); + mode.set_mode(ENABLE_MOUSE_MODE)?; + Ok(()) } fn disable_mouse_mode(&self) -> Result<()> { let mode = ConsoleMode::from(Handle::current_in_handle()?); - mode.set_mode(unsafe { ORIG_MODE })?; + mode.set_mode(original_console_mode())?; Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 86a11e6..3d8a3cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,9 +140,9 @@ pub use crossterm_utils::Result; use serde::{Deserialize, Serialize}; #[cfg(unix)] -use self::input::unix_input::UnixInput; +use self::input::unix::UnixInput; #[cfg(windows)] -use self::input::windows_input::WindowsInput; +use self::input::windows::WindowsInput; use self::input::Input; pub use self::input::{AsyncReader, SyncReader}; diff --git a/src/sys/unix.rs b/src/sys/unix.rs index bae349b..c006923 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -3,10 +3,10 @@ use std::{fs, io}; use crossterm_utils::Result; -/// Get the TTY device. +/// Reruns the TTY device. /// /// This allows for getting stdio representing _only_ the TTY, and not other streams. -pub fn get_tty() -> Result { +pub(crate) fn get_tty() -> Result { let file = fs::OpenOptions::new() .read(true) .write(true) @@ -15,19 +15,8 @@ pub fn get_tty() -> Result { Ok(file) } -fn get_tty_fd() -> Result { - let fd = unsafe { - if libc::isatty(libc::STDIN_FILENO) == 1 { - libc::STDIN_FILENO - } else { - let tty_f = fs::File::open("/dev/tty")?; - tty_f.as_raw_fd() - } - }; - Ok(fd) -} - -pub fn read_char_raw() -> Result { +/// Read character with raw mode already enabled. +pub(crate) fn read_char_raw() -> Result { let mut buf = [0u8; 20]; let fd = get_tty_fd()?; @@ -58,3 +47,16 @@ pub fn read_char_raw() -> Result { Ok(rv) } + +/// Reruns the TTY device. +fn get_tty_fd() -> Result { + let fd = unsafe { + if libc::isatty(libc::STDIN_FILENO) == 1 { + libc::STDIN_FILENO + } else { + let tty_f = fs::File::open("/dev/tty")?; + tty_f.as_raw_fd() + } + }; + Ok(fd) +} From b4e667fac11733b5914620207fb42e7df44bad90 Mon Sep 17 00:00:00 2001 From: Timon Post Date: Tue, 1 Oct 2019 09:26:33 +0200 Subject: [PATCH 9/9] move statement --- src/input/windows.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/input/windows.rs b/src/input/windows.rs index 212239a..8e2364d 100644 --- a/src/input/windows.rs +++ b/src/input/windows.rs @@ -32,14 +32,6 @@ use lazy_static::lazy_static; use crate::{input::Input, InputEvent, KeyEvent, MouseButton}; -pub(crate) struct WindowsInput; - -impl WindowsInput { - pub fn new() -> WindowsInput { - WindowsInput - } -} - const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; lazy_static! { @@ -64,6 +56,14 @@ fn original_console_mode() -> u32 { .expect("Original console mode not set") } +pub(crate) struct WindowsInput; + +impl WindowsInput { + pub fn new() -> WindowsInput { + WindowsInput + } +} + impl Input for WindowsInput { fn read_char(&self) -> Result { // _getwch is without echo and _getwche is with echo