diff --git a/Cargo.toml b/Cargo.toml index b873da8..d7483c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,4 +25,4 @@ path = "src/lib.rs" [[bin]] name = "example_bin" - path = "./examples/Crossterm 0.3.0/bin.rs" \ No newline at end of file + path = "./examples/Crossterm 0.3.1/bin.rs" \ No newline at end of file diff --git a/examples/Crossterm 0.3.0/bin.rs b/examples/Crossterm 0.3.0/bin.rs index 8e36747..859008b 100644 --- a/examples/Crossterm 0.3.0/bin.rs +++ b/examples/Crossterm 0.3.0/bin.rs @@ -19,14 +19,53 @@ use crossterm::Context; // mod color; // mod cursor; // mod crossterm_type; +use crossterm::raw::IntoRawMode; +use std::{thread, time}; +use std::io::Read; fn main() { - let context = Context::new(); - let input = ::crossterm::input::input(&context); - let line = input.read_line().unwrap(); - println!("input: {}",line); + +// let mut rv = String::new(); +// { +// let alternate = ::crossterm::screen::AlternateScreen::from(context.clone()); +// alternate.into_raw_mode(context.clone()); + +// thread::spawn(|| { +// let context = Context::new(); +// let input = ::crossterm::input::input(&context); +// let result = input.read_async().unwrap(); +// println!("input: {:?}",result); +// }); + + let context = Context::new(); + let input = ::crossterm::input::input(&context); + let mut stdin = input.read_until_async(b'\r' as u8).bytes(); + + for i in 0..100 + { + let a = stdin.next(); + + println!("input: {:?} exptected: {:?}", a,b'\r'); + + if let Some(Ok(b'q')) = a { + break; + } + + thread::sleep(time::Duration::from_millis(50)); +// println!("Some data {:?}", b) + } + + + +// ::std::io::stdin().read_line(&mut rv); +// let len = rv.trim_right_matches(&['\r', '\n'][..]).len(); +// rv.truncate(len); + + +// } + } diff --git a/examples/Crossterm 0.3.1/README.md b/examples/Crossterm 0.3.1/README.md new file mode 100644 index 0000000..2e1d360 --- /dev/null +++ b/examples/Crossterm 0.3.1/README.md @@ -0,0 +1,7 @@ +This folder contains examples for version 0.3.0 Here you will find examples of all the functionalities crossterm offers. + +It has 4 modules: +- color (this is about all the styling of the terminal) +- cursor (this is about all the actions you can perform with the cursor) +- terminal (this is about all the actions you can perform on the terminal) +- program examples (this folder will contain some real life examples) diff --git a/examples/Crossterm 0.3.1/bin.rs b/examples/Crossterm 0.3.1/bin.rs new file mode 100644 index 0000000..e373dfc --- /dev/null +++ b/examples/Crossterm 0.3.1/bin.rs @@ -0,0 +1,33 @@ + +//! 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::Context; + +// mod terminal; +// mod color; +// mod cursor; +// mod crossterm_type; +mod input; + +use input::keyboard::{async_input, input as stdin}; + +fn main() +{ + async_input::read_async(); +// stdin::read_line(); +// stdin::read_char(); +} + + diff --git a/examples/Crossterm 0.3.1/color/mod.rs b/examples/Crossterm 0.3.1/color/mod.rs new file mode 100644 index 0000000..548c9ec --- /dev/null +++ b/examples/Crossterm 0.3.1/color/mod.rs @@ -0,0 +1,206 @@ +//! +//! Examples of coloring the terminal. +//! + +extern crate crossterm; + +use self::crossterm::style::Color; +use self::crossterm::terminal; +use self::crossterm::Context; + +/// print some red font | demonstration. +pub fn paint_foreground() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + // Pass an string to the `paint()` method with you want to paint. + // This will give you an object back wits can be styled and displayed. + let mut styledobject = terminal.paint("Red font"); + // Call the method `with()` on the object given by `paint()` and pass in any Color from the Color enum. + styledobject = styledobject.with(Color::Red); + // Print the object to the console and see the result. + println!("{}", styledobject); + + // Crossterm provides method chaining so that the above points can be inlined. + println!("{}", terminal.paint("Red font").with(Color::Red)); +} + +/// print some font on red background | demonstration. +pub fn paint_background() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + // Pass an string to the `paint()` method with you want to paint. + // This will give you an object back wits can be styled and displayed. + let mut styledobject = terminal.paint("Red background color"); + // Call the method `on()` on the object given by `paint()` and pass in an Color from the Color enum. + styledobject = styledobject.on(Color::Red); + // Print the object to the console and check see the result + println!("{}", styledobject); + + // Crossterm provides method chaining so that the above points can be inlined. + println!("{}", terminal.paint("Red background color").on(Color::Red)); +} + +/// print font with fore- background color | demonstration. +pub fn paint_foreground_and_background() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + // Pass an string to the `paint()` method with you want to paint. + // This will give you an object back wits can be styled and displayed. + let mut styledobject = terminal.paint("Red font on blue background color"); + /* Foreground color: + Call the method `with()` on the object given by `paint()` + Pass in an Color from the Color enum. + */ + styledobject = styledobject.with(Color::Red); + /* Background color: + Call the method `on()` on the object given by `paint()` + Pass in an Color from the Color enum. + */ + styledobject = styledobject.on(Color::Blue); + // Print the object to the console and see the result. + println!("{}", styledobject); + + // Crossterm provides method chaining so that the above points can be inlined. + println!( + "{}", + terminal + .paint("Red font on blue background color") + .with(Color::Red) + .on(Color::Blue) + ); +} + +/// Print all available foreground colors | demonstration. +pub fn print_all_foreground_colors() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + println!("Black : \t {}", terminal.paint("■").with(Color::Black)); + println!("Red : \t\t {}", terminal.paint("■").with(Color::Red)); + println!( + "Dark Red: \t {}", + terminal.paint("■").with(Color::DarkRed) + ); + println!("Green : \t {}", terminal.paint("■").with(Color::Green)); + println!( + "Dark Green : \t {}", + terminal.paint("■").with(Color::DarkGreen) + ); + println!("Yellow : \t {}", terminal.paint("■").with(Color::Yellow)); + println!( + "Dark Yellow : \t {}", + terminal.paint("■").with(Color::DarkYellow) + ); + println!("Blue : \t\t {}", terminal.paint("■").with(Color::Blue)); + println!( + "Dark Blue : \t {}", + terminal.paint("■").with(Color::DarkBlue) + ); + println!( + "Magenta : \t {}", + terminal.paint("■").with(Color::Magenta) + ); + println!( + "Dark Magenta : \t {}", + terminal.paint("■").with(Color::DarkMagenta) + ); + println!("Cyan : \t\t {}", terminal.paint("■").with(Color::Cyan)); + println!( + "Dark Cyan : \t {}", + terminal.paint("■").with(Color::DarkCyan) + ); + println!("Grey : \t\t {}", terminal.paint("■").with(Color::Grey)); + println!("White : \t {}", terminal.paint("■").with(Color::White)); +} + +/// Print all available foreground colors | demonstration. +pub fn print_all_background_colors() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + println!("Black : \t {}", terminal.paint(" ").on(Color::Black)); + println!("Red : \t\t {}", terminal.paint(" ").on(Color::Red)); + println!("Dark Red: \t {}", terminal.paint(" ").on(Color::DarkRed)); + println!("Green : \t {}", terminal.paint(" ").on(Color::Green)); + println!( + "Dark Green : \t {}", + terminal.paint(" ").on(Color::DarkGreen) + ); + println!("Yellow : \t {}", terminal.paint(" ").on(Color::Yellow)); + println!( + "Dark Yellow : \t {}", + terminal.paint(" ").on(Color::DarkYellow) + ); + println!("Blue : \t\t {}", terminal.paint(" ").on(Color::Blue)); + println!( + "Dark Blue : \t {}", + terminal.paint(" ").on(Color::DarkBlue) + ); + println!("Magenta : \t {}", terminal.paint(" ").on(Color::Magenta)); + println!( + "Dark Magenta : \t {}", + terminal.paint(" ").on(Color::DarkMagenta) + ); + println!("Cyan : \t\t {}", terminal.paint(" ").on(Color::Cyan)); + println!( + "Dark Cyan : \t {}", + terminal.paint(" ").on(Color::DarkCyan) + ); + println!("Grey : \t\t {}", terminal.paint(" ").on(Color::Grey)); + println!("White : \t {}", terminal.paint(" ").on(Color::White)); + #[cfg(unix)] + println!( + "RGB (10,10,10): \t {}", + terminal.paint(" ").on(Color::Rgb { + r: 10, + g: 10, + b: 10 + }) + ); + #[cfg(unix)] + println!( + "RGB (10,10,10): \t {}", + terminal.paint(" ").on(Color::AnsiValue(50)) + ); +} + +/// Print font with all available attributes. Note that this can only be used at unix systems and that some are not supported widely | demonstration.. +#[cfg(unix)] +pub fn print_font_with_attributes() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + println!("{}", terminal.paint("Normal text")); + println!("{}", terminal.paint("Bold text").bold()); + println!("{}", terminal.paint("Italic text").italic()); + println!("{}", terminal.paint("Slow blinking text").slow_blink()); + println!("{}", terminal.paint("Rapid blinking text").rapid_blink()); + println!("{}", terminal.paint("Hidden text").hidden()); + println!("{}", terminal.paint("Underlined text").underlined()); + println!("{}", terminal.paint("Reversed color").reverse()); + println!("{}", terminal.paint("Dim text color").dim()); + println!("{}", terminal.paint("Crossed out font").crossed_out()); +} + +/// Print all supported rgb colors | demonstration. +#[cfg(unix)] +pub fn print_supported_colors() { + let context = Context::new(); + let terminal = terminal::terminal(&context); + + let count = crossterm::style::color(&context) + .get_available_color_count() + .unwrap(); + + for i in 0..count { + println!( + "{}", + terminal + .paint(format!("Color: {}", i)) + .with(Color::AnsiValue(i as u8)) + ); + } +} diff --git a/examples/Crossterm 0.3.1/crossterm_type/mod.rs b/examples/Crossterm 0.3.1/crossterm_type/mod.rs new file mode 100644 index 0000000..9a7671b --- /dev/null +++ b/examples/Crossterm 0.3.1/crossterm_type/mod.rs @@ -0,0 +1,67 @@ +extern crate crossterm; + +use crossterm::Crossterm; + +/// use the `Crossterm` to get an instance to the cursor module | demonstration. +pub fn use_crossterm_cursor() +{ + let crossterm = Crossterm::new(); + let mut cursor = crossterm.cursor(); + cursor.goto(5,5).print("test"); +} + +use crossterm::style::Color; + +/// use the `Crossterm` to get an instance to the color module | demonstration. +pub fn use_crossterm_color() +{ + let crossterm = Crossterm::new(); + let mut color = crossterm.color(); + color.set_bg(Color::Red); + color.set_fg(Color::Green); +} + +use crossterm::terminal::ClearType; + +/// use the `Crossterm` to get an instance to the terminal module | demonstration. +pub fn use_crossterm_terminal() +{ + let crossterm = Crossterm::new(); + let mut terminal = crossterm.terminal(); + terminal.clear(ClearType::All); + terminal.set_size(40,40); +} + +/// paint text with colors using `Crossterm` | demonstration. +pub fn use_crossterm_paint() +{ + let crossterm = Crossterm::new(); + crossterm.paint("Black on BLUE").with(Color::Black).on(Color::Blue); +} + +/// write text to terminal using `Crossterm` | demonstration. +pub fn use_crossterm_write() +{ + let crossterm = Crossterm::new(); + crossterm.write("some text \nsome text on new line"); +} + +/// Switch to alternate screen using the `Context` of `Crossterm` | demonstration. +pub fn create_alternate_screen_from_crossterm() +{ + use crossterm::screen::*; + use std::convert::From; + + let crossterm = Crossterm::new(); + + { + // move into alternate screen + let alternate_screen = AlternateScreen::from(crossterm.context()); + + // this will move the cursor and print `some text` on the alternate screen. + crossterm.cursor().goto(10, 10).print("Some text"); + } // <- alternate screen ends here an will be switched back to main screen. + + // print "Some other text" on the mainscreen at x: 0, y: 10 + crossterm.cursor().goto(0,10).print("Some other text"); +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/cursor/mod.rs b/examples/Crossterm 0.3.1/cursor/mod.rs new file mode 100644 index 0000000..2345778 --- /dev/null +++ b/examples/Crossterm 0.3.1/cursor/mod.rs @@ -0,0 +1,141 @@ +//! +//! Examples of actions that could be performed with te cursor. +//! + +extern crate crossterm; +use self::crossterm::cursor::{cursor, TerminalCursor}; +use self::crossterm::Context; + +/// Set the cursor to position X: 10, Y: 5 in the terminal. +pub fn goto() { + let context = Context::new(); + + // Get the cursor + let mut cursor = cursor(&context); + // Set the cursor to position X: 10, Y: 5 in the terminal + cursor.goto(10, 5); +} + +/// get the cursor position +pub fn pos() { + let context = Context::new(); + + // Get the cursor + let mut cursor = cursor(&context); + // get the cursor position. + let (x, y) = cursor.pos(); +} + +/// Move the cursor 3 up | demonstration. +pub fn move_up() { + let context = Context::new(); + + // Get the cursor + let mut cursor = cursor(&context); + + // Move the cursor to position 3 times to the up in the terminal + cursor.move_up(10); +} + +/// Move the cursor 3 to the right | demonstration. +pub fn move_right() { + let context = Context::new(); + + // Get the cursor + let mut cursor = cursor(&context); + // Move the cursor to position 3 times to the right in the terminal + cursor.move_right(3); +} + +/// Move the cursor 3 down | demonstration. +pub fn move_down() { + let context = Context::new(); + + // Get the cursor + let mut cursor = cursor(&context); + // Move the cursor to position 3 times to the down in the terminal + cursor.move_down(3); +} + +/// Move the cursor 3 to the left | demonstration. +pub fn move_left() { + let context = Context::new(); + + // Get the cursor + let mut cursor = cursor(&context); + // Move the cursor to position 3 times to the left in the terminal + cursor.move_left(3); +} + +/// Print character at X: 10 Y: 5 | demonstration. +pub fn print() { + let context = Context::new(); + + // To print an some displayable content on an certain position. + + // Get the cursor + let mut cursor = cursor(&context); + // Set the cursor to position X: 10, Y: 5 in the terminal + cursor.goto(10, 5); + // Print the @ symbol at position X: 10, Y: 5 in the terminal + print!("@"); + // Rust is line buffered inorder to print at an certain position we need to clear the buffer first. + use std; + use std::io::Write; + std::io::stdout().flush(); + + /* Because the above method is a little to much code, + you can use the `print()` method for printing an value at an certain position in the terminal. + + Crossterm provides method chaining so that the above points can be inlined. + */ + + cursor.goto(10, 5).print("@"); +} + +/// Save and reset cursor position | demonstration.. +pub fn safe_and_reset_position() { + let context = Context::new(); + + let mut cursor = cursor(&context); + + // Goto X: 5 Y: 5 + cursor.goto(5, 5); + // Safe cursor position: X: 5 Y: 5 + cursor.save_position(); + // Goto X: 5 Y: 20 + cursor.goto(5, 20); + // Print at X: 5 Y: 20. + println!("Yea!"); + // Reset back to X: 5 Y: 5. + cursor.reset_position(); + // Print Back at X: 5 Y: 5. + println!("Back"); + + println!() +} + +/// Hide cursor display | demonstration. +pub fn hide_cursor() { + let context = Context::new(); + + let cursor = cursor(&context); + cursor.hide(); +} + +/// Show cursor display | demonstration. +pub fn show_cursor() { + let context = Context::new(); + + let cursor = cursor(&context); + cursor.show(); +} + +/// Show cursor display, only works on certain terminals.| demonstration +pub fn blink_cursor() { + let context = Context::new(); + + let cursor = cursor(&context); + cursor.blink(false); + cursor.blink(false); +} diff --git a/examples/Crossterm 0.3.1/input/keyboard/async_input.rs b/examples/Crossterm 0.3.1/input/keyboard/async_input.rs new file mode 100644 index 0000000..aa602ed --- /dev/null +++ b/examples/Crossterm 0.3.1/input/keyboard/async_input.rs @@ -0,0 +1,51 @@ +extern crate crossterm; + +use self::crossterm::input::input; +use self::crossterm::Context; + +use std::{thread, time}; +use std::io::Read; + +// this will capture the input until the given key was pressed. +pub fn capture_input_until_a_certain_char_async() +{ + let context = Context::new(); + let input = input(&context); + + let mut stdin = input.read_until_async(b'\r').bytes(); + + for i in 0..100 + { + let a = stdin.next(); + + if let Some(Ok(b'x')) = a { + println!("The key: x was pressed."); + break; + } + + thread::sleep(time::Duration::from_millis(50)); + } +} + +// this will capture an character input until the given key was pressed. +pub fn read_async() +{ + let context = Context::new(); + let input = input(&context); + + let mut stdin = input.read_async().bytes(); + + for i in 0..100 + { + let a = stdin.next(); + + println!("pressed: {:?}", a); + + if let Some(Ok(b'x')) = a { + println!("The key: x was pressed."); + break; + } + + thread::sleep(time::Duration::from_millis(50)); + } +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/input/keyboard/input b/examples/Crossterm 0.3.1/input/keyboard/input new file mode 100644 index 0000000..e69de29 diff --git a/examples/Crossterm 0.3.1/input/keyboard/input.rs b/examples/Crossterm 0.3.1/input/keyboard/input.rs new file mode 100644 index 0000000..0d05e4b --- /dev/null +++ b/examples/Crossterm 0.3.1/input/keyboard/input.rs @@ -0,0 +1,28 @@ +extern crate crossterm; + +use self::crossterm::input::input; +use self::crossterm::Context; + +pub fn read_char() +{ + let context = Context::new(); + let input = input(&context); + + match input.read_char() + { + Ok(c) => println!("character pressed: {}", c), + Err(e) => println!("error: {}", e) + } +} + +pub fn read_line() +{ + let context = Context::new(); + let input = input(&context); + + match input.read_line() + { + Ok(s) => println!("string typed: {}", s), + Err(e) => println!("error: {}", e) + } +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/input/keyboard/mod.rs b/examples/Crossterm 0.3.1/input/keyboard/mod.rs new file mode 100644 index 0000000..e75eca0 --- /dev/null +++ b/examples/Crossterm 0.3.1/input/keyboard/mod.rs @@ -0,0 +1,2 @@ +pub mod async_input; +pub mod input; \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/input/mod.rs b/examples/Crossterm 0.3.1/input/mod.rs new file mode 100644 index 0000000..d731c41 --- /dev/null +++ b/examples/Crossterm 0.3.1/input/mod.rs @@ -0,0 +1 @@ +pub mod keyboard; \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/README.md b/examples/Crossterm 0.3.1/program_examples/README.md new file mode 100644 index 0000000..65103a4 --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/README.md @@ -0,0 +1,8 @@ +This folder will contain some examples of how to use this crate in an real live environment. + +If you have created a game or something feel free to upload it, would be a great help for other people and me to make this crate better! + +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. \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/Cargo.toml b/examples/Crossterm 0.3.1/program_examples/first_depth_search/Cargo.toml new file mode 100644 index 0000000..8f6fe91 --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "first_depth_search" +version = "0.1.0" +authors = ["TimonPost "] + +[dependencies] +rand = "0.4.2" + +[dependencies.crossterm] +path = "../../../../" +branch = "development" \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/algorithm.rs b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/algorithm.rs new file mode 100644 index 0000000..f7b33db --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/algorithm.rs @@ -0,0 +1,155 @@ +//! Implementation of the first depth search algorithm +use super::variables::{Direction, Position}; +use super::messages::END_MESSAGE; +use super::map::Map; + +use crossterm::style::Color; +use crossterm::Crossterm; + +use super::rand; +use super::rand::distributions::{IndependentSample, Range}; + +use std::io::{stdout, Write}; +use std::{thread, time}; + +pub struct FirstDepthSearch<'crossterm> +{ + direction: Direction, + map: Map, + stack: Vec, + root_pos: Position, + is_terminated: bool, + crossterm: &'crossterm Crossterm +} + +impl<'crossterm> FirstDepthSearch<'crossterm> +{ + pub fn new(map: Map, start_pos: Position, crossterm: &'crossterm Crossterm) -> FirstDepthSearch<'crossterm> + { + FirstDepthSearch + { + direction: Direction::Up, + map: map, + stack: Vec::new(), + root_pos: start_pos, + is_terminated: false, + crossterm: crossterm, + } + } + + pub fn start(&mut self) + { + self.is_terminated = false; + + // push first position on the stack + self.stack.push(self.root_pos); + + let mut cursor = self.crossterm.cursor(); + cursor.hide(); + + // loop until there are now items left in the stack. + loop { + if self.stack.len() == 0 + { + break; + } + + self.choose_random_neighbor(); + + if self.is_terminated + { + break; + } + + self.update_position(); + + let cell = self.crossterm.paint(" ").on(Color::Blue); + + let pos = self.root_pos.clone(); + + let x = pos.x as u16; + let y = pos.y as u16; + + cursor.goto(x,y).print(cell); + ::std::io::stdout().flush(); + + thread::sleep(time::Duration::from_millis(2)); + } + } + + /// With this function we are choosing an random neighbor that we havent visited yet. + fn choose_random_neighbor(&mut self) + { + let mut available_directions: Vec = Vec::with_capacity(4); + + // check every direction if the direction is not visited we can add it to the list. + // note that if the y or x is 0 that we don't want to subtract because we get an subtract overflow. + if self.root_pos.y != 0 && !self.map.is_cell_visited(self.root_pos.x, self.root_pos.y - 1) + { + available_directions.push(Direction::Up) + } + + if !&self.map.is_cell_visited(self.root_pos.x, self.root_pos.y + 1) + { + available_directions.push(Direction::Down) + } + + if self.root_pos.x != 0 && !self.map.is_cell_visited(self.root_pos.x - 1, self.root_pos.y) + { + available_directions.push(Direction::Left) + } + + if !&self.map.is_cell_visited(self.root_pos.x + 1, self.root_pos.y) + { + available_directions.push(Direction::Right) + } + + let directions_count = available_directions.len(); + + // if there are no directions left we need to backtrack until we find directions to go to. + if directions_count != 0 + { + let step = Range::new(0, directions_count); + let mut rng = rand::thread_rng(); + let choice = step.ind_sample(&mut rng); + + // set the current direction to the new random generated direction. + self.direction = available_directions[choice]; + } + else { + self.find_first_possible_direction(); + } + } + + /// Find direction to go to if there is no direction pop the current position of the stack for back tracking to the previous position. + fn find_first_possible_direction(&mut self) + { + // if there are no elements left in the stack that means we have visited all cell and we van terminate the program. + if let Some(previous_cell) = &self.stack.pop() + { + // update root pos to previous cell and continue searching for new neighbours + self.root_pos = *previous_cell; + self.choose_random_neighbor(); + } + else { + self.is_terminated = true; + } + } + + /// update the root position to the new direction we went in + fn update_position(&mut self) + { + match self.direction + { + Direction::Up => self.root_pos.y -= 1, + Direction::Down => self.root_pos.y += 1, + Direction::Left => self.root_pos.x -= 1, + Direction::Right => self.root_pos.x += 1, + _ => panic!() + }; + + self.map.set_visited(self.root_pos.x, self.root_pos.y); + self.stack.push(self.root_pos); + } +} + diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/main.rs b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/main.rs new file mode 100644 index 0000000..1ce5e18 --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/main.rs @@ -0,0 +1,90 @@ +extern crate rand; +extern crate crossterm; + +mod map; +mod algorithm; +mod messages; +mod variables; + +use crossterm::Crossterm; +use crossterm::terminal::ClearType; +use crossterm::style::Color; +use crossterm::screen; + +use self::variables::{Size, Position }; +use self::messages::WELCOME_MESSAGE; + +use std::iter::Iterator; +use std::{thread, time}; + +fn main() +{ + run(); +} + +/// run the program +pub fn run() +{ +// // create new Crossterm instance. +// let mut crossterm = Crossterm::new(); +// +// print_welcome_screen(&crossterm); +// +// start_algorithm(&mut crossterm); +// +// print_end_screen(&crossterm); + + +} + +fn start_algorithm(crossterm: &mut Crossterm) +{ + // we first want to switch to alternate screen. On the alternate screen we are going to run or firstdepthsearch algorithm + let alternate_screen = screen::AlternateScreen::from(crossterm.context()); + + // setup the map size and the position to start searching for a path. + let map_size = Size::new(100,40); + let start_pos = Position::new(10,10); + + // create and render the map. Or map border is going to have an █ look and inside the map is just a space. + let mut map = map::Map::new(map_size, '█', ' '); + map.render_map(crossterm); + + // create the algorithm and start the + let mut algorithm = algorithm::FirstDepthSearch::new(map, start_pos, &crossterm); + algorithm.start(); +} + +fn print_end_screen(crossterm: &Crossterm) +{ + +} + +fn print_welcome_screen(crossterm: &Crossterm) +{ + // create the handle for the cursor and terminal. + let mut cursor = crossterm.cursor(); + let mut terminal = crossterm.terminal(); + + // clear the screen and print the welcome message. + terminal.clear(ClearType::All); + cursor.goto(0,0); + terminal.write(WELCOME_MESSAGE.join("\n")); + + cursor.hide(); + cursor.goto(0,10); + terminal.write( + "The first depth search algorithm will start in: Seconds" + ); + + // print some progress example. + for i in (1..5).rev() { + // print the current counter at the line of `Seconds to Go: {counter}` + cursor + .goto(48, 10) + .print(terminal.paint(format!("{}", i)).with(Color::Red).on(Color::Blue)); + + // 1 second delay + thread::sleep(time::Duration::from_secs(1)); + } +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/map.rs b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/map.rs new file mode 100644 index 0000000..b86d813 --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/map.rs @@ -0,0 +1,75 @@ +use super::variables::{Cell, Position, Size }; +use crossterm::terminal::terminal; +use crossterm::cursor::cursor; +use crossterm::Crossterm; +use crossterm::style::{ObjectStyle, StyledObject, Color}; +use crossterm::Context; +use std::rc::Rc; + +use std::fmt::Display; + +pub struct Map +{ + pub map: Vec>, + pub size: Size, +} + +impl Map +{ + pub fn new(map_size: Size, wall_cell_char: char, map_cell_char: char) -> Map + { + let mut map: Vec> = Vec::new(); + + // initialize the map shown on the screen. Each cell of terminal should have a value that could be changed by the algorithm + // create n rows with n cells. + for y in 0..map_size.height + { + let mut row: Vec = Vec::new(); + + for x in 0..map_size.width + { + if (y == 0 || y == map_size.height - 1) || (x == 0 || x == map_size.width - 1) + { + row.push(Cell::new(Position::new(x, y), Color::Black, wall_cell_char, true)); + } else { + row.push(Cell::new(Position::new(x, y), Color::Black, map_cell_char, false)); + } + } + map.push(row); + } + + Map { map: map, size: Size::new(map_size.width, map_size.height)} + } + + // render the map on the screen. + pub fn render_map(&mut self, crossterm: &mut Crossterm) + { + let mut cursor = crossterm.cursor(); + + for row in self.map.iter_mut() + { + for column in row.iter_mut() + { + // we only have to render the walls + if (column.position.y == 0 || column.position.y == self.size.height - 1) || (column.position.x == 0 || column.position.x == self.size.width - 1) + { + let cell_style = crossterm.paint(column.look).on(column.color); + cursor.goto(column.position.x as u16, column.position.y as u16) + .print(cell_style); + } + } + } + } + + // check if position in the map at the given coords is visted. + pub fn is_cell_visited(&self, x: usize, y: usize) -> bool + { + self.map[y][x].visited + } + + // change an position in the map to visited. + pub fn set_visited(&mut self, x: usize, y: usize) + { + self.map[y][x].visited = true; + } +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/messages.rs b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/messages.rs new file mode 100644 index 0000000..6ff44de --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/messages.rs @@ -0,0 +1,25 @@ +use super::variables::Position; + +pub const WELCOME_MESSAGE: [&str; 6] = +[ + "__ __ .__ __ ", + "/ \\ / \\ ____ | | | | ______ _____ ____ ", + "\\ \\/\\/ // __ \\| | | |/ / _ \\ / \\_/ __ \\ ", + " \\ /\\ ___/| |_| < <_> ) Y Y \\ ___/ ", + " \\__/\\ / \\___ >____/__|_ \\____/|__|_| /\\___ > ", + " \\/ \\/ \\/ \\/ \\/ " +]; + +pub const END_MESSAGE: [&str; 5] = +[ + "-----------------------", + " ", + " No routes (DONE) ", + " ", + "-----------------------", +]; + +pub fn print_stack_count(position: Position) +{ + +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/mod.rs b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/mod.rs new file mode 100644 index 0000000..53456b6 --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/mod.rs @@ -0,0 +1,4 @@ +mod map; +mod messages; +mod variables; +mod algorithm; \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/variables.rs b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/variables.rs new file mode 100644 index 0000000..e724a53 --- /dev/null +++ b/examples/Crossterm 0.3.1/program_examples/first_depth_search/src/variables.rs @@ -0,0 +1,63 @@ +extern crate crossterm; + +use self::crossterm::terminal::{terminal, ClearType}; +use self::crossterm::style::{Color, StyledObject, ObjectStyle }; +use self::crossterm::Context; + +use std::fmt::Debug; +use std::fmt; + +#[derive(Copy, Clone,Debug)] +pub enum Direction +{ + Up = 0, + Down = 1, + Left = 2, + Right = 3 +} + +#[derive(Copy, Clone, Debug)] +pub struct Position +{ + pub x: usize, + pub y: usize +} + +impl Position +{ + pub fn new(x: usize, y: usize) -> Position + { + Position { x, y } + } +} + +#[derive(Copy, Clone)] +pub struct Size +{ + pub width: usize, + pub height: usize +} + +impl Size +{ + pub fn new(width: usize, height: usize) -> Size + { + Size {width,height} + } +} + +pub struct Cell +{ + pub position: Position, + pub color: Color, + pub look: char, + pub visited: bool +} + +impl Cell +{ + pub fn new(position: Position, color: Color, look: char, visited: bool) -> Cell + { + Cell { position, color, look, visited } + } +} \ No newline at end of file diff --git a/examples/Crossterm 0.3.1/terminal/alternate_screen.rs b/examples/Crossterm 0.3.1/terminal/alternate_screen.rs new file mode 100644 index 0000000..d935be7 --- /dev/null +++ b/examples/Crossterm 0.3.1/terminal/alternate_screen.rs @@ -0,0 +1,81 @@ +extern crate crossterm; + +use crossterm::style::Color; +use crossterm::cursor::cursor; +use crossterm::screen::AlternateScreen; +use crossterm::terminal::{self, ClearType}; +use crossterm::Context; + +use std::io::{stdout, Write}; +use std::rc::Rc; +use std::{thread, time}; + +fn print_wait_screen(context: Rc) { + let mut terminal = terminal::terminal(&context); + terminal.clear(ClearType::All); + + let mut cursor = cursor(&context); + cursor.goto(0, 0); + cursor.hide(); + + terminal.write( + "Welcome to the wait screen.\n\ + Please wait a few seconds until we arrive back at the main screen.\n\ + Progress: ", + ); + + // print some progress example. + for i in 1..5 { + // print the current counter at the line of `Seconds to Go: {counter}` + cursor + .goto(10, 2) + .print(terminal.paint(format!("{} of the 5 items processed", i)).with(Color::Red).on(Color::Blue)); + + // 1 second delay + thread::sleep(time::Duration::from_secs(1)); + } + + stdout().flush(); +} + +/// print wait screen on alternate screen, then swich back. +pub fn print_wait_screen_on_alternate_window(context: Rc) { + // create scope. If this scope ends the screen will be switched back to mainscreen. + // because `AlternateScreen` switches back to main screen when switching back. + { + // create new alternate screen instance and switch to the alternate screen. + let mut screen = AlternateScreen::from(context.clone()); + + write!(screen, "test"); + println!(); + // Print the wait screen. + print_wait_screen(context.clone()); + } +} + +/// some stress test switch from and to alternate screen. +pub fn switch_between_main_and_alternate_screen() { + let context = Context::new(); + let mut cursor = cursor(&context); + + { + // create new alternate screen instance and switch to the alternate screen. + let mut screen = AlternateScreen::from(context.clone()); + cursor.goto(0, 0); + write!(screen, "we are at the alternate screen!"); + screen.flush(); + thread::sleep(time::Duration::from_secs(3)); + + screen.to_main(); + write!(screen, "we are at the main screen!"); + screen.flush(); + thread::sleep(time::Duration::from_secs(3)); + + screen.to_alternate(); + write!(screen, "we are at the alternate screen!"); + screen.flush(); + thread::sleep(time::Duration::from_secs(3)); + } + + println!("Whe are back at the main screen"); +} diff --git a/examples/Crossterm 0.3.1/terminal/mod.rs b/examples/Crossterm 0.3.1/terminal/mod.rs new file mode 100644 index 0000000..c6adf4b --- /dev/null +++ b/examples/Crossterm 0.3.1/terminal/mod.rs @@ -0,0 +1,8 @@ +/// Examples of actions that could be performed on the alternatescreen. +pub mod alternate_screen; + +/// Examples of actions that could be performed on the terminal. +pub mod terminal; + +// Raw screen +pub mod raw_mode; diff --git a/examples/Crossterm 0.3.1/terminal/raw_mode.rs b/examples/Crossterm 0.3.1/terminal/raw_mode.rs new file mode 100644 index 0000000..2209d70 --- /dev/null +++ b/examples/Crossterm 0.3.1/terminal/raw_mode.rs @@ -0,0 +1,56 @@ +extern crate crossterm; + +use crossterm::cursor::cursor; +use crossterm::screen::AlternateScreen; +use crossterm::terminal::{self, ClearType}; +use crossterm::Context; + +use std::io::{stdout, Write}; +use std::rc::Rc; +use std::{thread, time}; + +use crossterm::raw::IntoRawMode; + +// raw screen is not working correctly currently +fn print_wait_screen(context: Rc) { + terminal::terminal(&context).clear(ClearType::All); + + let mut cursor = cursor(&context); + cursor.goto(0, 0).print("Welcome to the wait screen."); + cursor + .goto(0, 1) + .print("Please wait a few seconds until we arrive back at the main screen."); + cursor.goto(0, 2).print("Progress: "); + + // print some progress example. + for i in 1..5 { + // print the current counter at the line of `Seconds to Go: {counter}` + cursor + .goto(10, 2) + .print(format!("{} of the 5 items processed", i)); + + // 1 second delay + thread::sleep(time::Duration::from_secs(1)); + } +} + +pub fn print_wait_screen_on_alternate_window() { + let context = Context::new(); + + // create scope. If this scope ends the screen will be switched back to mainscreen. + // because `AlternateScreen` switches back to main screen when going out of scope. + { + // create new alternate screen instance this call is also switching the screen to alternate screen. + // then convert the output of the program to raw mode. + // then print the wait screen on the alternate screen in raw mode. + let mut screen = AlternateScreen::from(context.clone()); + let raw_screen = screen.into_raw_mode(context.clone()); + + // Print the wait screen. + print_wait_screen(context.clone()); + + screen.flush(); + } + + println!("Whe are back at the main screen"); +} diff --git a/examples/Crossterm 0.3.1/terminal/terminal.rs b/examples/Crossterm 0.3.1/terminal/terminal.rs new file mode 100644 index 0000000..97afe3c --- /dev/null +++ b/examples/Crossterm 0.3.1/terminal/terminal.rs @@ -0,0 +1,160 @@ +//! +//! Terminal Examples +//! + +extern crate crossterm; + +use crossterm::cursor; +use crossterm::terminal::{terminal, ClearType}; +use crossterm::Context; + +fn print_test_data() { + for i in 0..100 { + println!("Test data to test terminal: {}", i); + } +} + +/// Clear all lines in terminal | demonstration +pub fn clear_all_lines() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + + print_test_data(); + + // Clear all lines in terminal; + terminal.clear(ClearType::All); +} + +/// Clear all lines from cursor position X:4, Y:4 down | demonstration +pub fn clear_from_cursor_down() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + + print_test_data(); + + // Set terminal cursor position (see example for more info). + cursor::cursor(&context).goto(4, 8); + + // Clear all cells from current cursor position down. + terminal.clear(ClearType::FromCursorDown); +} + +/// Clear all lines from cursor position X:4, Y:4 up | demonstration +pub fn clear_from_cursor_up() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + + print_test_data(); + + // Set terminal cursor position (see example for more info). + cursor::cursor(&context).goto(4, 4); + + // Clear all cells from current cursor position down. + terminal.clear(ClearType::FromCursorUp); +} + +/// Clear all lines from cursor position X:4, Y:4 up | demonstration +pub fn clear_current_line() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + + print_test_data(); + + // Set terminal cursor position (see example for more info). + cursor::cursor(&context).goto(4, 4); + + // Clear current line cells. + terminal.clear(ClearType::CurrentLine); +} + +/// Clear all lines from cursor position X:4, Y:7 up | demonstration +pub fn clear_until_new_line() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + + print_test_data(); + + // Set terminal cursor position (see example for more info). + cursor::cursor(&context).goto(4, 20); + + // Clear all the cells until next line. + terminal.clear(ClearType::UntilNewLine); +} + +/// Print the the current terminal size | demonstration. +pub fn print_terminal_size() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + // Get terminal size + let terminal_size = terminal.terminal_size(); + // Print results + print!("X: {}, y: {}", terminal_size.0, terminal_size.1); +} + +/// Set the terminal size to width 10, height: 10 | demonstration. +pub fn set_terminal_size() { + let context = Context::new(); + + let mut terminal = terminal(&context); + + terminal.set_size(10, 10); +} + +/// Scroll down 10 lines | demonstration. +pub fn scroll_down() { + let context = Context::new(); + + print_test_data(); + + + + // Get terminal + let mut terminal = terminal(&context); + // Scroll down 10 lines. + terminal.scroll_down(10); +} + +/// Scroll down 10 lines | demonstration. +pub fn scroll_up() { + let context = Context::new(); + + print_test_data(); + + // Get terminal + let mut terminal = terminal(&context); + // Scroll up 10 lines. + terminal.scroll_up(5); +} + +/// Resize the terminal to X: 10, Y: 10 | demonstration. +pub fn resize_terminal() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + + // Get terminal size + terminal.set_size(10, 10); +} + +/// exit the current proccess. +pub fn exit() { + let context = Context::new(); + + // Get terminal + let mut terminal = terminal(&context); + // Get terminal size + terminal.exit(); +} diff --git a/src/input/input.rs b/src/input/input.rs index 0838190..8f8ebf9 100644 --- a/src/input/input.rs +++ b/src/input/input.rs @@ -15,10 +15,10 @@ impl TerminalInput pub fn new(context: Rc) -> TerminalInput { #[cfg(target_os = "windows")] - let input = Box::from(WindowsInput::new()); + let input = Box::from(WindowsInput::new(context.clone())); #[cfg(not(target_os = "windows"))] - let cursor = Box::from(UnixInput::new()); + let input = Box::from(UnixInput::new()); TerminalInput { terminal_input: input, @@ -28,32 +28,27 @@ impl TerminalInput pub fn read_line(&self) -> io::Result { - let mut rv = String::new(); - io::stdin().read_line(&mut rv)?; - let len = rv.trim_right_matches(&['\r', '\n'][..]).len(); - rv.truncate(len); - Ok(rv) + self.terminal_input.read_line() } - fn read_char(&self) -> io::Result + pub fn read_char(&self) -> io::Result { - // todo: read char - Ok(String::new()) + return self.terminal_input.read_char() } - fn read_key(&self) -> io::Result<()> + pub fn read_key(&self) -> io::Result { - // todo: read pressed key - Ok(()) + self.terminal_input.read_pressed_key() } - fn read_async(&self) + pub fn read_async(&self) -> AsyncReader { + self.terminal_input.read_async() // todo: async reading } - fn read_until(&self, delimiter: u8) - { // todo: read until char + pub fn read_until_async(&self, delimiter: u8) -> AsyncReader + { self.terminal_input.read_until_async(delimiter) } } diff --git a/src/input/mod.rs b/src/input/mod.rs index ac0cc7f..6ac4f56 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2,21 +2,75 @@ use std::io; pub mod input; +#[cfg(target_os = "windows")] +use self::windows_input::WindowsInput; #[cfg(target_os = "windows")] mod windows_input; -mod unix_input; #[cfg(target_os = "windows")] -use self::windows_input::WindowsInput; use self::unix_input::UnixInput; +mod unix_input; + pub use self::input::{ input, TerminalInput }; +use std::io::Read; +use std::sync::mpsc; + trait ITerminalInput { - fn read_char(&self) -> io::Result; - fn read_key(&self) -> io::Result<()>; + fn read_line(&self) -> io::Result; - fn read_async(&self); - fn read_until(&self, delimiter: u8); + fn read_char(&self) -> io::Result; + fn read_pressed_key(&self) -> io::Result; + + fn read_async(&self) -> AsyncReader; + fn read_until_async(&self, delimiter: u8) -> AsyncReader; +} + +pub struct AsyncReader +{ + recv: mpsc::Receiver> +} + +impl Read for AsyncReader { + /// Read from the byte stream. + /// + /// This will never block, but try to drain the event queue until empty. If the total number of + /// bytes written is lower than the buffer's length, the event queue is empty or that the event + /// stream halted. + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut total = 0; + + loop { + if total >= buf.len() { + break; + } + + match self.recv.try_recv() { + Ok(Ok(b)) => { + buf[total] = b; + total += 1; + } + Ok(Err(e)) => return Err(e), + Err(_) => break, + } + } + + Ok(total) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Key { + Unknown, + ArrowLeft, + ArrowRight, + ArrowUp, + ArrowDown, + Enter, + Escape, + Char(char), + #[doc(hidden)] + __More, } \ No newline at end of file diff --git a/src/input/unix_input.rs b/src/input/unix_input.rs index 2a7c40c..296bec5 100644 --- a/src/input/unix_input.rs +++ b/src/input/unix_input.rs @@ -1,3 +1,13 @@ +use std::io; +use std::io::Write; +use std::char; +use std::sync::mpsc; +use std::thread; + +use super::super::terminal::terminal; +//use super::super::kernel::unix_kernel::terminal::get_tty; +use super::{ Key, ITerminalInput, AsyncReader }; + pub struct UnixInput; impl UnixInput @@ -6,4 +16,111 @@ impl UnixInput { UnixInput {} } -} \ No newline at end of file +} +// fn read_line(&self) -> io::Result +// { +// let mut rv = String::new(); +// io::stdin().read_line(&mut rv)?; +// let len = rv.trim_right_matches(&['\r', '\n'][..]).len(); +// rv.truncate(len); +// Ok(rv) +// } +// +// fn read_char(&self) -> io::Result +// { +// let mut buf = [0u8; 20]; +// let mut termios = termios::Termios::from_fd(fd)?; +// let original = termios.clone(); +// termios::cfmakeraw(&mut termios); +// termios::tcsetattr(fd, termios::TCSADRAIN, &termios)?; +// let rv = unsafe { +// let read = libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 20); +// if read < 0 { +// Err(io::Error::last_os_error()) +// } else if buf[0] == b'\x03' { +// Err(io::Error::new(io::ErrorKind::Interrupted, "read interrupted")) +// } else { +// Ok(key_from_escape_codes(&buf[..read as usize])) +// } +// }; +// termios::tcsetattr(fd, termios::TCSADRAIN, &original)?; +// +// // if the user hit ^C we want to signal SIGINT to outselves. +// if let Err(ref err) = rv { +// if err.kind() == io::ErrorKind::Interrupted { +// unsafe { libc::raise(libc::SIGINT); } +// } +// } +// +// rv +// } +// +// fn read_pressed_key(&self) -> io::Result +// { +// use Context; +// let context = Context::new(); +// +// let buf: [u8; 1024] = unsafe { ::std::mem::zeroed() }; +//// reading::read(&mut buf, &context.screen_manager); +// +// Ok(Key::Unknown) +//// let pressed_char = unsafe { _getwch() }; +//// +//// // if 0 or 0xe0 we need to listen again because the next key will be an special key +//// if pressed_char == 0 || pressed_char == 0xe0 { +//// let special_key: i32 = unsafe { _getwch() }; +//// println!("spkey {}",special_key); +//// return Ok(key_from_key_code(0x26)); +//// } else { +//// match char::from_u32(pressed_char as u32) +//// { +//// Some(c) => return Ok(Key::Char(c)), +//// None => { panic!("Some error needs to be returned") } +//// } +//// } +// } +// +// fn read_async(&self) -> AsyncReader +// { +// let (send, recv) = mpsc::channel(); +// +// thread::spawn(move || for i in get_tty().unwrap().bytes() { +// +// match i { +// Ok(byte) => { +// let end_of_stream = &byte == &delimiter; +// let send_error = send.send(Ok(byte)).is_err(); +// +// if end_of_stream || send_error { return; } +// }, +// Err(_) => { return; } +// } +// }); +// +// AsyncReader { recv: recv } +// } +// +// fn read_until_async(&self, delimiter: u8) -> AsyncReader +// { +// let (tx, rx) = mpsc::channel(); +// +// thread::spawn(move || { +// loop +// { +// let pressed_char: u8 = (unsafe { _getwch() }) as u8; +// +// let end_of_stream = (pressed_char == delimiter); +// +// // we could return error but maybe option to keep listening until valid character is inputted. +// if pressed_char == 0 || pressed_char == 0xe0 || end_of_stream { +// return; +// } +// +// tx.send(Ok(pressed_char as u8)); +// } +// }); +// +// AsyncReader { recv: rx } +// } +//} + diff --git a/src/input/windows_input.rs b/src/input/windows_input.rs index 1904c62..ebbf6ed 100644 --- a/src/input/windows_input.rs +++ b/src/input/windows_input.rs @@ -1,40 +1,228 @@ use std::io; use std::io::Write; +use std::char; +use std::sync::mpsc; +use std::thread; -use super::*; +use super::{ Key, ITerminalInput, AsyncReader }; + +use winapi::um::winnt::{ INT }; +use winapi::um::winuser; + +use super::super::terminal::terminal; +use super::super::kernel::windows_kernel::reading; +use Context; +use std::rc::Rc; pub struct WindowsInput -{ } +{ + context: Rc, + pub display_input: bool, +} impl WindowsInput { - pub fn new() -> WindowsInput + pub fn new(context: Rc) -> WindowsInput { - WindowsInput {} + WindowsInput { context, display_input: false } } } impl ITerminalInput for WindowsInput { - fn read_char(&self) -> io::Result + fn read_line(&self) -> io::Result { - let mut rv = String::new(); - Ok(rv) + let term = terminal(&self.context); + let mut chars: Vec = Vec::new(); + + loop { + let pressed_char = unsafe { _getwch() }; + + // if 0 or 0xe0 we need to listen again because the next key will be an special key + if pressed_char != 0 || pressed_char != 0xe0 { + match char::from_u32(pressed_char as u32) + { + Some(c) => { + if is_line_end(c) { break; } + else { chars.push(c); } + + if self.display_input + { + term.write(c); + } + + }, + None => { panic!("Some error needs to be returned") } + }; + } + } + + return Ok(chars.into_iter().collect()); } - fn read_key(&self) -> io::Result<()> + fn read_char(&self) -> io::Result { - let mut rv = String::new(); - Ok(()) + let term = terminal(&self.context); + + let pressed_char = unsafe { _getwch() }; + + // we could return error but maybe option to keep listening until valid character is inputted. + if pressed_char == 0 || pressed_char == 0xe0 { + return Err(io::Error::new(io::ErrorKind::Other, "Given input char is not a valid char, mostly occurs when pressing special keys")); + } + + match char::from_u32(pressed_char as u32) + { + Some(c) => { + if self.display_input + { + term.write(c); + } + return Ok(c); + } + None => Err(io::Error::new(io::ErrorKind::Other, "Could not parse given input to char")) + } } - fn read_async(&self) + fn read_pressed_key(&self) -> io::Result { + use Context; + let context = Context::new(); + let buf: [u8; 1024] = unsafe { ::std::mem::zeroed() }; +// reading::read(&mut buf, &context.screen_manager); + + Ok(Key::Unknown) +// let pressed_char = unsafe { _getwch() }; +// +// // if 0 or 0xe0 we need to listen again because the next key will be an special key +// if pressed_char == 0 || pressed_char == 0xe0 { +// let special_key: i32 = unsafe { _getwch() }; +// println!("spkey {}",special_key); +// return Ok(key_from_key_code(0x26)); +// } else { +// match char::from_u32(pressed_char as u32) +// { +// Some(c) => return Ok(Key::Char(c)), +// None => { panic!("Some error needs to be returned") } +// } +// } } - fn read_until(&self, delimiter: u8) + fn read_async(&self) -> AsyncReader { + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + loop + { + let pressed_char: u8 = (unsafe { _getwch() }) as u8; + + // we could return error but maybe option to keep listening until valid character is inputted. + if pressed_char == 0 || pressed_char == 0xe0 { + return; + } + + tx.send(Ok(pressed_char as u8)); + + if pressed_char == 13 + { + return; + } + } + }); + + AsyncReader { recv: rx } } + + fn read_until_async(&self, delimiter: u8) -> AsyncReader + { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + loop + { + let pressed_char: u8 = (unsafe { _getwch() }) as u8; + + let end_of_stream = (pressed_char == delimiter); + + // we could return error but maybe option to keep listening until valid character is inputted. + if pressed_char == 0 || pressed_char == 0xe0 || end_of_stream { + return; + } + + tx.send(Ok(pressed_char as u8)); + } + }); + + AsyncReader { recv: rx } + } +} + +fn is_line_end(key: char) -> bool +{ + if key as u8 == 13 + { + return true; + } + + return false; +} + + +//0 59 = F1 +//0 60 = F2 +//0 61 = F3 +//0 62 = F4 +//0 63 = F5 +//0 64 = F6 +//0 65 = F7 +//0 66 = F8 +//0 67 = F9 +//0 68 = F10 +//224 71 = Home +//224 72 = ↑ (up arrow) +//224 73 = Page Up +//224 75 = ← (left arrow) +//224 77 = → (right arrow) +//224 79 = End +//224 80 = ↓ (down arrow) +//224 81 = Page Down +//224 82 = Insert +//224 83 = Delete +//224 133 = F11 +//224 134 = F12 + + +fn key_from_key_code(code: INT) -> Key { + + println!("code: {}", code); + println!("up winapi: {}", winuser::VK_UP); + + match code { +// 59 => Key::F1, +// 60 => Key::F2, +// 61 => Key::F3, +// 62 => Key::F4, +// 63 => Key::F5, +// 64 => Key::F6, +// 65 => Key::F7, +// 66 => Key::F8, +// 67 => Key::F9, +// 68 => Key::F10, + winuser::VK_LEFT => Key::ArrowLeft, + winuser::VK_RIGHT => Key::ArrowRight, + winuser::VK_UP => Key::ArrowUp, + winuser::VK_DOWN => Key::ArrowDown, + winuser::VK_RETURN => Key::Enter, + winuser::VK_ESCAPE => Key::Escape, + winuser::VK_BACK => Key::Char('\x08'), + winuser::VK_TAB => Key::Char('\x09'), + _ => Key::Unknown, + } +} + +extern "C" { + fn _getwch() -> INT; + fn _getwch_nolock() -> INT; } \ No newline at end of file diff --git a/src/kernel/unix_kernel/terminal.rs b/src/kernel/unix_kernel/terminal.rs index 51ff257..5b1aa8e 100644 --- a/src/kernel/unix_kernel/terminal.rs +++ b/src/kernel/unix_kernel/terminal.rs @@ -8,7 +8,7 @@ use {libc, CommandManager, Context, StateManager}; use std::io::Error; use std::rc::Rc; -use std::{io, mem}; +use std::{io, mem, fs}; /// A representation of the size of the current terminal. #[repr(C)] @@ -133,6 +133,13 @@ pub fn get_terminal_mode() -> io::Result { } } +/// Get the TTY device. +/// +/// This allows for getting stdio representing _only_ the TTY, and not other streams. +pub fn get_tty() -> io::Result { + fs::OpenOptions::new().read(true).write(true).open("/dev/tty") +} + pub fn exit() { ::std::process::exit(0); } diff --git a/src/kernel/windows_kernel/kernel.rs b/src/kernel/windows_kernel/kernel.rs index ae411f5..24ffa58 100644 --- a/src/kernel/windows_kernel/kernel.rs +++ b/src/kernel/windows_kernel/kernel.rs @@ -258,54 +258,7 @@ pub fn set_console_screen_buffer_size( } } -/// Fill a certain block with characters. -pub fn fill_console_output_character( - cells_written: &mut u32, - start_location: COORD, - cells_to_write: u32, - screen_manager: &Rc>, -) -> bool { - let handle = get_current_handle(screen_manager); - - unsafe { - // fill the cells in console with blanks - let success = FillConsoleOutputCharacterA( - handle, - ' ' as i8, - cells_to_write, - start_location, - cells_written, - ); - is_true(success) - } -} - -/// Set console ouput attribute for certain block. -pub fn fill_console_output_attribute( - cells_written: &mut u32, - start_location: COORD, - cells_to_write: u32, - screen_manager: &Rc>, -) -> bool { - // Get the position of the current console window - - let (csbi, mut handle) = get_buffer_info_and_hande(screen_manager); - - let success; - - unsafe { - success = FillConsoleOutputAttribute( - handle, - csbi.wAttributes, - cells_to_write, - start_location, - cells_written, - ); - } - - is_true(success) -} /// Create new console screen buffer. This can be used for alternate screen. pub fn create_console_screen_buffer() -> HANDLE { @@ -343,104 +296,9 @@ pub fn set_active_screen_buffer(new_buffer: HANDLE) { } } -/// Read the console outptut. -pub fn read_console_output( - read_buffer: &HANDLE, - copy_buffer: &mut [CHAR_INFO; 160], - buffer_size: COORD, - buffer_coord: COORD, - source_buffer: PSMALL_RECT, -) { - use self::wincon::ReadConsoleOutputA; - - unsafe { - if !is_true( - ReadConsoleOutputA( - *read_buffer, // screen buffer to read from - copy_buffer.as_mut_ptr(), // buffer to copy into - buffer_size, // col-row size of chiBuffer - buffer_coord, // top left dest. cell in chiBuffer - source_buffer, - ), // screen buffer source rectangle - ) { - panic!("Cannot read console output"); - } - } -} - -/// Write console output. -pub fn write_console_output( - write_buffer: &HANDLE, - copy_buffer: &mut [CHAR_INFO; 160], - buffer_size: COORD, - buffer_coord: COORD, - source_buffer: PSMALL_RECT, -) { - use self::wincon::WriteConsoleOutputA; - - unsafe { - if !is_true( - WriteConsoleOutputA( - *write_buffer, // screen buffer to write to - copy_buffer.as_mut_ptr(), // buffer to copy into - buffer_size, // col-row size of chiBuffer - buffer_coord, // top left dest. cell in chiBuffer - source_buffer, - ), // screen buffer source rectangle - ) { - panic!("Cannot write to console output"); - } - } -} - -//use std::os::raw::c_void; -use std::str; -use winapi::ctypes::c_void; - -/// Write utf8 buffer to console. -pub fn write_char_buffer(handle: &HANDLE, buf: &[u8]) -> ::std::io::Result { - // get string from u8[] and parse it to an c_str - let mut utf8 = match str::from_utf8(buf) { - Ok(string) => string, - Err(_) => "123", - }; - - let utf16: Vec = utf8.encode_utf16().collect(); - let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void; - - // get buffer info - let csbi = get_console_screen_buffer_info_from_handle(handle); - - // get current position - let current_pos = COORD { - X: csbi.dwCursorPosition.X, - Y: csbi.dwCursorPosition.Y, - }; - - let mut cells_written: u32 = 0; - - let mut success = false; - // write to console - unsafe { - success = is_true(WriteConsoleW( - *handle, - utf16_ptr, - utf16.len() as u32, - &mut cells_written, - NULL, - )); - } - - match success - { - // think this is wrong could be done better! - true => Ok(utf8.as_bytes().len()), - false => Ok(0) - } -} /// Parse integer to an bool -fn is_true(value: i32) -> bool { +pub fn is_true(value: i32) -> bool { if value == 0 { return false; } else { diff --git a/src/kernel/windows_kernel/mod.rs b/src/kernel/windows_kernel/mod.rs index 086fc3a..32a22d4 100644 --- a/src/kernel/windows_kernel/mod.rs +++ b/src/kernel/windows_kernel/mod.rs @@ -4,6 +4,8 @@ pub mod ansi_support; pub mod cursor; pub mod kernel; pub mod terminal; +pub mod writing; +pub mod reading; use self::winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT}; use shared::traits::Empty; diff --git a/src/kernel/windows_kernel/reading.rs b/src/kernel/windows_kernel/reading.rs new file mode 100644 index 0000000..1780b6f --- /dev/null +++ b/src/kernel/windows_kernel/reading.rs @@ -0,0 +1,84 @@ +use { Context, ScreenManager }; +use std::rc::Rc; +use std::sync::Mutex; + +use winapi::um::consoleapi::ReadConsoleW; +use winapi::um::winnt::HANDLE; +use winapi::um::wincon::{ COORD, PSMALL_RECT, ReadConsoleOutputA, CHAR_INFO, }; +use winapi::shared::minwindef::{ DWORD, LPDWORD, LPVOID }; +use winapi::shared::ntdef::NULL; + +use super::kernel; +use winapi::ctypes::c_void; + +pub fn read(buf: &mut [u8], screen_manager: &Rc>) { +// // Read more if the buffer is empty +// let mut utf16: Vec = Vec::new(); +// let mut num: DWORD = 0; +// +// let handle = kernel::get_current_handle(&screen_manager); +// +// unsafe { +// ReadConsoleW(handle, +// utf16.as_mut_ptr() as LPVOID, +// utf16.len() as u32, +// &mut num as LPDWORD, +// ptr::mut_null()) +// }; +// +// utf16.truncate(num as uint); +// let utf8 = match from_utf16(utf16.as_slice()) { +// Some(utf8) => utf8.into_bytes(), +// None => {} +// }; +// +// panic!(utf8); + +} + +pub fn read_line(screen_manager: &Rc>) -> ::std::io::Result +{ + const BUFFER_LENGHT: u32 = 1024; + let mut buffer: &mut [CHAR_INFO; BUFFER_LENGHT as usize] = unsafe {::std::mem::zeroed() }; + + let handle = kernel::get_current_handle(&screen_manager); + + let mut dw_mode: DWORD = 0; + let console_mode = kernel::get_console_mode(&handle, &mut dw_mode); + + let ptr = buffer.as_ptr() as *const _ as *mut c_void; + let mut chars_read: u32 = 0; + + panic!(); + unsafe + { + ReadConsoleW(handle, ptr, BUFFER_LENGHT , &mut chars_read, unsafe {::std::mem::zeroed() }); + } + + Ok(String::new()) +} + +/// Read the console outptut. +pub fn read_console_output( + read_buffer: &HANDLE, + copy_buffer: &mut [CHAR_INFO; 160], + buffer_size: COORD, + buffer_coord: COORD, + source_buffer: PSMALL_RECT, +) { + + + unsafe { + if !kernel::is_true( + ReadConsoleOutputA( + *read_buffer, // screen buffer to read from + copy_buffer.as_mut_ptr(), // buffer to copy into + buffer_size, // col-row size of chiBuffer + buffer_coord, // top left dest. cell in chiBuffer + source_buffer, + ), // screen buffer source rectangle + ) { + panic!("Cannot read console output"); + } + } +} diff --git a/src/kernel/windows_kernel/writing.rs b/src/kernel/windows_kernel/writing.rs new file mode 100644 index 0000000..5b1e927 --- /dev/null +++ b/src/kernel/windows_kernel/writing.rs @@ -0,0 +1,130 @@ +use { Context, ScreenManager }; +use std::rc::Rc; +use std::sync::Mutex; + +use winapi::um::wincon; +use winapi::um::winnt::HANDLE; +use winapi::um::consoleapi::WriteConsoleW; +use winapi::um::wincon::{WriteConsoleOutputA, PSMALL_RECT, FillConsoleOutputAttribute, FillConsoleOutputCharacterA, COORD, CHAR_INFO}; +use winapi::shared::ntdef::NULL; + +use super::kernel; + +/// Fill a certain block with characters. +pub fn fill_console_output_character( + cells_written: &mut u32, + start_location: COORD, + cells_to_write: u32, + screen_manager: &Rc>, +) -> bool { + + let handle = kernel::get_current_handle(screen_manager); + + unsafe { + // fill the cells in console with blanks + let success = FillConsoleOutputCharacterA( + handle, + ' ' as i8, + cells_to_write, + start_location, + cells_written, + ); + kernel::is_true(success) + } +} + +/// Set console ouput attribute for certain block. +pub fn fill_console_output_attribute( + cells_written: &mut u32, + start_location: COORD, + cells_to_write: u32, + screen_manager: &Rc>, +) -> bool { + // Get the position of the current console window + + let (csbi, mut handle) = kernel::get_buffer_info_and_hande(screen_manager); + + let success; + + unsafe { + success = FillConsoleOutputAttribute( + handle, + csbi.wAttributes, + cells_to_write, + start_location, + cells_written, + ); + } + + kernel::is_true(success) +} + +/// Write console output. +pub fn write_console_output( + write_buffer: &HANDLE, + copy_buffer: &mut [CHAR_INFO; 160], + buffer_size: COORD, + buffer_coord: COORD, + source_buffer: PSMALL_RECT, +) { + use self::wincon::WriteConsoleOutputA; + + unsafe { + if !kernel::is_true( + WriteConsoleOutputA( + *write_buffer, // screen buffer to write to + copy_buffer.as_mut_ptr(), // buffer to copy into + buffer_size, // col-row size of chiBuffer + buffer_coord, // top left dest. cell in chiBuffer + source_buffer, + ), // screen buffer source rectangle + ) { + panic!("Cannot write to console output"); + } + } +} + +use winapi::ctypes::c_void; +use std::str; + +/// Write utf8 buffer to console. +pub fn write_char_buffer(handle: &HANDLE, buf: &[u8]) -> ::std::io::Result { + // get string from u8[] and parse it to an c_str + let mut utf8 = match str::from_utf8(buf) { + Ok(string) => string, + Err(_) => "123", + }; + + let utf16: Vec = utf8.encode_utf16().collect(); + let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void; + + // get buffer info + let csbi = kernel::get_console_screen_buffer_info_from_handle(handle); + + // get current position + let current_pos = COORD { + X: csbi.dwCursorPosition.X, + Y: csbi.dwCursorPosition.Y, + }; + + let mut cells_written: u32 = 0; + + let mut success = false; + // write to console + unsafe { + success = kernel::is_true(WriteConsoleW( + *handle, + utf16_ptr, + utf16.len() as u32, + &mut cells_written, + NULL, + )); + } + + match success + { + // think this is wrong could be done better! + true => Ok(utf8.as_bytes().len()), + false => Ok(0) + } +} \ No newline at end of file diff --git a/src/manager/ansi_manager.rs b/src/manager/ansi_manager.rs index 4d32578..52fe5f2 100644 --- a/src/manager/ansi_manager.rs +++ b/src/manager/ansi_manager.rs @@ -3,13 +3,14 @@ //! This module uses the stdout to write to the console. use std::any::Any; -use std::io::{self, Write}; +use std::io::{self, Write, Read }; use super::IScreenManager; pub struct AnsiScreenManager { pub is_alternate_screen: bool, output: Box, + input: Box } impl IScreenManager for AnsiScreenManager { @@ -29,6 +30,20 @@ impl IScreenManager for AnsiScreenManager { Ok(0) } +// fn read_line(&mut self) -> io::Result +// { +// let mut rv = String::new(); +// self.input.read_line(&mut rv)?; +// let len = rv.trim_right_matches(&['\r', '\n'][..]).len(); +// rv.truncate(len); +// Ok(rv) +// } +// +// fn read_char(&mut self) -> io::Result +// { +// +// } + fn write(&mut self, buf: &[u8]) -> io::Result { self.output.write(buf) } @@ -45,6 +60,7 @@ impl IScreenManager for AnsiScreenManager { impl AnsiScreenManager { pub fn new() -> Self { AnsiScreenManager { + input: (Box::from(io::stdin()) as Box), output: (Box::from(io::stdout()) as Box), is_alternate_screen: false, } diff --git a/src/manager/win_manager.rs b/src/manager/win_manager.rs index 96f7c5c..349de66 100644 --- a/src/manager/win_manager.rs +++ b/src/manager/win_manager.rs @@ -1,5 +1,7 @@ use super::IScreenManager; + use kernel::windows_kernel::kernel; +use kernel::windows_kernel::writing; use winapi::um::wincon::ENABLE_PROCESSED_OUTPUT; use winapi::um::winnt::HANDLE; @@ -30,9 +32,9 @@ impl IScreenManager for WinApiScreenManager { fn write(&mut self, buf: &[u8]) -> io::Result { if self.is_alternate_screen { - kernel::write_char_buffer(&self.alternate_handle, buf) + writing::write_char_buffer(&self.alternate_handle, buf) } else { - kernel::write_char_buffer(&self.output, buf) + writing::write_char_buffer(&self.output, buf) } } diff --git a/src/state/context.rs b/src/state/context.rs index 8957d7f..5d4c0fc 100644 --- a/src/state/context.rs +++ b/src/state/context.rs @@ -50,14 +50,14 @@ use {ScreenManager, StateManager}; use std::rc::Rc; use std::sync::Mutex; - +use std::marker::Sync; /// This type is the context of the current terminal. The context is a wrapper for states changes of the terminal and can be used for managing the output of the terminal. pub struct Context { pub screen_manager: Rc>, pub state_manager: Mutex, } -impl Context { +impl Context{ /// Create new Context instance so that you can provide it to other modules like terminal, cursor and color /// /// This context type is just an wrapper that crossterm uses for managing the state the terminal. diff --git a/src/terminal/winapi_terminal.rs b/src/terminal/winapi_terminal.rs index c49d811..57f0207 100644 --- a/src/terminal/winapi_terminal.rs +++ b/src/terminal/winapi_terminal.rs @@ -5,7 +5,7 @@ use super::super::shared::functions; use super::super::ScreenManager; use super::{ClearType, ITerminal, Rc}; use cursor::cursor; -use kernel::windows_kernel::{kernel, terminal}; +use kernel::windows_kernel::{kernel, terminal, writing}; use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT}; use Context; @@ -276,7 +276,7 @@ fn clear(start_loaction: COORD, cells_to_write: u32, screen_manager: &Rc