terminal_escapes/
lib.rs

1use std::fmt;
2use std::io::{Read, Write, Result, Error, ErrorKind};
3use std::iter::Peekable;
4
5#[derive(Clone, Copy, PartialEq, Eq)]
6pub enum Sequence<'a> {
7    /// Reset all terminal settings to default.
8    Reset,
9
10    /// Enable/disable line wrapping.
11    LineWrap(bool),
12
13    /// Set default font.
14    FontDefault,
15
16    /// Set alternate font.
17    FontAlternate,
18
19    /// Set the cursor position (row, column).
20    CursorAt(u32, u32),
21
22    /// Move the cursor position by the given delta (row, column).
23    ///
24    /// (0, 0) is at the top left of the screen.
25    CursorMove(i32, i32),
26
27    /// Save the cursor position.
28    CursorSave,
29
30    /// Resotre the saved cursor position.
31    CursorRestore,
32
33    /// Save the cursor position and attributes.
34    CursorSaveAttributes,
35
36    /// Resotre the saved cursor position and attributes.
37    CursorRestoreAttributes,
38
39    /// Enable scrolling.
40    ///
41    /// If `None` is passed then scrolling is enabled for the whole screen.
42    /// If `Some((start, end))` is passed then scrolling is enabled from row `start` to `end`.
43    ScrollEnable(Option<(u32, u32)>),
44
45    /// Scroll by the given number of rows.
46    /// Positive rows means scrolling down, negative rows means scrolling up.
47    Scroll(i32),
48
49    /// Erases from the current cursor position to the end of the current line.
50    EraseEndOfLine,
51
52    /// Erases from the current cursor position to the start of the current line.
53    EraseStartOfLine,
54
55    /// Erases the entire current line.
56    EraseLine,
57
58    /// Erases the screen from the current line down to the bottom of the screen.
59    EraseDown,
60
61    /// Erases the screen from the current line up to the top of the screen.
62    EraseUp,
63
64    /// Erases the screen with the background colour and moves the cursor to home.
65    EraseScreen,
66
67    /// Sets display attributes.
68    SetAttributes(&'a [Attribute])
69}
70
71/// Display attributes.
72#[derive(Clone, Copy, PartialEq, Eq)]
73pub enum Attribute {
74    /// Reset all attributes.
75    Default,
76
77    /// Set the text bright.
78    Bright,
79
80    /// Set the text dim.
81    Dim,
82
83    /// Underscore the text.
84    Underscore,
85
86    /// Make the text blink.
87    Blink,
88
89    /// Reverse.
90    Reverse,
91
92    /// Hides the cursor.
93    Hidden,
94
95    /// Set the background color.
96    Foreground(Color),
97
98    /// Set the foreground color.
99    Background(Color)
100}
101
102/// Standard colors.
103#[derive(Clone, Copy, PartialEq, Eq)]
104pub enum Color {
105    Black,
106    Red,
107    Green,
108    Yellow,
109    Blue,
110    Magenta,
111    Cyan,
112    White
113}
114
115impl<'a> fmt::Display for Sequence<'a> {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        use Sequence::*;
118        match self {
119            Reset => write!(f, "\x1bc"),
120            LineWrap(true) => write!(f, "\x1b[7h"),
121            LineWrap(false) => write!(f, "\x1b[7l"),
122            FontDefault => write!(f, "\x1b("),
123            FontAlternate => write!(f, "\x1b)"),
124            CursorAt(row, column) => write!(f, "\x1b[{};{}H", row, column),
125            CursorMove(drow, dcolumn) => {
126                if *drow != 0 {
127                    if *drow < 0 {
128                        write!(f, "\x1b[{}A", -drow)?;
129                    } else {
130                        write!(f, "\x1b[{}B", drow)?;
131                    }
132                }
133                if *dcolumn != 0 {
134                    if *dcolumn > 0 {
135                        write!(f, "\x1b[{}C", dcolumn)?;
136                    } else {
137                        write!(f, "\x1b[{}D", -dcolumn)?;
138                    }
139                }
140                Ok(())
141            },
142            CursorSave => write!(f, "\x1b[s"),
143            CursorRestore => write!(f, "\x1b[u"),
144            CursorSaveAttributes => write!(f, "\x1b7"),
145            CursorRestoreAttributes => write!(f, "\x1b8"),
146            ScrollEnable(None) => write!(f, "\x1b[r"),
147            ScrollEnable(Some((start, end))) => write!(f, "\x1b[{};{}r", start, end),
148            Scroll(d) => {
149                if *d < 0 {
150                    for _ in 0..(-*d) {
151                        write!(f, "\x1b[D")?;
152                    }
153                } else {
154                    for _ in 0..*d {
155                        write!(f, "\x1b[M")?;
156                    }
157                }
158                Ok(())
159            },
160            EraseEndOfLine => write!(f, "\x1b[K"),
161            EraseStartOfLine => write!(f, "\x1b[1K"),
162            EraseLine => write!(f, "\x1b[2K"),
163            EraseDown => write!(f, "\x1b[J"),
164            EraseUp => write!(f, "\x1b[1J"),
165            EraseScreen => write!(f, "\x1b[2J"),
166            SetAttributes(attributes) => {
167                write!(f, "\x1b[{}m", DisplayAttributes(attributes))
168            }
169        }
170    }
171}
172
173impl fmt::Display for Attribute {
174    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        use Attribute::*;
176        match self {
177            Default => write!(f, "0"),
178            Bright => write!(f, "1"),
179            Dim => write!(f, "2"),
180            Underscore => write!(f, "4"),
181            Blink => write!(f, "5"),
182            Reverse => write!(f, "7"),
183            Hidden => write!(f, "8"),
184            Foreground(c) => write!(f, "{}", 30+c.index()),
185            Background(c) => write!(f, "{}", 40+c.index()),
186        }
187    }
188}
189
190impl Color {
191    /// Return the index of the color.
192    ///
193    /// Used to format color attributes.
194    pub fn index(&self) -> u8 {
195        use Color::*;
196        match self {
197            Black => 0,
198            Red => 1,
199            Green => 2,
200            Yellow => 3,
201            Blue => 4,
202            Magenta => 5,
203            Cyan => 6,
204            White => 7
205        }
206    }
207}
208
209struct DisplayAttributes<'a>(&'a [Attribute]);
210
211impl<'a> fmt::Display for DisplayAttributes<'a> {
212    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213        match self.0.split_first() {
214            Some((head, tail)) => {
215                write!(f, "{}", head)?;
216                for a in tail {
217                    write!(f, ";{}", a)?;
218                }
219                Ok(())
220            },
221            None => Ok(())
222        }
223    }
224}
225
226/// Client queries.
227pub enum Query {
228    /// Get the cursor position.
229    CursorPosition
230}
231
232impl fmt::Display for Query {
233    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234        use Query::*;
235        match self {
236            CursorPosition => write!(f, "\x1b[6n")
237        }
238    }
239}
240
241/// Device responses.
242pub enum Response {
243    /// Cursor position informations.
244    CursorPosition(u32, u32)
245}
246
247/// A device handling escapes sequences.
248pub struct Device<I: Read, O: Write> {
249    input: I,
250    output: O,
251    buffer: Vec<u8>,
252    offset: usize
253}
254
255impl<I: Read, O: Write> Device<I, O> {
256    /// Create a new device from an input and output.
257    pub fn new(input: I, output: O) -> Device<I, O> {
258        Device {
259            input: input,
260            output: output,
261            buffer: Vec::new(),
262            offset: 0
263        }
264    }
265
266    /// Read the next byte out of the input stream.
267    fn next_byte(&mut self) -> Result<Option<u8>> {
268        let mut buffer = [0];
269        let n = self.input.read(&mut buffer)?;
270        if n == 0 {
271            Ok(None)
272        } else {
273            Ok(Some(buffer[0]))
274        }
275    }
276
277    /// Wait for the device response.
278    ///
279    /// Any eventual data arrived before the device response is buffered so it can be
280    /// later outputed to a reader.
281    fn response(&mut self) -> Result<Response> {
282        loop {
283            match self.next_byte()? {
284                Some(0x1b) => {
285                    return self.parse_response()
286                },
287                Some(c) => {
288                    self.buffer.push(c)
289                },
290                None => return Err(Error::new(ErrorKind::UnexpectedEof, "no response from the device"))
291            }
292        }
293    }
294
295    /// Parse a device response, after the initial `<ESC>` character.
296    fn parse_response(&mut self) -> Result<Response> {
297        let mut buffer = Vec::new();
298        loop {
299            match self.next_byte()? {
300                Some(c) => {
301                    match c {
302                        0x52 => { // [{ROW};{COLUMN}R: Cursor position
303                            let mut it = buffer.into_iter().peekable();
304                            expect_byte(&mut it, 0x5b)?; // [
305                            let row = read_u32(&mut it)?; // {ROW}
306                            expect_byte(&mut it, 0x3b)?; // ;
307                            let column = read_u32(&mut it)?; // {COLUMN}
308
309                            return Ok(Response::CursorPosition(row-1, column-1))
310                        },
311                        _ => {
312                            buffer.push(c)
313                        }
314                    }
315                },
316                None => return Err(Error::new(ErrorKind::UnexpectedEof, "incomplete device response"))
317            }
318        }
319    }
320
321    /// Requests the cursor position on the device.
322    ///
323    /// The output format is `(row, column)`.
324    #[allow(irrefutable_let_patterns)]
325    pub fn cursor_position(&mut self) -> Result<(u32, u32)> {
326        write!(self.output, "{}", Query::CursorPosition)?;
327        self.flush()?;
328        if let Response::CursorPosition(x, y) = self.response()? {
329            Ok((x, y))
330        } else {
331            Err(Error::new(ErrorKind::InvalidData, "invalid device response"))
332        }
333    }
334
335    /// Reset all terminal settings to default.
336    pub fn reset(&mut self) -> Result<()> {
337        write!(self.output, "{}", Sequence::Reset)
338    }
339
340    /// Enable/disable line wrapping.
341    pub fn line_wrap(&mut self, enable: bool) -> Result<()> {
342        write!(self.output, "{}", Sequence::LineWrap(enable))
343    }
344
345    /// Set default font.
346    pub fn font_default(&mut self) -> Result<()> {
347        write!(self.output, "{}", Sequence::FontDefault)
348    }
349
350    /// Set alternate font.
351    pub fn font_alternate(&mut self) -> Result<()> {
352        write!(self.output, "{}", Sequence::FontAlternate)
353    }
354
355    /// Set the cursor position.
356    pub fn cursor_at(&mut self, row: u32, column: u32) -> Result<()> {
357        write!(self.output, "{}", Sequence::CursorAt(row, column))
358    }
359
360    /// Move the cursor position by the given delta.
361    ///
362    /// (0, 0) is at the top left of the screen.
363    pub fn cursor_move(&mut self, drow: i32, dcolumn: i32) -> Result<()> {
364        write!(self.output, "{}", Sequence::CursorMove(drow, dcolumn))
365    }
366
367    /// Save the cursor position.
368    pub fn cursor_save(&mut self) -> Result<()> {
369        write!(self.output, "{}", Sequence::CursorSave)
370    }
371
372    /// Resotre the saved cursor position.
373    pub fn cursor_restore(&mut self) -> Result<()> {
374        write!(self.output, "{}", Sequence::CursorRestore)
375    }
376
377    /// Save the cursor position.
378    pub fn cursor_save_attributes(&mut self) -> Result<()> {
379        write!(self.output, "{}", Sequence::CursorSaveAttributes)
380    }
381
382    /// Resotre the saved cursor position.
383    pub fn cursor_restore_attributes(&mut self) -> Result<()> {
384        write!(self.output, "{}", Sequence::CursorRestoreAttributes)
385    }
386
387    /// Enable scrolling.
388    ///
389    /// If `None` is passed then scrolling is enabled for the whole screen.
390    /// If `Some((start, end))` is passed then scrolling is enabled from row `start` to `end`.
391    pub fn scroll_enable(&mut self, region: Option<(u32, u32)>) -> Result<()> {
392        write!(self.output, "{}", Sequence::ScrollEnable(region))
393    }
394
395    /// Scroll by the given number of rows.
396    /// Positive rows means scrolling down, negative rows means scrolling up.
397    pub fn scroll(&mut self, rows: i32) -> Result<()> {
398        write!(self.output, "{}", Sequence::Scroll(rows))
399    }
400
401    /// Erases from the current cursor position to the end of the current line.
402    pub fn erase_end_of_line(&mut self) -> Result<()> {
403        write!(self.output, "{}", Sequence::EraseEndOfLine)
404    }
405
406    /// Erases from the current cursor position to the start of the current line.
407    pub fn erase_start_of_line(&mut self) -> Result<()> {
408        write!(self.output, "{}", Sequence::EraseStartOfLine)
409    }
410
411    /// Erases the entire current line.
412    pub fn erase_line(&mut self) -> Result<()> {
413        write!(self.output, "{}", Sequence::EraseLine)
414    }
415
416    /// Erases the screen from the current line down to the bottom of the screen.
417    pub fn erase_down(&mut self) -> Result<()> {
418        write!(self.output, "{}", Sequence::EraseDown)
419    }
420
421    /// Erases the screen from the current line up to the top of the screen.
422    pub fn erase_up(&mut self) -> Result<()> {
423        write!(self.output, "{}", Sequence::EraseUp)
424    }
425
426    /// Erases the screen with the background colour and moves the cursor to home.
427    pub fn erase_screen(&mut self) -> Result<()> {
428        write!(self.output, "{}", Sequence::EraseScreen)
429    }
430
431    /// Sets display attributes.
432    pub fn set_attributes(&mut self, attributes: &[Attribute]) -> Result<()> {
433        write!(self.output, "{}", Sequence::SetAttributes(attributes))
434    }
435}
436
437/// Read the expected byte out of the given peekable iterator, or return an error.
438fn expect_byte<I: Iterator<Item = u8>>(it: &mut Peekable<I>, expected: u8) -> Result<()> {
439    match it.next() {
440        Some(b) if b == expected => Ok(()),
441        _ => Err(Error::new(ErrorKind::InvalidData, "invalid device response"))
442    }
443}
444
445/// Read a `u32` out of the given peekable iterator, or return an error.
446fn read_u32<I: Iterator<Item = u8>>(it: &mut Peekable<I>) -> Result<u32> {
447    let mut empty = true;
448    let mut value = 0u32;
449
450    loop {
451        match it.peek() {
452            Some(b) => {
453                if *b >= 0x30 && *b <= 0x39 {
454                    let b = it.next().unwrap();
455                    empty = false;
456                    value = value * 10 + (b - 0x30) as u32;
457                } else {
458                    break
459                }
460            },
461            None => {
462                break
463            }
464        }
465    }
466
467    if empty {
468        Err(Error::new(ErrorKind::InvalidData, "invalid device response"))
469    } else {
470        Ok(value)
471    }
472}
473
474impl<I: Read, O: Write> Read for Device<I, O> {
475    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
476        let len = std::cmp::min(self.buffer.len()-self.offset, buf.len());
477        if len > 0 {
478            for i in 0..len {
479                buf[i] = self.buffer[self.offset];
480                self.offset += 1;
481            }
482
483            if self.offset >= self.buffer.len() {
484                self.offset = 0;
485                self.buffer.clear();
486            }
487        }
488
489        Ok(self.input.read(&mut buf[len..])? + len)
490    }
491}
492
493impl<I: Read, O: Write> Write for Device<I, O> {
494    fn write(&mut self, buf: &[u8]) -> Result<usize> {
495        self.output.write(buf)
496    }
497
498    fn flush(&mut self) -> Result<()> {
499        self.output.flush()
500    }
501}