diff options
author | Richard Palethorpe <richiejp@f-m.fm> | 2017-01-09 21:07:23 +0100 |
---|---|---|
committer | Joe Wilm <jwilm@users.noreply.github.com> | 2017-01-21 12:09:21 -0800 |
commit | 62294771905917087c484bfc57e4f6ca547d89ba (patch) | |
tree | d2c11cb7cb9e011969489b9802f5472b1f65ccd2 /src | |
parent | 6d0abe26077d7ca04119b241678ea9ede9f4fe4d (diff) |
ansi: Designate and invoke graphic character sets
Implement the designation of graphic character sets G0-G3 to ASCII or the
Special character and line drawing glyphs. As well as the invokation/selection
of the character sets (shift in, shift out and lock shifting).
Diffstat (limited to 'src')
-rw-r--r-- | src/ansi.rs | 122 | ||||
-rw-r--r-- | src/term/mod.rs | 122 |
2 files changed, 236 insertions, 8 deletions
diff --git a/src/ansi.rs b/src/ansi.rs index 81a48987..4f45fd62 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -238,6 +238,18 @@ pub trait Handler { /// DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq) fn unset_keypad_application_mode(&mut self) {} + + /// Set one of the graphic character sets, G0 to G3, as the active charset. + /// + /// 'Invoke' one of G0 to G3 in the GL area. Also refered to as shift in, + /// shift out and locking shift depending on the set being activated + fn set_active_charset(&mut self, CharsetIndex) {} + + /// Assign a graphic character set to G0, G1, G2 or G3 + /// + /// 'Designate' a graphic character set as one of G0 to G3, so that it can + /// later be 'invoked' by `set_active_charset` + fn configure_charset(&mut self, CharsetIndex, StandardCharset) {} } /// Terminal modes @@ -436,6 +448,23 @@ pub enum Attr { Background(Color), } +/// Identifiers which can be assigned to a graphic character set +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CharsetIndex { + /// Default set, is designated as ASCII at startup + G0, + G1, + G2, + G3, +} + +/// Standard or common character sets which can be designated as G0-G3 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum StandardCharset { + Ascii, + SpecialCharacterAndLineDrawing, +} + impl<'a, H, W> vte::Perform for Performer<'a, H, W> where H: Handler + TermInfo + 'a, W: io::Write + 'a @@ -454,6 +483,8 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W> C0::LF | C0::VT | C0::FF => self.handler.linefeed(), C0::BEL => self.handler.bell(), C0::SUB => self.handler.substitute(), + C0::SI => self.handler.set_active_charset(CharsetIndex::G0), + C0::SO => self.handler.set_active_charset(CharsetIndex::G1), C1::NEL => self.handler.newline(), C1::HTS => self.handler.set_horizontal_tabstop(), C1::DECID => self.handler.identify_terminal(self.writer), @@ -742,19 +773,41 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W> _ignore: bool, byte: u8 ) { + macro_rules! unhandled { + () => {{ + err_println!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})", + params, intermediates, byte as char, byte); + return; + }} + } + + macro_rules! configure_charset { + ($charset:path) => {{ + let index: CharsetIndex = match intermediates.first().cloned() { + Some(b'(') => CharsetIndex::G0, + Some(b')') => CharsetIndex::G1, + Some(b'*') => CharsetIndex::G2, + Some(b'+') => CharsetIndex::G3, + _ => unhandled!(), + }; + self.handler.configure_charset(index, $charset) + }} + } + match byte { + b'B' => configure_charset!(StandardCharset::Ascii), b'D' => self.handler.linefeed(), b'E' => self.handler.newline(), b'H' => self.handler.set_horizontal_tabstop(), b'M' => self.handler.reverse_index(), b'Z' => self.handler.identify_terminal(self.writer), b'c' => self.handler.reset_state(), + b'0' => configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing), b'7' => self.handler.save_cursor_position(), b'8' => self.handler.restore_cursor_position(), b'=' => self.handler.set_keypad_application_mode(), b'>' => self.handler.unset_keypad_application_mode(), - _ => err_println!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})", - params, intermediates, byte as char, byte), + _ => unhandled!(), } } } @@ -969,7 +1022,7 @@ pub mod C1 { mod tests { use std::io; use index::{Line, Column}; - use super::{Processor, Handler, Attr, TermInfo, Color}; + use super::{Processor, Handler, Attr, TermInfo, Color, StandardCharset, CharsetIndex}; use ::Rgb; /// The /dev/null of io::Write @@ -1074,4 +1127,67 @@ mod tests { parser.advance(&mut handler, *byte, &mut Void); } } + + struct CharsetHandler { + index: CharsetIndex, + charset: StandardCharset, + } + + impl Default for CharsetHandler { + fn default() -> CharsetHandler { + CharsetHandler { + index: CharsetIndex::G0, + charset: StandardCharset::Ascii, + } + } + } + + impl Handler for CharsetHandler { + fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) { + self.index = index; + self.charset = charset; + } + + fn set_active_charset(&mut self, index: CharsetIndex) { + self.index = index; + } + } + + impl TermInfo for CharsetHandler { + fn lines(&self) -> Line { Line(200) } + fn cols(&self) -> Column { Column(90) } + } + + #[test] + fn parse_designate_g0_as_line_drawing() { + static BYTES: &'static [u8] = &[0x1b, b'(', b'0']; + let mut parser = Processor::new(); + let mut handler = CharsetHandler::default(); + + for byte in &BYTES[..] { + parser.advance(&mut handler, *byte, &mut Void); + } + + assert_eq!(handler.index, CharsetIndex::G0); + assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); + } + + #[test] + fn parse_designate_g1_as_line_drawing_and_invoke() { + static BYTES: &'static [u8] = &[0x1b, 0x29, 0x30, 0x0e]; + let mut parser = Processor::new(); + let mut handler = CharsetHandler::default(); + + for byte in &BYTES[..3] { + parser.advance(&mut handler, *byte, &mut Void); + } + + assert_eq!(handler.index, CharsetIndex::G1); + assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); + + let mut handler = CharsetHandler::default(); + parser.advance(&mut handler, BYTES[3], &mut Void); + + assert_eq!(handler.index, CharsetIndex::G1); + } } diff --git a/src/term/mod.rs b/src/term/mod.rs index bb0bbc1b..45ce12e0 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -13,12 +13,12 @@ // limitations under the License. // //! Exports the `Term` type which is a high-level API for the Grid -use std::ops::{Deref, Range}; +use std::ops::{Deref, Range, Index, IndexMut}; use std::ptr; use std::cmp::min; use std::io; -use ansi::{self, Color, NamedColor, Attr, Handler}; +use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset}; use grid::{Grid, ClearRegion, ToRange}; use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive}; use selection::{Span, Selection}; @@ -200,6 +200,79 @@ pub use self::mode::TermMode; pub const TAB_SPACES: usize = 8; +trait CharsetMapping { + fn map(&self, c: char) -> char { + c + } +} + +impl CharsetMapping for StandardCharset { + /// Switch/Map character to the active charset. Ascii is the common case and + /// for that we want to do as little as possible. + #[inline] + fn map(&self, c: char) -> char { + match *self { + StandardCharset::Ascii => c, + StandardCharset::SpecialCharacterAndLineDrawing => + match c { + '`' => '◆', + 'a' => '▒', + 'b' => '\t', + 'c' => '\u{000c}', + 'd' => '\r', + 'e' => '\n', + 'f' => '°', + 'g' => '±', + 'h' => '\u{2424}', + 'i' => '\u{000b}', + 'j' => '┘', + 'k' => '┐', + 'l' => '┌', + 'm' => '└', + 'n' => '┼', + 'o' => '⎺', + 'p' => '⎻', + 'q' => '─', + 'r' => '⎼', + 's' => '⎽', + 't' => '├', + 'u' => '┤', + 'v' => '┴', + 'w' => '┬', + 'x' => '│', + 'y' => '≤', + 'z' => '≥', + '{' => 'π', + '|' => '≠', + '}' => '£', + '~' => '·', + _ => c + }, + } + } +} + +struct Charsets([StandardCharset; 4]); + +impl Charsets { + fn new() -> Charsets { + Charsets([StandardCharset::Ascii; 4]) + } +} + +impl Index<CharsetIndex> for Charsets { + type Output = StandardCharset; + fn index(&self, index: CharsetIndex) -> &StandardCharset { + &self.0[index as usize] + } +} + +impl IndexMut<CharsetIndex> for Charsets { + fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset { + &mut self.0[index as usize] + } +} + pub struct Term { /// The grid grid: Grid<Cell>, @@ -228,6 +301,13 @@ pub struct Term { /// Alt cursor alt_cursor: Point, + /// Currently configured graphic character sets + charsets: Charsets, + + /// The graphic character set, out of `charsets`, which ASCII is currently + /// being mapped to + active_charset: CharsetIndex, + /// Tabstops tabs: Vec<bool>, @@ -323,6 +403,8 @@ impl Term { alt: false, cursor: Point::default(), alt_cursor: Point::default(), + active_charset: CharsetIndex::G0, + charsets: Charsets::new(), tabs: tabs, mode: Default::default(), scroll_region: scroll_region, @@ -695,7 +777,7 @@ impl ansi::Handler for Term { { let cell = &mut self.grid[&self.cursor]; *cell = self.template_cell; - cell.c = c; + cell.c = self.charsets[self.active_charset].map(c); } if (self.cursor.col + 1) < self.grid.num_cols() { @@ -1109,6 +1191,18 @@ impl ansi::Handler for Term { debug_println!("unset mode::APP_KEYPAD"); self.mode.remove(mode::APP_KEYPAD); } + + #[inline] + fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) { + debug_println!("designate {:?} character set as {:?}", index, charset); + self.charsets[index] = charset; + } + + #[inline] + fn set_active_charset(&mut self, index: CharsetIndex) { + debug_println!("Activate {:?} character set", index); + self.active_charset = index; + } } #[cfg(test)] @@ -1116,11 +1210,12 @@ mod tests { extern crate serde_json; extern crate test; - use super::limit; + use super::{Term, limit, SizeInfo}; use grid::Grid; - use index::{Line, Column}; + use index::{Point, Line, Column}; use term::{Cell}; + use ansi::{Handler, CharsetIndex, StandardCharset}; /// Check that the grid can be serialized back and forth losslessly /// @@ -1144,6 +1239,23 @@ mod tests { assert_eq!(limit(5, 6, 10), 6); assert_eq!(limit(5, 1, 4), 4); } + + #[test] + fn input_line_drawing_character() { + let size = SizeInfo { + width: 21.0, + height: 51.0, + cell_width: 3.0, + cell_height: 3.0, + }; + let mut term = Term::new(size); + let cursor = Point::new(Line(0), Column(0)); + term.configure_charset(CharsetIndex::G0, + StandardCharset::SpecialCharacterAndLineDrawing); + term.input('a'); + + assert_eq!(term.grid()[&cursor].c, '▒'); + } } #[cfg(test)] |