Merge branch 'development' of https://github.com/TimonPost/crossterm into development

This commit is contained in:
= 2018-08-21 18:07:01 +02:00
commit 33d587817e
18 changed files with 198 additions and 200 deletions

View File

@ -11,22 +11,17 @@ exclude = ["target", "Cargo.lock"]
readme = "README.md"
[dependencies]
lazy_static = "1.1.0"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }
winapi = { version = "0.3.5", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }
[target.'cfg(unix)'.dependencies]
libc = "0.2"
libc = "0.2.43"
termios = "0.3.0"
[lib]
name = "crossterm"
path = "src/lib.rs"
#[[bin]]
# name = "example_bin"
# path = "./examples/Crossterm 0.3.1/bin.rs"
[[example]]
name = "examples"
path = "examples/examples.rs"
@ -36,5 +31,5 @@ name = "logging"
path = "examples/program_examples/logging.rs"
[[example]]
name = "duplex"
path = "examples/program_examples/duplex.rs"
name = "command_bar"
path = "examples/program_examples/command_bar.rs"

View File

@ -10,7 +10,7 @@ Crossterm aims to be simple and easy to call in code.
Though the simplicity of Crossterm, you do not have to worry about the platform you are working with.
You can just call whatever action you want and behind the scenes it will check what to do based on the current platform.
This crate supports all unix and windows terminals down to windows XP (not all terminals are tested see [Tested Terminals](Link) for more info)
This crate supports all unix and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](Link) for more info)
## Table of contents:
- [Getting started](https://github.com/TimonPost/crossterm#getting-started)
@ -167,7 +167,7 @@ This module provides the functionalities to work with the terminal cursor.
```rust
use crossterm::Screen;
use crossterm::cursor::cursor;
use crossterm::cursor;
// create Screen to wheron the `cursor()` should function.
let screen = Screen::default();
@ -206,6 +206,32 @@ cursor.blink(true)
```
### Input | [see more](LINK)
This module provides the functionalities to work with terminal input.
Check [this](link) for handling async input.
```rust
use crossterm::Screen;
use crossterm::input;
// create Screen to wheron the `cursor()` should function.
let screen = Screen::default();
let mut input = input(&screen);
match input.read_char() {
Ok(s) => println!("char typed: {}", s),
Err(e) => println!("char error : {}", e),
}
match input.read_line() {
Ok(s) => println!("string typed: {}", s),
Err(e) => println!("error: {}", e),
}
```
### Terminal | [see more](LINK)
This module provides the functionalities to work with the terminal in general.
@ -254,11 +280,12 @@ Check these links: [AlternateScreen](https://github.com/TimonPost/crossterm/blob
- Windows 10 (pro)
- Windows CMD
- Windows 10 (pro)
- Windows 8.1 (N)
- Ubuntu Desktop Terminal
- Ubuntu 17.10
- Arch linux Konsole
This crate supports all Unix terminals and windows terminals down to Windows XP but not all of them have been tested.
This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested.
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
## Notice

View File

@ -7,8 +7,6 @@
//! - Run program with: `cargo run --example examples`
extern crate crossterm;
#[macro_use]
extern crate lazy_static;
// modules that could be test
mod terminal;
@ -17,62 +15,19 @@ mod cursor;
mod some_types;
mod input;
use crossterm::{Screen, Crossterm};
use crossterm::terminal::{Terminal, ClearType};
use crossterm::cursor::TerminalCursor;
use std::io::Write;
use std::{time, thread};
use std::sync::mpsc;
use std::sync::{Arc,Mutex};
use crossterm::cursor::cursor;
use std::io::Read;
fn main()
{
use crossterm::screen::RawScreen;
use crossterm::Screen;
fn main() {
use crossterm::color;
let mut screen = Screen::new(true);
let input = CROSSTERM.input();
let mut stdin = input.read_async().bytes();
CROSSTERM.cursor().hide();
write!(screen, "text \n\r");
let a = screen.enable_alternate_modes(true).unwrap();
let mut input_buf = Arc::new(Mutex::new(String::new()));
write!(a, "text \n\r");
loop
{
let a = stdin.next();
swap_write("dddd", &input_buf.lock().unwrap());
match a {
Some(Ok(b'\r')) =>
{
input_buf.lock().unwrap().clear();
// need to start receiving again because if pressed enter then async reading will stop
stdin = input.read_async().bytes();
}
Some(Ok(val)) =>
{
input_buf.lock().unwrap().push(val as char);
}
_ => {}
}
thread::sleep(time::Duration::from_millis(100));
}
}
pub fn swap_write(msg: &str, input_buf: &String) {
let term = CROSSTERM.terminal();
let (_, term_height) = term.terminal_size();
CROSSTERM.cursor().goto(0, term_height);
term.clear(ClearType::CurrentLine);
term.write(format!("{}\r\n", msg));
term.write(format!(">{}", input_buf));
}
lazy_static! {
static ref CROSSTERM: Crossterm = {
let screen = Screen::new(true);
Crossterm::new(&screen)
};
}

View File

@ -103,7 +103,6 @@ pub fn async_reading_on_alternate_screen() {
let screen = Screen::new(false);
// switch to alternate screen
if let Ok(alternate) = screen.enable_alternate_modes(true)
{

View File

@ -5,6 +5,17 @@ If you have created a game or something feel free to upload it, would be a great
The programs are:
- First depth search:
This is an search algorithm implemented visually. This program uses the following functionalities: cursor movement, coloring, alternate screen and terminal clearing.
- Duplex: This is a terminal application where there is some kind of conterminous output and with async input. So you could type an command while text is being outputted.
- This is an async logging program to demonstrate asynchronous working with crossterm.
This is an search algorithm implemented visually. This program uses the following functionalities: cursor movement, coloring, alternate screen and terminal clearing.
- Snake
This is a snake game implemented with this library.
- Command Bar:
This is a terminal application where multiple threads write to the output while you can enter commands asynchronously.
- Logging:
This is an async logging program to demonstrate asynchronous logging with an queue.

View File

@ -0,0 +1,94 @@
extern crate crossterm;
use crossterm::{Screen, Crossterm};
use crossterm::terminal::{terminal,Terminal, ClearType};
use crossterm::cursor::TerminalCursor;
use std::sync::{Arc,Mutex};
use std::io::Read;
use std::{thread,time};
fn main() {
use crossterm::color;
let screen = Screen::new(true);
let crossterm = Crossterm::new(&screen);
let input = crossterm.input();
let terminal = crossterm.terminal();
let cursor = crossterm.cursor();
let mut stdin = input.read_async().bytes();
cursor.hide();
let mut input_buf = Arc::new(Mutex::new(String::new()));
let mut count = 0;
let threads = log(input_buf.clone());
loop
{
let a = stdin.next();
match a {
Some(Ok(b'\r')) =>
{
input_buf.lock().unwrap().clear();
// need to start receiving again because if pressed enter then async reading will stop
stdin = input.read_async().bytes();
}
Some(Ok(val)) =>
{
input_buf.lock().unwrap().push(val as char);
}
_ => {}
}
thread::sleep(time::Duration::from_millis(100));
count += 1;
}
for thread in threads
{
thread.join();
}
cursor.show();
}
fn log(input_buf: Arc<Mutex<String>>) -> Vec<thread::JoinHandle<()>>
{
let mut threads = Vec::with_capacity(10);
let (_, term_height) = terminal(&Screen::default()).terminal_size();
for i in 0..10
{
let input_buffer = input_buf.clone();
let join = thread::spawn( move || {
let crossterm = Crossterm::new(&Screen::default());
let cursor = crossterm.cursor();
let terminal = crossterm.terminal();
for j in 0..1000
{
swap_write(format!("Some output: {} from thread: {}", j, i).as_ref(), &input_buffer.lock().unwrap(), &terminal, &cursor, term_height);
thread::sleep(time::Duration::from_millis(300));
}
});
threads.push(join);
}
return threads;
}
pub fn swap_write(msg: &str, input_buf: &String, terminal: &Terminal, cursor: &TerminalCursor, term_height: u16) {
cursor.goto(0, term_height);
terminal.clear(ClearType::CurrentLine);
terminal.write(format!("{}\r\n", msg));
terminal.write(format!(">{}", input_buf));
}

View File

@ -1,76 +0,0 @@
//! This bin folder can be used to try the examples out located in the examples directory.
//!
//! All you need to do is:
//!
//! - Download the crossterm source code.
//! - Add this in the Cargo.toml file:
//! ``` [[bin]]
//! name = "example_bin"
//! path = "./examples/bin.rs"
//! ```
//!
//! - Run program with: `cargo run`
extern crate crossterm;
use crossterm::style::Color;
use crossterm::Crossterm;
use crossterm::terminal::ClearType;
use std::thread::sleep;
use std::sync::{Arc,Mutex};
use std::io::Read;
use std::time::Duration;
// mod terminal;
// mod color;
// mod cursor;
// mod crossterm_type;
// mod input;
//use input::keyboard::{async_input, input as stdin};
use std::thread;
}fn main() {
let mut terminal = Arc::new(Mutex::new(Crossterm::new()));
let input = terminal.lock().unwrap().input().read_async();
terminal.lock().unwrap().enable_raw_mode();
let mut input_buf = Arc::new(Mutex::new(String::new()));
let mut key_buf = [0 as u8; 32];
thread::spawn(move || {
loop {
swap_write(&mut terminal.lock().unwrap(), "random program output",&input_buf.lock().unwrap());
sleep(Duration::from_millis(100));
}
});
loop {
let mut term = terminal.lock().unwrap();
let (term_width, term_height) = term.terminal().terminal_size();
if let Ok(count) = input.read(&mut key_buf) {
for idx in 0..count {
let b = key_buf.get(idx).unwrap();
if *b == 3 {
std::process::exit(0); // Ctrl+C = exit immediate
} else if *b == 13 {
// The return key was pressed.
let mut input_buf_tmp = &mut input_buf.lock().unwrap();
input_buf.lock().unwrap().clear();
swap_write(&mut term, "", &input_buf_tmp);
} else {
let mut input_buf_tmp = &mut input_buf.lock().unwrap();
input_buf_tmp.push(*b as char);
swap_write(&mut term, "", &input_buf_tmp);
}
}
}
}
pub fn swap_write(terminal: &mut Crossterm, msg: &str, input_buf: &String) {
let (term_width, term_height) = terminal.terminal().terminal_size();
terminal.cursor().goto(0, term_height);
terminal.terminal().clear(ClearType::CurrentLine);
terminal
.terminal()
.write(format!("{}\n\r>{}", msg, input_buf));
}

View File

@ -7,7 +7,9 @@ use winapi::um::wincon;
use winapi::shared::minwindef::DWORD;
use winapi::um::wincon::{ENABLE_VIRTUAL_TERMINAL_PROCESSING};
use winapi::um::winnt::HANDLE;
use std::io::{Error, ErrorKind, Result};
use std::sync::Arc;
/// This command is used for enabling and disabling ANSI code support for windows systems,
/// For more info check: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.
@ -72,14 +74,12 @@ impl IEnableAnsiCommand for EnableAnsiCommand {
pub struct RawModeCommand {
mask: DWORD,
}
use self::wincon::{ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT, ENABLE_WRAP_AT_EOL_OUTPUT, ENABLE_PROCESSED_OUTPUT};
impl RawModeCommand
{
pub fn new() -> Self {
use self::wincon::{ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT};
RawModeCommand {
mask: ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT,
mask: ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT,
}
}
}
@ -87,10 +87,11 @@ impl RawModeCommand
impl RawModeCommand {
/// Enables raw mode.
pub fn enable(&mut self) -> Result<()> {
let input_handle = handle::get_input_handle()?;
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&input_handle, &mut dw_mode) {
let stdout = handle::get_output_handle().unwrap();
if !kernel::get_console_mode(&stdout, &mut dw_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not get console mode when enabling raw mode",
@ -99,7 +100,7 @@ impl RawModeCommand {
let new_mode = dw_mode & !self.mask;
if !kernel::set_console_mode(&input_handle, new_mode) {
if !kernel::set_console_mode(&stdout, new_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not set console mode when enabling raw mode",
@ -111,10 +112,10 @@ impl RawModeCommand {
/// Disables raw mode.
pub fn disable(&self) -> Result<()> {
let output_handle = handle::get_input_handle()?;
let stdout = handle::get_output_handle().unwrap();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode) {
if !kernel::get_console_mode(&stdout, &mut dw_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not get console mode when disabling raw mode",
@ -123,7 +124,7 @@ impl RawModeCommand {
let new_mode = dw_mode | self.mask;
if !kernel::set_console_mode(&output_handle, new_mode) {
if !kernel::set_console_mode(&stdout, new_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not set console mode when disabling raw mode",

View File

@ -7,7 +7,7 @@
//! Vim uses the entirety of the screen to edit the file, then returning to bash leaves the original buffer unchanged.
use super::commands::{self, IAlternateScreenCommand};
use super::{functions, Screen, TerminalOutput};
use super::{functions, Screen, TerminalOutput,RawScreen};
use std::io;
use std::convert::From;
@ -34,7 +34,8 @@ impl AlternateScreen {
/// The alternate buffer is exactly the dimensions of the window, without any scrollback region.
/// For an example of this behavior, consider when vim is launched from bash.
/// Vim uses the entirety of the screen to edit the file, then returning to bash leaves the original buffer unchanged.
pub fn to_alternate_screen(stdout: TerminalOutput) -> io::Result<AlternateScreen> {
pub fn to_alternate_screen(stdout: TerminalOutput, raw_mode: bool) -> io::Result<AlternateScreen> {
#[cfg(target_os = "windows")]
let command = functions::get_module::<Box<commands::IAlternateScreenCommand + Send>>(
Box::from(commands::win_commands::ToAlternateScreenCommand::new()),
@ -46,7 +47,15 @@ impl AlternateScreen {
let mut stdout = stdout;
command.enable(&mut stdout)?;
return Ok(AlternateScreen::new(command, Screen::from(stdout)));
let screen = Screen::from(stdout);
if raw_mode
{
RawScreen::into_raw_mode();
}
return Ok(AlternateScreen::new(command, screen));
}
/// Switch the alternate screen back to main screen.

View File

@ -15,8 +15,10 @@
//! With these modes you can easier design the terminal screen.
use super::commands::*;
use TerminalOutput;
use std::io;
use std::sync::Arc;
/// A wrapper for the raw terminal state. Which can be used to write to.
pub struct RawScreen;

View File

@ -55,33 +55,13 @@ impl Screen
{
if raw_mode
{
RawScreen::into_raw_mode();;
return Screen { stdout: Arc::new(TerminalOutput::new(true)), buffer: Vec::new() };
let screen = Screen { stdout: Arc::new(TerminalOutput::new()), buffer: Vec::new() };
return screen;
}
return Screen::default();
}
/// This method could be used for enabling raw mode for the terminal.
///
/// What exactly is raw state:
/// - No line buffering.
/// Normally the terminals uses line buffering. This means that the input will be send to the terminal line by line.
/// With raw mode the input will be send one byte at a time.
/// - Input
/// All input has to be written manually by the programmer.
/// - Characters
/// The characters are not processed by the terminal driver, but are sent straight through.
/// Special character have no meaning, like backspace will not be interpret as backspace but instead will be directly send to the terminal.
/// - Escape characters
/// Note that in raw modes `\n` will move to the new line but the cursor will be at the same position as before on the new line therefor use `\n\r` to start at the new line at the first cell.
///
/// With these modes you can easier design the terminal screen.
pub fn enable_raw_modes(&self) -> Result<()> {
RawScreen::into_raw_mode()?;
return Ok(())
}
/// Switch to alternate screen. This function will return an `AlternateScreen` instance if everything went well this type will give you control over the `AlternateScreen`.
///
/// # What is Alternate screen?
@ -90,14 +70,9 @@ impl Screen
/// For an example of this behavior, consider when vim is launched from bash.
/// Vim uses the entirety of the screen to edit the file, then returning to bash leaves the original buffer unchanged.
pub fn enable_alternate_modes(&self, raw_mode: bool) -> Result<AlternateScreen> {
let stdout = TerminalOutput::new(raw_mode);
let stdout = TerminalOutput::new();
if raw_mode
{
RawScreen::into_raw_mode();
}
let alternate_screen = AlternateScreen::to_alternate_screen(stdout)?;
let alternate_screen = AlternateScreen::to_alternate_screen(stdout, raw_mode)?;
return Ok(alternate_screen);
}
}
@ -122,7 +97,7 @@ impl Default for Screen
{
/// Create an new screen which will not be in raw mode or alternate mode.
fn default() -> Self {
return Screen { stdout: Arc::new(TerminalOutput::new(false)), buffer: Vec::new() };
return Screen { stdout: Arc::new(TerminalOutput::new()), buffer: Vec::new() };
}
}

View File

@ -12,7 +12,7 @@ use Screen;
///
/// ```rust
/// extern crate crossterm;
/// use self::crossterm::cursor::cursor;
/// use self::crossterm::cursor;
/// use self::crossterm::Screen;
///
/// let screen = Screen::default();

View File

@ -11,7 +11,7 @@ use Screen;
/// ```rust
/// extern crate crossterm;
/// use self::crossterm::Screen;
/// use self::crossterm::input::input;
/// use self::crossterm::input;
///
/// let screen = Screen::default();
/// let input = input(&screen);

View File

@ -22,6 +22,7 @@ use super::*;
use std::any::Any;
use std::default::Default;
use screen::RawScreen;
/// Struct that is an handle to an terminal screen.
/// This handle could be used to write to the current screen
@ -34,7 +35,7 @@ pub struct TerminalOutput {
impl TerminalOutput {
/// Create new screen write instance whereon screen related actions can be performed.
pub fn new(is_in_raw_mode: bool) -> Self {
pub fn new() -> Self {
#[cfg(target_os = "windows")]
let stdout: Box<IStdout + Send + Sync> = functions::get_module::<Box<IStdout + Send + Sync>>(
Box::from(WinApiOutput::new()),
@ -44,7 +45,7 @@ impl TerminalOutput {
#[cfg(not(target_os = "windows"))]
let stdout = Box::from(AnsiOutput::new()) as Box<IStdout + Send + Sync>;
TerminalOutput { stdout , is_in_raw_mode}
TerminalOutput { stdout , is_in_raw_mode: false}
}
/// Write String to the current screen.

View File

@ -1,4 +1,6 @@
use super::IStdout;
use screen::RawScreen;
use common::commands::win_commands::RawModeCommand;
use kernel::windows_kernel::{handle, writing};
use winapi::um::winnt::HANDLE;
@ -9,6 +11,7 @@ use std::io;
/// This struct is a wrapper for WINAPI `HANDLE`
pub struct WinApiOutput {
pub handle: Mutex<HANDLE>,
raw_mode: bool,
}
impl IStdout for WinApiOutput {
@ -21,7 +24,6 @@ impl IStdout for WinApiOutput {
writing::write_char_buffer(&self.handle.lock().unwrap(), buf)
}
fn flush(&self) -> io::Result<()> {
Ok(())
}
@ -37,7 +39,8 @@ impl IStdout for WinApiOutput {
impl WinApiOutput {
pub fn new() -> Self {
WinApiOutput { handle: Mutex::new(handle::get_output_handle().unwrap()) }
let handle = handle::get_output_handle().unwrap();
WinApiOutput { raw_mode: false, handle: Mutex::new(handle) }
}
pub fn set(&mut self, handle: HANDLE)
@ -47,8 +50,7 @@ impl WinApiOutput {
pub fn get_handle(&self) -> HANDLE
{
let gx = self.handle.lock();
gx.unwrap().clone()
return self.handle.lock().unwrap().clone();
}
}

View File

@ -7,11 +7,14 @@ use Screen;
/// Struct that stores an specific platform implementation for color related actions.
///
/// For styling text use the `::crossterm::style()` function. `TerminalColor` will set the colors of the screen permanently and the `style()` will only style the text given.
///
/// Check `/examples/color` in the library for more specific examples.
///
///
/// ```rust
/// use crossterm::{Screen}
/// use crossterm::color::color;
/// use crossterm::style::color;
///
/// let screen = Screen::default();
/// let colored_terminal = color(&screen);

View File

@ -140,7 +140,7 @@ impl<D: Display> StyledObject<D> {
self.attr(Attribute::CrossedOut)
}
/// This could be used to paint the styled object on the screen. Pass a refrence to the screen whereon you want to perform the painting.
/// This could be used to paint the styled object on the screen. Pass a reference to the screen whereon you want to perform the painting.
///
/// ``` rust
/// style("Some colored text")

View File

@ -10,7 +10,7 @@ use std::fmt;
/// Check `/examples/terminal` in the library for more specific examples.
///
/// ```rust
/// use crossterm::terminal::terminal;
/// use crossterm::terminal;
///
/// let screen = Screen::default();
/// let term = terminal(&screen);