summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard Palethorpe <richiejp@f-m.fm>2017-01-09 21:07:23 +0100
committerJoe Wilm <jwilm@users.noreply.github.com>2017-01-21 12:09:21 -0800
commit62294771905917087c484bfc57e4f6ca547d89ba (patch)
treed2c11cb7cb9e011969489b9802f5472b1f65ccd2 /src
parent6d0abe26077d7ca04119b241678ea9ede9f4fe4d (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.rs122
-rw-r--r--src/term/mod.rs122
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)]