200 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
//! A module that contains all the actions related to reading input from the terminal.
 | 
						|
//! Like reading a line, reading a character and reading asynchronously.
 | 
						|
 | 
						|
mod input;
 | 
						|
 | 
						|
#[cfg(unix)]
 | 
						|
mod unix_input;
 | 
						|
#[cfg(windows)]
 | 
						|
mod windows_input;
 | 
						|
 | 
						|
#[cfg(unix)]
 | 
						|
pub use self::unix_input::SyncReader;
 | 
						|
#[cfg(unix)]
 | 
						|
use self::unix_input::UnixInput;
 | 
						|
 | 
						|
#[cfg(windows)]
 | 
						|
pub use self::windows_input::SyncReader;
 | 
						|
#[cfg(windows)]
 | 
						|
use self::windows_input::WindowsInput;
 | 
						|
 | 
						|
use self::input::parse_event;
 | 
						|
pub use self::input::{input, TerminalInput};
 | 
						|
use crossterm_utils::{ErrorKind, Result};
 | 
						|
use std::io;
 | 
						|
use std::sync::{mpsc, Arc};
 | 
						|
 | 
						|
use std::sync::atomic::{AtomicBool, Ordering};
 | 
						|
use std::sync::mpsc::{Receiver, Sender};
 | 
						|
use std::thread;
 | 
						|
 | 
						|
/// This trait defines the actions that can be preformed with the terminal input.
 | 
						|
/// This trait can be implemented so that a concrete implementation of the ITerminalInput can fulfill
 | 
						|
/// the wishes to work on a specific platform.
 | 
						|
///
 | 
						|
/// ## For example:
 | 
						|
///
 | 
						|
/// 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 {
 | 
						|
    /// Read one character from the user input
 | 
						|
    fn read_char(&self) -> io::Result<char>;
 | 
						|
    /// Read the input asynchronously from the user.
 | 
						|
    fn read_async(&self) -> AsyncReader;
 | 
						|
    ///  Read the input asynchronously until a certain character is hit.
 | 
						|
    fn read_until_async(&self, delimiter: u8) -> AsyncReader;
 | 
						|
    /// Read the input synchronously from the user.
 | 
						|
    fn read_sync(&self) -> SyncReader;
 | 
						|
    fn enable_mouse_mode(&self) -> Result<()>;
 | 
						|
    fn disable_mouse_mode(&self) -> Result<()>;
 | 
						|
}
 | 
						|
 | 
						|
/// Enum to specify which input event has occurred.
 | 
						|
#[derive(Debug, PartialOrd, PartialEq)]
 | 
						|
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<u8>),
 | 
						|
    /// An unknown event has occurred.
 | 
						|
    Unknown,
 | 
						|
}
 | 
						|
 | 
						|
/// Enum to specify which mouse event has occurred.
 | 
						|
#[derive(Debug, PartialOrd, PartialEq)]
 | 
						|
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.
 | 
						|
#[derive(Debug, PartialOrd, PartialEq)]
 | 
						|
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)]
 | 
						|
pub enum KeyEvent {
 | 
						|
    Backspace,
 | 
						|
    Left,
 | 
						|
    Right,
 | 
						|
    Up,
 | 
						|
    Down,
 | 
						|
    Home,
 | 
						|
    End,
 | 
						|
    PageUp,
 | 
						|
    PageDown,
 | 
						|
    BackTab,
 | 
						|
    Delete,
 | 
						|
    Insert,
 | 
						|
    F(u8),
 | 
						|
    Char(char),
 | 
						|
    Alt(char),
 | 
						|
    Ctrl(char),
 | 
						|
    Null,
 | 
						|
    Esc,
 | 
						|
    CtrlUp,
 | 
						|
    CtrlDown,
 | 
						|
    CtrlRight,
 | 
						|
    CtrlLeft,
 | 
						|
    ShiftUp,
 | 
						|
    ShiftDown,
 | 
						|
    ShiftRight,
 | 
						|
    ShiftLeft,
 | 
						|
}
 | 
						|
 | 
						|
/// This type allows you to read the input asynchronously which means that input events are gathered on the background and will be queued for you to read.
 | 
						|
///
 | 
						|
/// **[SyncReader](./LINK)**
 | 
						|
/// If you want a blocking, or less resource consuming read to happen use `SyncReader`, this will leave a way all the thread and queueing and will be a blocking read.
 | 
						|
///
 | 
						|
/// This type is an iterator, and could be used to iterate over input events.
 | 
						|
///
 | 
						|
/// # Remarks
 | 
						|
/// - Threads spawned will be disposed of as soon the `AsyncReader` goes out of scope.
 | 
						|
/// - MPSC-channels are used to queue input events, this type implements an iterator of the rx side of the queue.
 | 
						|
pub struct AsyncReader {
 | 
						|
    event_rx: Receiver<u8>,
 | 
						|
    shutdown: Arc<AtomicBool>,
 | 
						|
}
 | 
						|
 | 
						|
impl AsyncReader {
 | 
						|
    /// Construct a new instance of the `AsyncReader`.
 | 
						|
    /// The reading will immediately start when calling this function.
 | 
						|
    pub fn new(function: Box<Fn(&Sender<u8>, &Arc<AtomicBool>) + Send>) -> AsyncReader {
 | 
						|
        let shutdown_handle = Arc::new(AtomicBool::new(false));
 | 
						|
 | 
						|
        let (event_tx, event_rx) = mpsc::channel();
 | 
						|
        let thread_shutdown = shutdown_handle.clone();
 | 
						|
 | 
						|
        thread::spawn(move || loop {
 | 
						|
            function(&event_tx, &thread_shutdown);
 | 
						|
        });
 | 
						|
 | 
						|
        AsyncReader {
 | 
						|
            event_rx,
 | 
						|
            shutdown: shutdown_handle,
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// Stop the input event reading.
 | 
						|
    ///
 | 
						|
    /// You don't necessarily have to call this function because it will automatically be called when this reader goes out of scope.
 | 
						|
    ///
 | 
						|
    /// # Remarks
 | 
						|
    /// - Background thread will be closed.
 | 
						|
    /// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead.
 | 
						|
    pub fn stop_reading(&mut self) {
 | 
						|
        self.shutdown.store(true, Ordering::SeqCst);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl Iterator for AsyncReader {
 | 
						|
    type Item = InputEvent;
 | 
						|
 | 
						|
    /// Check if there are input events to read.
 | 
						|
    ///
 | 
						|
    /// It will return `None` when nothing is there to read, `Some(InputEvent)` if there are events to read.
 | 
						|
    ///
 | 
						|
    /// # Remark
 | 
						|
    /// - This is **not** a blocking call.
 | 
						|
    fn next(&mut self) -> Option<Self::Item> {
 | 
						|
        let mut iterator = self.event_rx.try_iter();
 | 
						|
 | 
						|
        match iterator.next() {
 | 
						|
            Some(char_value) => {
 | 
						|
                if let Ok(char_value) = parse_event(char_value, &mut iterator) {
 | 
						|
                    Some(char_value)
 | 
						|
                } else {
 | 
						|
                    None
 | 
						|
                }
 | 
						|
            }
 | 
						|
            None => None,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl Drop for AsyncReader {
 | 
						|
    fn drop(&mut self) {
 | 
						|
        self.stop_reading();
 | 
						|
    }
 | 
						|
}
 |