diff --git a/Cargo.lock b/Cargo.lock index ce0b4d0056ed5ab3ceadc477f63fa2dc6728fa59..e3d3a4409e4fbd07382d1ddb4bed662c0bc87e65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,9 +1,9 @@ [root] name = "orbutils" -version = "0.1.4" +version = "0.1.5" dependencies = [ - "orbclient 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "orbtk 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "orbclient 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "orbtk 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -32,7 +32,7 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "sdl2 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -40,10 +40,10 @@ dependencies = [ [[package]] name = "orbtk" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "orbclient 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "orbclient 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 54e12f6d2523895f0ce756bf1cff086f85abb8be..9da4efb95a13dcbabe06f98035c3013bba1bbbf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "orbutils" description = "The Orbital Utilities" repository = "https://github.com/redox-os/orbutils" -version = "0.1.5" +version = "0.1.6" license-file = "LICENSE" readme = "README.md" authors = ["Jeremy Soller <jackpot51@gmail.com>"] diff --git a/src/launcher/main.rs b/src/launcher/main.rs index e030c13209a25b512c9b8e49786b7b9a92fcf18c..c476d2ac0f6614f9319a77148742bf3a783f1314 100644 --- a/src/launcher/main.rs +++ b/src/launcher/main.rs @@ -174,7 +174,7 @@ fn main() { println!("launcher: failed to read shutdown icon"); } - let mut window = Window::new(0, 600 - 48, 800, 48, "").unwrap(); + let mut window = Window::new(0, 768 - 48, 1024, 48, "").unwrap(); draw(&mut window, &packages, &shutdown, -1, -1); 'running: loop { diff --git a/src/terminal/console.rs b/src/terminal/console.rs new file mode 100644 index 0000000000000000000000000000000000000000..5de32e47302feef3566efc9a8f14412476ed1bfb --- /dev/null +++ b/src/terminal/console.rs @@ -0,0 +1,223 @@ +use std::mem; + +use orbclient::{Color, Event, EventOption, Window}; +use orbclient::event; + +const BLACK: Color = Color::rgb(0, 0, 0); +const RED: Color = Color::rgb(194, 54, 33); +const GREEN: Color = Color::rgb(37, 188, 36); +const YELLOW: Color = Color::rgb(173, 173, 39); +const BLUE: Color = Color::rgb(73, 46, 225); +const MAGENTA: Color = Color::rgb(211, 56, 211); +const CYAN: Color = Color::rgb(51, 187, 200); +const WHITE: Color = Color::rgb(203, 204, 205); + +pub struct Console { + pub window: Box<Window>, + pub point_x: i32, + pub point_y: i32, + pub foreground: Color, + pub background: Color, + pub redraw: bool, + pub command: String, + pub escape: bool, + pub escape_sequence: bool, + pub sequence: Vec<String>, +} + +impl Console { + pub fn new() -> Console { + let mut window = Window::new(-1, -1, 640, 480, "Terminal").unwrap(); + window.sync(); + Console { + window: window, + point_x: 0, + point_y: 0, + foreground: WHITE, + background: BLACK, + redraw: true, + command: String::new(), + escape: false, + escape_sequence: false, + sequence: Vec::new(), + } + } + + pub fn code(&mut self, c: char) { + if self.escape_sequence { + if c >= '0' && c <= '9' { + // Add a number to the sequence list + if let Some(mut value) = self.sequence.last_mut() { + value.push(c); + } + } else if c == ';' { + // Split sequence into list + self.sequence.push(String::new()); + } else if c == 'm' { + // Display attributes + for value in self.sequence.iter() { + if value == "0" { + // Reset all + self.foreground = WHITE; + self.background = BLACK; + } else if value == "30" { + self.foreground = BLACK; + } else if value == "31" { + self.foreground = RED; + } else if value == "32" { + self.foreground = GREEN; + } else if value == "33" { + self.foreground = YELLOW; + } else if value == "34" { + self.foreground = BLUE; + } else if value == "35" { + self.foreground = MAGENTA; + } else if value == "36" { + self.foreground = CYAN; + } else if value == "37" { + self.foreground = WHITE; + } else if value == "40" { + self.background = BLACK; + } else if value == "41" { + self.background = RED; + } else if value == "42" { + self.background = GREEN; + } else if value == "43" { + self.background = YELLOW; + } else if value == "44" { + self.background = BLUE; + } else if value == "45" { + self.background = MAGENTA; + } else if value == "46" { + self.background = CYAN; + } else if value == "47" { + self.background = WHITE; + } + } + + self.escape_sequence = false; + } else { + self.escape_sequence = false; + } + + if !self.escape_sequence { + self.sequence.clear(); + self.escape = false; + } + } else if c == '[' { + // Control sequence initiator + + self.escape_sequence = true; + self.sequence.push(String::new()); + } else if c == 'c' { + // Reset + self.point_x = 0; + self.point_y = 0; + self.foreground = WHITE; + self.background = BLACK; + self.window.set(self.background); + self.redraw = true; + + self.escape = false; + } else { + // Unknown escape character + + self.escape = false; + } + } + + pub fn scroll(&mut self, rows: usize) { + if rows > 0 && rows < self.window.height() as usize { + let offset = rows * self.window.width() as usize; + let data = self.window.data_mut(); + for i in 0..data.len() - offset { + let color = data[i + offset]; + data[i] = color; + } + for i in data.len() - offset..data.len() { + data[i] = self.background; + } + } + } + + pub fn character(&mut self, c: char) { + self.window.rect(self.point_x, self.point_y, 8, 16, self.background); + if c == '\x00' { + // Ignore null character + } else if c == '\x1B' { + self.escape = true; + } else if c == '\n' { + self.point_x = 0; + self.point_y += 16; + } else if c == '\t' { + self.point_x = ((self.point_x / 64) + 1) * 64; + } else if c == '\x08' { + self.point_x -= 8; + if self.point_x < 0 { + self.point_x = 0 + } + self.window.rect(self.point_x, self.point_y, 8, 16, self.background); + } else { + self.window.char(self.point_x, self.point_y, c, self.foreground); + self.point_x += 8; + } + if self.point_x >= self.window.width() as i32 { + self.point_x = 0; + self.point_y += 16; + } + while self.point_y + 16 > self.window.height() as i32 { + self.scroll(16); + self.point_y -= 16; + } + self.window.rect(self.point_x, self.point_y, 8, 16, self.foreground); + self.redraw = true; + } + + pub fn event(&mut self, event: Event) -> Option<String> { + match event.to_option() { + EventOption::Key(key_event) => { + if key_event.pressed { + match key_event.scancode { + event::K_BKSP => if ! self.command.is_empty() { + self.write(&[8]); + self.command.pop(); + }, + _ => match key_event.character { + '\0' => (), + c => { + self.command.push(c); + self.write(&[c as u8]); + + if c == '\n' { + let mut command = String::new(); + mem::swap(&mut self.command, &mut command); + return Some(command); + } + } + }, + } + } + }, + _ => (), + } + + None + } + + pub fn write(&mut self, bytes: &[u8]) { + for byte in bytes.iter() { + let c = *byte as char; + + if self.escape { + self.code(c); + } else { + self.character(c); + } + } + + if self.redraw { + self.redraw = false; + self.window.sync(); + } + } +} diff --git a/src/terminal/main.rs b/src/terminal/main.rs index 85ddec6637c8707b50bd040b8a8a58b98380116d..16e15ccefbc3a898c6900b36e84a47690aadae70 100644 --- a/src/terminal/main.rs +++ b/src/terminal/main.rs @@ -1,6 +1,8 @@ +#![feature(const_fn)] + extern crate orbclient; -use orbclient::Color; +use orbclient::event; use std::env; use std::io::{self, Read, Write}; @@ -8,9 +10,9 @@ use std::process::{Command, Stdio}; use std::sync::{Arc, Mutex}; use std::thread; -use window::{ConsoleEvent, ConsoleWindow}; +use console::Console; -mod window; +mod console; fn main() { let shell = env::args().nth(1).unwrap_or("sh".to_string()); @@ -76,15 +78,14 @@ fn main() { } let mut stdin = process.stdin.unwrap(); - let mut window = ConsoleWindow::new(-1, -1, 576, 400, "Terminal"); + let mut console = Console::new(); 'events: loop { match output_mutex.lock() { Ok(mut output) => { - let mut string = String::new(); - for byte in output.drain(..) { - string.push(byte as char); + if ! output.is_empty() { + console.write(&output); + output.clear(); } - window.print(&string, Color::rgb(255, 255, 255)); }, Err(_) => { println!("failed to lock print output mutex"); @@ -92,10 +93,12 @@ fn main() { } } - for console_event in window.read() { - match console_event { - ConsoleEvent::Line(mut line) => { - line.push('\n'); + for event in console.window.events_no_wait() { + if event.code == event::EVENT_QUIT { + break 'events; + } + match console.event(event) { + Some(line) => { match stdin.write(&line.as_bytes()) { Ok(_) => (), Err(err) => { @@ -104,7 +107,7 @@ fn main() { } } }, - ConsoleEvent::Quit => break 'events + None => () } } diff --git a/src/terminal/window.rs b/src/terminal/window.rs deleted file mode 100644 index 00ead957fb1e2aa156909bc790eb4d8e186c68c0..0000000000000000000000000000000000000000 --- a/src/terminal/window.rs +++ /dev/null @@ -1,229 +0,0 @@ - -use orbclient::*; - -/// A console char -pub struct ConsoleChar { - /// The char - character: char, - /// The color - color: Color, -} - -/// A console event -pub enum ConsoleEvent { - Line(String), - Quit -} - -/// A console window -pub struct ConsoleWindow { - /// The window - pub window: Box<Window>, - /// The char buffer - pub output: Vec<ConsoleChar>, - /// Previous commands - pub history: Vec<String>, - /// History index - pub history_i: u32, - /// Offset - pub offset: usize, - /// Scroll distance x - pub scroll_x: i32, - /// Scroll distance y - pub scroll_y: i32, - /// Wrap the text, if true - pub wrap: bool, -} - -impl ConsoleWindow { - /// Create a new console window - pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Box<Self> { - Box::new(ConsoleWindow { - window: Window::new(x, y, w, h, title).unwrap(), - output: Vec::new(), - history: vec!["".to_string()], - history_i: 0, - offset: 0, - scroll_x: 0, - scroll_y: 0, - wrap: true, - }) - } - - /// Print to the window - pub fn print(&mut self, string: &str, color: Color) { - for c in string.chars() { - self.output.push(ConsoleChar { - character: c, - color: color, - }); - } - self.sync(); - } - - /// Read input - pub fn read(&mut self) -> Vec<ConsoleEvent> { - let mut console_events = Vec::new(); - - for event in self.window.events_no_wait() { - match event.to_option() { - EventOption::Key(key_event) => { - if key_event.pressed { - match key_event.scancode { - K_BKSP => { - if self.offset > 0 { - self.history[self.history_i as usize] = - self.history[self.history_i as usize][0..self.offset - 1] - .to_string() + - &self.history[self.history_i as usize][self.offset..]; - self.offset -= 1; - } - } - K_DEL => { - if (self.offset) < self.history[self.history_i as usize].len() { - self.history[self.history_i as usize] = - self.history[self.history_i as usize][0..self.offset].to_string() + - &self.history[self.history_i as usize][self.offset + 1..self.history[self.history_i as usize].len() - 1]; - } - } - K_HOME => self.offset = 0, - K_UP => { - if self.history_i as usize + 1 < self.history.len() { - self.history_i += 1; - } - self.offset = self.history[self.history_i as usize].len(); - } - K_LEFT => { - if self.offset > 0 { - self.offset -= 1; - } - } - K_RIGHT => { - if (self.offset) < self.history[self.history_i as usize].len() { - self.offset += 1; - } - } - K_END => self.offset = self.history[self.history_i as usize].len(), - K_DOWN => { - if self.history_i > 0 { - self.history_i -= 1; - } - self.offset = self.history[self.history_i as usize].len(); - } - _ => { - match key_event.character { - '\x00' => (), - '\n' => { - let command = self.history[self.history_i as usize].clone(); - self.offset = 0; - self.history_i = 0; - if !self.history[0].is_empty() { - self.history.insert(0, "".to_string()); - } - while self.history.len() > 1000 { - self.history.pop(); - } - self.print(&command, Color::rgb(255, 255, 255)); - self.print("\n", Color::rgb(255, 255, 255)); - console_events.push(ConsoleEvent::Line(command)); - } - '\x1B' => (), - _ => { - self.history[self.history_i as usize] = - self.history[self.history_i as usize][0..self.offset] - .to_string() + - &key_event.character.to_string() + - &self.history[self.history_i as usize][self.offset..]; - self.offset += 1; - } - } - } - } - self.sync(); - } - } - EventOption::Quit(_quit_event) => console_events.push(ConsoleEvent::Quit), - _ => (), - } - } - - console_events - } - - /// Redraw the window - pub fn sync(&mut self) { - let scroll_x = self.scroll_x; - let scroll_y = self.scroll_y; - - let mut col = -scroll_x; - let cols = self.window.width() as i32 / 8; - let mut row = -scroll_y; - let rows = self.window.height() as i32 / 16; - - { - self.window.set(Color::rgb(0, 0, 0)); - - for c in self.output.iter() { - if self.wrap && col >= cols { - col = -scroll_x; - row += 1; - } - - if c.character == '\n' { - col = -scroll_x; - row += 1; - } else if c.character == '\t' { - col += 8 - col % 8; - } else { - if col >= 0 && col < cols && row >= 0 && row < rows { - self.window.char(8 * col, 16 * row, c.character, c.color); - } - col += 1; - } - } - - let mut i = 0; - for c in self.history[self.history_i as usize].chars() { - if self.wrap && col >= cols { - col = -scroll_x; - row += 1; - } - - if self.offset == i && col >= 0 && col < cols && row >= 0 && row < rows { - self.window.char(8 * col, 16 * row, '_', Color::rgb(255, 255, 255)); - } - - if c == '\n' { - col = -scroll_x; - row += 1; - } else if c == '\t' { - col += 8 - col % 8; - } else { - if col >= 0 && col < cols && row >= 0 && row < rows { - self.window.char(8 * col, 16 * row, c, Color::rgb(255, 255, 255)); - } - col += 1; - } - - i += 1; - } - - if self.wrap && col >= cols { - col = -scroll_x; - row += 1; - } - - if self.offset == i && col >= 0 && col < cols && row >= 0 && row < rows { - self.window.char(8 * col, 16 * row, '_', Color::rgb(255, 255, 255)); - } - } - - self.window.sync(); - - if row >= rows { - self.scroll_y += row - rows + 1; - - self.sync(); - } - } -}