summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-05-30 20:44:37 -0700
committerJoe Wilm <joe@jwilm.com>2016-06-02 19:42:28 -0700
commit30ec14510935d46e7454863f9a4e63e53bf7728c (patch)
tree9501fe70ecf582e57903fbc061d3e6a0928f3f33
parent70b0423a31016798592fc0e96ce316cb3f1e9d46 (diff)
Initial support for Terminal Emulation (woo!)
This patch introduces basic support for terminal emulation. Basic means commands that don't use paging and are not full screen applications like vim or tmux. Some paging applications are working properly, such as as `git log`. Other pagers work reasonably well as long as the help menu is not accessed. There is now a central Rgb color type which is shared by the renderer, terminal emulation, and the pty parser. The parser no longer owns a Handler. Instead, a mutable reference to a Handler is provided whenever advancing the parser. This resolved some potential ownership issues (eg parser owning the `Term` type would've been unworkable).
-rw-r--r--res/text.f.glsl7
-rw-r--r--src/ansi.rs233
-rw-r--r--src/grid.rs88
-rw-r--r--src/main.rs158
-rw-r--r--src/renderer/mod.rs9
-rw-r--r--src/term.rs350
-rw-r--r--src/tty.rs51
7 files changed, 716 insertions, 180 deletions
diff --git a/res/text.f.glsl b/res/text.f.glsl
index 3389fd7e..e817626f 100644
--- a/res/text.f.glsl
+++ b/res/text.f.glsl
@@ -5,10 +5,11 @@ layout(location = 0, index = 0) out vec4 color;
layout(location = 0, index = 1) out vec4 alphaMask;
uniform sampler2D mask;
-uniform vec3 textColor;
+uniform ivec3 textColor;
void main()
{
- alphaMask = vec4(texture(mask, TexCoords).rgb, 1.);
- color = vec4(textColor, 1.);
+ alphaMask = vec4(texture(mask, TexCoords).rgb, 1.0);
+ vec3 textColorF = vec3(textColor) / vec3(255.0, 255.0, 255.0);
+ color = vec4(textColorF, 1.0);
}
diff --git a/src/ansi.rs b/src/ansi.rs
index 23baa9ad..389ab94f 100644
--- a/src/ansi.rs
+++ b/src/ansi.rs
@@ -18,6 +18,7 @@
//! sequences only used by folks trapped in 1988.
use std::io::{Cursor, Read, Write, Chars};
+use ::Rgb;
/// A CSI Escape sequence
#[derive(Debug, Eq, PartialEq)]
@@ -59,7 +60,7 @@ pub enum Item {
pub const CSI_ATTR_MAX: usize = 16;
-pub struct Parser<H> {
+pub struct Parser {
/// Workspace for building a control sequence
buf: [char; 1024],
@@ -70,11 +71,24 @@ pub struct Parser<H> {
/// Current state
state: State,
+}
+
+/// Terminal modes
+#[derive(Debug, Eq, PartialEq)]
+pub enum Mode {
+ SwapScreenAndSetRestoreCursor = 1049,
+}
- /// Handler
+impl Mode {
+ /// Create mode from a primitive
///
- /// Receives data from the parser
- pub handler: H,
+ /// TODO lots of unhandled values..
+ pub fn from_primitive(num: i64) -> Option<Mode> {
+ Some(match num {
+ 1049 => Mode::SwapScreenAndSetRestoreCursor,
+ _ => return None
+ })
+ }
}
/// Mode for clearing line
@@ -152,18 +166,6 @@ pub enum Color {
BrightWhite,
}
-/// 16-million color specifier
-/// TODO
-#[derive(Debug, Eq, PartialEq)]
-pub struct ColorSpec {
- /// Red
- pub r: u8,
- /// Green
- pub g: u8,
- /// blue
- pub b: u8,
-}
-
/// Terminal character attributes
#[derive(Debug, Eq, PartialEq)]
pub enum Attr {
@@ -204,11 +206,11 @@ pub enum Attr {
/// Set indexed foreground color
Foreground(Color),
/// Set specific foreground color
- ForegroundSpec(ColorSpec),
+ ForegroundSpec(Rgb),
/// Set indexed background color
Background(Color),
/// Set specific background color
- BackgroundSpec(ColorSpec),
+ BackgroundSpec(Rgb),
/// Set default foreground
DefaultForeground,
/// Set default background
@@ -336,6 +338,12 @@ pub trait Handler {
/// set a terminal attribute
fn terminal_attribute(&mut self, attr: Attr) {}
+
+ /// Set mode
+ fn set_mode(&mut self, Mode) {}
+
+ /// Unset mode
+ fn unset_mode(&mut self, Mode) {}
}
/// An implementation of handler that just prints everything it gets
@@ -378,55 +386,62 @@ impl Handler for DebugHandler {
fn reset_state(&mut self) { println!("reset_state"); }
fn reverse_index(&mut self) { println!("reverse_index"); }
fn terminal_attribute(&mut self, attr: Attr) { println!("terminal_attribute: {:?}", attr); }
+ fn set_mode(&mut self, mode: Mode) { println!("set_mode: {:?}", mode); }
+ fn unset_mode(&mut self, mode: Mode) { println!("unset_mode: {:?}", mode); }
}
-impl<H: Handler> Parser<H> {
- pub fn new(handler: H) -> Parser<H> {
+impl Parser {
+ pub fn new() -> Parser {
Parser {
buf: [0 as char; 1024],
idx: 0,
state: Default::default(),
- handler: handler,
}
}
/// Advance the state machine.
///
/// Maybe returns an Item which represents a state change of the terminal
- pub fn advance(&mut self, c: char) {
+ pub fn advance<H>(&mut self, handler: &mut H, c: char)
+ where H: Handler
+ {
// println!("state: {:?}; char: {:?}", self.state, c);
// Control characters get handled immediately
if is_control(c) {
- self.control(c);
+ self.control(handler, c);
return;
}
match self.state {
State::Base => {
- self.advance_base(c);
+ self.advance_base(handler, c);
},
State::Escape => {
- self.escape(c);
+ self.escape(handler, c);
},
State::Csi => {
- self.csi(c);
+ self.csi(handler, c);
}
}
}
- fn advance_base(&mut self, c: char) {
- self.handler.input(c);
+ fn advance_base<H>(&mut self, handler: &mut H, c: char)
+ where H: Handler
+ {
+ handler.input(c);
}
/// Handle character following an ESC
///
/// TODO Handle `ST`, `'#'`, `'P'`, `'_'`, `'^'`, `']'`, `'k'`,
/// 'n', 'o', '(', ')', '*', '+', '=', '>'
- fn escape(&mut self, c: char) {
+ fn escape<H>(&mut self, handler: &mut H, c: char)
+ where H: Handler
+ {
// Helper for items which complete a sequence.
macro_rules! sequence_complete {
($fun:ident) => {{
- self.handler.$fun();
+ handler.$fun();
self.state = State::Base;
}}
}
@@ -444,30 +459,38 @@ impl<H: Handler> Parser<H> {
'7' => sequence_complete!(save_cursor_position),
'8' => sequence_complete!(restore_cursor_position),
_ => {
+ self.state = State::Base;
err_println!("Unknown ESC 0x{:02x} {:?}", c as usize, c);
}
}
}
- fn csi(&mut self, c: char) {
+ fn csi<H>(&mut self, handler: &mut H, c: char)
+ where H: Handler
+ {
self.buf[self.idx] = c;
self.idx += 1;
if (self.idx == self.buf.len()) || is_csi_terminator(c) {
- self.csi_parse();
+ self.csi_parse(handler);
}
}
/// Parse current CSI escape buffer
///
/// ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
- fn csi_parse(&mut self) {
+ fn csi_parse<H>(&mut self, handler: &mut H)
+ where H: Handler
+ {
let mut idx = 0;
let mut args = [0i64; CSI_ATTR_MAX];
let mut args_idx = 0;
// Get a slice which is the used subset of self.buf
let mut raw = &self.buf[..self.idx];
+ if raw[0] == '?' {
+ raw = &raw[1..];
+ }
// Parse args
while !raw.is_empty() {
@@ -500,26 +523,46 @@ impl<H: Handler> Parser<H> {
}}
}
+ macro_rules! unhandled {
+ () => {{
+ err_println!("Recognized, but unhandled CSI: {:?}", &self.buf[..self.idx]);
+ self.state = State::Base;
+ return;
+ }}
+ }
+
macro_rules! arg_or_default {
($arg:expr, $default:expr) => {
if $arg == 0 { $default } else { $arg }
}
}
+ macro_rules! debug_csi {
+ () => {
+ err_println!("CSI: {:?}", &self.buf[..self.idx]);
+ }
+ }
+
if raw.is_empty() {
println!("raw is empty");
unknown!();
}
match raw[0] {
- '@' => self.handler.insert_blank(arg_or_default!(args[0], 1)),
- 'A' => self.handler.move_up(arg_or_default!(args[0], 1)),
- 'B' | 'e' => self.handler.move_down(arg_or_default!(args[0], 1)),
- 'c' => self.handler.identify_terminal(),
- 'C' | 'a' => self.handler.move_forward(arg_or_default!(args[0], 1)),
- 'D' => self.handler.move_backward(arg_or_default!(args[0], 1)),
- 'E' => self.handler.move_down_and_cr(arg_or_default!(args[0], 1)),
- 'F' => self.handler.move_up_and_cr(arg_or_default!(args[0], 1)),
+ '@' => handler.insert_blank(arg_or_default!(args[0], 1)),
+ 'A' => {
+ debug_csi!();
+ handler.move_up(arg_or_default!(args[0], 1));
+ },
+ 'B' | 'e' => handler.move_down(arg_or_default!(args[0], 1)),
+ 'c' => handler.identify_terminal(),
+ 'C' | 'a' => {
+ debug_csi!();
+ handler.move_forward(arg_or_default!(args[0], 1))
+ },
+ 'D' => handler.move_backward(arg_or_default!(args[0], 1)),
+ 'E' => handler.move_down_and_cr(arg_or_default!(args[0], 1)),
+ 'F' => handler.move_up_and_cr(arg_or_default!(args[0], 1)),
'g' => {
let mode = match args[0] {
0 => TabulationClearMode::Current,
@@ -527,15 +570,15 @@ impl<H: Handler> Parser<H> {
_ => unknown!(),
};
- self.handler.clear_tabs(mode);
+ handler.clear_tabs(mode);
},
- 'G' | '`' => self.handler.goto_col(arg_or_default!(args[0], 1)),
+ 'G' | '`' => handler.goto_col(arg_or_default!(args[0], 1)),
'H' | 'f' => {
- let x = arg_or_default!(args[0], 1);
- let y = arg_or_default!(args[1], 1);
- self.handler.goto(x, y);
+ let y = arg_or_default!(args[0], 1);
+ let x = arg_or_default!(args[1], 1);
+ handler.goto(x - 1, y - 1);
},
- 'I' => self.handler.move_forward_tabs(arg_or_default!(args[0], 1)),
+ 'I' => handler.move_forward_tabs(arg_or_default!(args[0], 1)),
'J' => {
let mode = match args[0] {
0 => ClearMode::Below,
@@ -544,7 +587,7 @@ impl<H: Handler> Parser<H> {
_ => unknown!(),
};
- self.handler.clear_screen(mode);
+ handler.clear_screen(mode);
},
'K' => {
let mode = match args[0] {
@@ -554,27 +597,29 @@ impl<H: Handler> Parser<H> {
_ => unknown!(),
};
- self.handler.clear_line(mode);
+ handler.clear_line(mode);
},
- 'S' => self.handler.scroll_up(arg_or_default!(args[0], 1)),
- 'T' => self.handler.scroll_down(arg_or_default!(args[0], 1)),
- 'L' => self.handler.insert_blank_lines(arg_or_default!(args[0], 1)),
+ 'S' => handler.scroll_up(arg_or_default!(args[0], 1)),
+ 'T' => handler.scroll_down(arg_or_default!(args[0], 1)),
+ 'L' => handler.insert_blank_lines(arg_or_default!(args[0], 1)),
'l' => {
- // TODO ResetMode
- //
- // This one seems like a lot of (important) work; going to come back to it.
- unknown!();
+ let mode = Mode::from_primitive(args[0]);
+ match mode {
+ Some(mode) => handler.set_mode(mode),
+ None => unhandled!(),
+ }
},
- 'M' => self.handler.delete_lines(arg_or_default!(args[0], 1)),
- 'X' => self.handler.erase_chars(arg_or_default!(args[0], 1)),
- 'P' => self.handler.delete_chars(arg_or_default!(args[0], 1)),
- 'Z' => self.handler.move_backward_tabs(arg_or_default!(args[0], 1)),
- 'd' => self.handler.goto_row(arg_or_default!(args[0], 1)),
+ 'M' => handler.delete_lines(arg_or_default!(args[0], 1)),
+ 'X' => handler.erase_chars(arg_or_default!(args[0], 1)),
+ 'P' => handler.delete_chars(arg_or_default!(args[0], 1)),
+ 'Z' => handler.move_backward_tabs(arg_or_default!(args[0], 1)),
+ 'd' => handler.goto_row(arg_or_default!(args[0], 1)),
'h' => {
- // TODO SetMode
- //
- // Ditto for 'l'
- unknown!();
+ let mode = Mode::from_primitive(args[0]);
+ match mode {
+ Some(mode) => handler.unset_mode(mode),
+ None => unhandled!(),
+ }
},
'm' => {
let raw_attrs = &args[..args_idx];
@@ -655,15 +700,15 @@ impl<H: Handler> Parser<H> {
_ => unknown!(),
};
- self.handler.terminal_attribute(attr);
+ handler.terminal_attribute(attr);
i += 1; // C-for expr
}
}
- 'n' => self.handler.identify_terminal(),
+ 'n' => handler.identify_terminal(),
'r' => unknown!(), // set scrolling region
- 's' => self.handler.save_cursor_position(),
- 'u' => self.handler.restore_cursor_position(),
+ 's' => handler.save_cursor_position(),
+ 'u' => handler.restore_cursor_position(),
_ => unknown!(),
}
@@ -674,15 +719,17 @@ impl<H: Handler> Parser<H> {
self.idx = 0;
}
- fn control(&mut self, c: char) {
+ fn control<H>(&mut self, handler: &mut H, c: char)
+ where H: Handler
+ {
match c {
- C0::HT => self.handler.put_tab(1),
- C0::BS => self.handler.backspace(1),
- C0::CR => self.handler.carriage_return(),
+ C0::HT => handler.put_tab(1),
+ C0::BS => handler.backspace(1),
+ C0::CR => handler.carriage_return(),
C0::LF |
C0::VT |
- C0::FF => self.handler.linefeed(),
- C0::BEL => self.handler.bell(),
+ C0::FF => handler.linefeed(),
+ C0::BEL => handler.bell(),
C0::ESC => {
self.csi_reset();
self.state = State::Escape;
@@ -690,7 +737,7 @@ impl<H: Handler> Parser<H> {
},
// C0::S0 => Control::SwitchG1,
// C0::S1 => Control::SwitchG0,
- C0::SUB => self.handler.substitute(),
+ C0::SUB => handler.substitute(),
C0::CAN => {
self.csi_reset();
return;
@@ -707,14 +754,14 @@ impl<H: Handler> Parser<H> {
()
},
C1::NEL => {
- self.handler.newline();
+ handler.newline();
()
},
C1::SSA | C1::ESA => {
()
},
C1::HTS => {
- self.handler.set_horizontal_tabstop();
+ handler.set_horizontal_tabstop();
()
},
C1::HTJ | C1::VTS | C1::PLD | C1::PLU | C1::RI | C1::SS2 |
@@ -723,7 +770,7 @@ impl<H: Handler> Parser<H> {
()
},
C1::DECID => {
- self.handler.identify_terminal();
+ handler.identify_terminal();
},
C1::CSI | C1::ST => {
()
@@ -740,7 +787,7 @@ impl<H: Handler> Parser<H> {
/// Parse a color specifier from list of attributes
-fn parse_color(attrs: &[i64], i: &mut usize) -> Option<ColorSpec> {
+fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Rgb> {
if attrs.len() < 2 {
return None;
}
@@ -765,7 +812,7 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<ColorSpec> {
return None;
}
- Some(ColorSpec {
+ Some(Rgb {
r: r as u8,
g: g as u8,
b: b as u8
@@ -1011,7 +1058,8 @@ impl Default for State {
#[cfg(test)]
mod tests {
use std::io::{Cursor, Read};
- use super::{Parser, Escape, Handler, Attr, ColorSpec, DebugHandler};
+ use super::{Parser, Escape, Handler, Attr, Rgb, DebugHandler};
+ use ::Rgb;
#[test]
fn parse_control_attribute() {
@@ -1031,13 +1079,14 @@ mod tests {
];
let cursor = Cursor::new(BYTES);
- let mut parser = Parser::new(TestHandler::default());
+ let mut parser = Parser::new();
+ let mut handler = TestHandler::default();
for c in cursor.chars() {
- parser.advance(c.unwrap());
+ parser.advance(&mut handler, c.unwrap());
}
- assert_eq!(parser.handler.attr, Some(Attr::Bold));
+ assert_eq!(handler.attr, Some(Attr::Bold));
}
#[test]
@@ -1059,19 +1108,20 @@ mod tests {
];
let mut cursor = Cursor::new(BYTES);
- let mut parser = Parser::new(TestHandler::default());
+ let mut parser = Parser::new();
+ let mut handler = TestHandler::default();
for c in cursor.chars() {
- parser.advance(c.unwrap());
+ parser.advance(&mut handler, c.unwrap());
}
- let spec = ColorSpec {
+ let spec = Rgb {
r: 128,
g: 66,
b: 255
};
- assert_eq!(parser.handler.attr, Some(Attr::ForegroundSpec(spec)));
+ assert_eq!(handler.attr, Some(Attr::ForegroundSpec(spec)));
}
/// No exactly a test; useful for debugging
@@ -1094,10 +1144,11 @@ mod tests {
];
let mut cursor = Cursor::new(BYTES);
- let mut parser = Parser::new(DebugHandler);
+ let mut handler = DebugHandler;
+ let mut parser = Parser::new();
for c in cursor.chars() {
- parser.advance(c.unwrap());
+ parser.advance(&mut handler, c.unwrap());
}
}
}
diff --git a/src/grid.rs b/src/grid.rs
index 86a2e45f..765a5e1a 100644
--- a/src/grid.rs
+++ b/src/grid.rs
@@ -1,31 +1,41 @@
//! Functions for computing properties of the terminal grid
-use std::ops::{Index, IndexMut};
+use std::collections::VecDeque;
+
+use std::ops::{Index, IndexMut, Deref, DerefMut};
+
+use term::Cursor;
+use ::Rgb;
/// Calculate the number of cells for an axis
pub fn num_cells_axis(cell_width: u32, cell_sep: i32, screen_width: u32) -> u32 {
- ((screen_width as i32 + cell_sep) as f64 / (cell_width as i32 - cell_sep) as f64) as u32
+ println!("num_cells_axis(cell_width: {}, cell_sep: {}, screen_width: {}",
+ cell_width, cell_sep, screen_width);
+ ((screen_width as i32 - cell_sep) as f64 / (cell_width as i32 + cell_sep) as f64) as u32
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct Cell {
- pub character: String,
+ pub c: char,
+ pub fg: Rgb,
+ pub bg: Rgb,
}
impl Cell {
- pub fn new<S>(c: S) -> Cell
- where S: Into<String>
- {
+ pub fn new(c: char) -> Cell {
Cell {
- character: c.into(),
+ c: c.into(),
+ bg: Default::default(),
+ fg: Default::default(),
}
}
}
/// Represents the terminal display contents
+#[derive(Clone)]
pub struct Grid {
/// Rows in the grid. Each row holds a list of cells corresponding to the columns in that row.
- raw: Vec<Row>,
+ raw: VecDeque<Row>,
/// Number of columns
cols: usize,
@@ -38,9 +48,9 @@ pub struct Grid {
impl Grid {
pub fn new(rows: usize, cols: usize) -> Grid {
- let mut raw = Vec::with_capacity(rows);
+ let mut raw = VecDeque::with_capacity(rows);
for _ in 0..raw.capacity() {
- raw.push(Row::new(cols));
+ raw.push_back(Row::new(cols));
}
Grid {
@@ -57,28 +67,67 @@ impl Grid {
pub fn cols(&self) -> usize {
self.cols
}
+
+ pub fn feed(&mut self) {
+ // do the borrowck dance
+ let row = self.raw.pop_front().unwrap();
+ self.raw.push_back(row);
+ }
+
+ pub fn unfeed(&mut self) {
+ // do the borrowck dance
+ let row = self.raw.pop_back().unwrap();
+ self.raw.push_front(row);
+ }
+
+ pub fn clear(&mut self) {
+ for row in self.raw.iter_mut() {
+ for cell in row.iter_mut() {
+ cell.c = ' ';
+ }
+ }
+ }
}
impl Index<usize> for Grid {
type Output = Row;
+ #[inline]
fn index<'a>(&'a self, index: usize) -> &'a Row {
&self.raw[index]
}
}
impl IndexMut<usize> for Grid {
+ #[inline]
fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Row {
&mut self.raw[index]
}
}
+impl Index<Cursor> for Grid {
+ type Output = Cell;
+
+ #[inline]
+ fn index<'a>(&'a self, cursor: Cursor) -> &'a Cell {
+ &self.raw[cursor.y as usize][cursor.x as usize]
+ }
+}
+
+impl IndexMut<Cursor> for Grid {
+ #[inline]
+ fn index_mut<'a>(&'a mut self, cursor: Cursor) -> &'a mut Cell {
+ &mut self.raw[cursor.y as usize][cursor.x as usize]
+ }
+}
+
/// A row in the grid
+#[derive(Debug, Clone)]
pub struct Row(Vec<Cell>);
impl Row {
pub fn new(columns: usize) -> Row {
- Row(vec![Cell::new(""); columns])
+ Row(vec![Cell::new(' '); columns])
}
pub fn cols(&self) -> usize {
@@ -86,15 +135,30 @@ impl Row {
}
}
+impl Deref for Row {
+ type Target = Vec<Cell>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for Row {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
impl Index<usize> for Row {
type Output = Cell;
+ #[inline]
fn index<'a>(&'a self, index: usize) -> &'a Cell {
&self.0[index]
}
}
impl IndexMut<usize> for Row {
+ #[inline]
fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Cell {
&mut self.0[index]
}
diff --git a/src/main.rs b/src/main.rs
index 1f945420..f13784bc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(io)]
+#![feature(unicode)]
extern crate fontconfig;
extern crate freetype;
@@ -11,10 +12,6 @@ extern crate glutin;
extern crate cgmath;
extern crate euclid;
-use std::collections::HashMap;
-
-use std::io::{BufReader, Read, BufRead};
-
#[macro_use]
mod macros;
@@ -25,10 +22,27 @@ mod grid;
mod meter;
mod tty;
mod ansi;
+mod term;
+
+use std::collections::HashMap;
+use std::io::{BufReader, Read, BufRead, Write, BufWriter};
+use std::sync::Arc;
+use std::fs::File;
+
+use std::os::unix::io::{FromRawFd, AsRawFd};
use renderer::{Glyph, QuadRenderer};
use text::FontDesc;
use grid::Grid;
+use term::Term;
+use meter::Meter;
+
+#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
+pub struct Rgb {
+ r: u8,
+ g: u8,
+ b: u8,
+}
mod gl {
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
@@ -37,9 +51,9 @@ mod gl {
static INIT_LIST: &'static str = "abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
01234567890\
- ~`!@#$%^&*()[]{}-_=+\\|\"/?.,<>;:";
+ ~`!@#$%^&*()[]{}-_=+\\|\"'/?.,<>;:█└│├─➜";
-type GlyphCache = HashMap<String, renderer::Glyph>;
+type GlyphCache = HashMap<char, renderer::Glyph>;
/// Render a string in a predefined location. Used for printing render time for profiling and
/// optimization.
@@ -47,13 +61,12 @@ fn render_string(s: &str,
renderer: &QuadRenderer,
glyph_cache: &GlyphCache,
cell_width: u32,
- color: &renderer::Rgb)
+ color: &Rgb)
{
let (mut x, mut y) = (200f32, 20f32);
for c in s.chars() {
- let s: String = c.escape_default().collect();
- if let Some(glyph) = glyph_cache.get(&s[..]) {
+ if let Some(glyph) = glyph_cache.get(&c) {
renderer.render(glyph, x, y, color);
}
@@ -62,7 +75,11 @@ fn render_string(s: &str,
}
fn main() {
- let window = glutin::Window::new().unwrap();
+ let window = glutin::WindowBuilder::new()
+ .with_title("alacritty".into())
+ .build()
+ .unwrap();
+
let (width, height) = window.get_inner_size_pixels().unwrap();
unsafe {
window.make_current().unwrap();
@@ -89,54 +106,19 @@ fn main() {
let num_cols = grid::num_cells_axis(cell_width, sep_x, width);
let num_rows = grid::num_cells_axis(cell_height, sep_y, height);
- let mut cmd = tty::new(num_rows as u8, num_cols as u8);
-
- ::std::thread::spawn(move || {
- for byte in cmd.bytes() {
- let b = byte.unwrap();
- println!("{:02x}, {:?}", b, ::std::char::from_u32(b as u32));
- }
- });
+ let tty = tty::new(num_rows as u8, num_cols as u8);
+ tty.resize(num_rows as usize, num_cols as usize, width as usize, height as usize);
+ let mut reader = tty.reader();
+ let mut writer = tty.writer();
println!("num_cols, num_rows = {}, {}", num_cols, num_rows);
let mut grid = Grid::new(num_rows as usize, num_cols as usize);
- // let contents = [
- // "for (row, line) in contents.iter().enumerate() {",
- // " for (i, c) in line.chars().enumerate() {",
- // " grid[row][i] = grid::Cell::new(Some(c.escape_default().collect()));",
- // " }",
- // "}"];
-
- let contents = include_str!("grid.rs");
- let mut row = 0usize;
- let mut col = 0;
-
- for (i, c) in contents.chars().enumerate() {
- if c == '\n' {
- row += 1;
- col = 0;
- continue;
- }
-
- if row >= (num_rows as usize) {
- break;
- }
-
- if col >= grid.cols() {
- continue;
- }
-
- grid[row][col] = grid::Cell::new(c.escape_default().collect::<String>());
- col += 1;
- }
-
let mut glyph_cache = HashMap::new();
for c in INIT_LIST.chars() {
let glyph = Glyph::new(&rasterizer.get_glyph(&desc, font_size, c));
- let string: String = c.escape_default().collect();
- glyph_cache.insert(string, glyph);
+ glyph_cache.insert(c, glyph);
}
unsafe {
@@ -145,46 +127,102 @@ fn main() {
gl::Enable(gl::MULTISAMPLE);
}
+ let (chars_tx, chars_rx) = ::std::sync::mpsc::channel();
+ ::std::thread::spawn(move || {
+ for c in reader.chars() {
+ let c = c.unwrap();
+ chars_tx.send(c);
+ }
+ });
+
let renderer = QuadRenderer::new(width, height);
+ let mut terminal = Term::new(tty, grid);
+ let mut meter = Meter::new();
+
+ let mut pty_parser = ansi::Parser::new();
- let mut meter = meter::Meter::new();
'main_loop: loop {
- for event in window.poll_events() {
- match event {
- glutin::Event::Closed => break 'main_loop,
- _ => ()
+ // Handle keyboard/mouse input and other window events
+ {
+ let mut writer = BufWriter::new(&writer);
+ for event in window.poll_events() {
+ match event {
+ glutin::Event::Closed => break 'main_loop,
+ glutin::Event::ReceivedCharacter(c) => {
+ let encoded = c.encode_utf8();
+ writer.write(encoded.as_slice());
+ },
+ glutin::Event::KeyboardInput(state, _code, key) => {
+ match state {
+ glutin::ElementState::Pressed => {
+ match key {
+ Some(glutin::VirtualKeyCode::Up) => {
+ writer.write("\x1b[A".as_bytes());
+ },
+ Some(glutin::VirtualKeyCode::Down) => {
+ writer.write("\x1b[B".as_bytes());
+ },
+ Some(glutin::VirtualKeyCode::Left) => {
+ writer.write("\x1b[D".as_bytes());
+ },
+ Some(glutin::VirtualKeyCode::Right) => {
+ writer.write("\x1b[C".as_bytes());
+ },
+ _ => (),
+ }
+ },
+ _ => (),
+ }
+ },
+ _ => ()
+ }
}
}
+ while let Ok(c) = chars_rx.try_recv() {
+ pty_parser.advance(&mut terminal, c);
+ }
+
unsafe {
gl::ClearColor(0.0, 0.0, 0.00, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
{
- let color = renderer::Rgb { r: 0.917, g: 0.917, b: 0.917 };
let _sampler = meter.sampler();
+ // Draw the grid
+ let grid = terminal.grid();
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
let cell = &row[j];
- if !cell.character.is_empty() {
- if let Some(glyph) = glyph_cache.get(&cell.character[..]) {
+ if cell.c != ' ' {
+ if let Some(glyph) = glyph_cache.get(&cell.c) {
let y = (cell_height as f32 + sep_y as f32) * (i as f32);
let x = (cell_width as f32 + sep_x as f32) * (j as f32);
let y_inverted = (height as f32) - y - (cell_height as f32);
- renderer.render(glyph, x, y_inverted, &color);
+ renderer.render(glyph, x, y_inverted, &cell.fg);
}
}
}
}
+
+ // Also draw the cursor
+ if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
+ let y = (cell_height as f32 + sep_y as f32) * (terminal.cursor_y() as f32);
+ let x = (cell_width as f32 + sep_x as f32) * (terminal.cursor_x() as f32);
+
+ let y_inverted = (height as f32) - y - (cell_height as f32);
+
+ renderer.render(glyph, x, y_inverted, &term::DEFAULT_FG);
+ }
}
let timing = format!("{:.3} usec", meter.average());
- let color = renderer::Rgb { r: 0.835, g: 0.306, b: 0.325 };
+ let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };