Implement Handler::identify_terminal for Term

The identify_terminal function signature had to change to support
writing to the terminal before processing additional input.
This commit is contained in:
Joe Wilm 2016-12-04 15:48:30 -08:00
parent 61c1e37c46
commit ed48d08c03
4 changed files with 79 additions and 22 deletions

View File

@ -31,6 +31,7 @@
//! should be, feel free to add it. Please try not to become overzealous and adding support for //! should be, feel free to add it. Please try not to become overzealous and adding support for
//! sequences only used by folks trapped in 1988. //! sequences only used by folks trapped in 1988.
use std::ops::Range; use std::ops::Range;
use std::io;
use vte; use vte;
@ -51,18 +52,24 @@ struct ProcessorState;
/// ///
/// Processor creates a Performer when running advance and passes the Performer /// Processor creates a Performer when running advance and passes the Performer
/// to vte::Parser. /// to vte::Parser.
struct Performer<'a, H: Handler + TermInfo + 'a> { struct Performer<'a, H: Handler + TermInfo + 'a, W: io::Write + 'a> {
_state: &'a mut ProcessorState, _state: &'a mut ProcessorState,
handler: &'a mut H handler: &'a mut H,
writer: &'a mut W
} }
impl<'a, H: Handler + TermInfo + 'a> Performer<'a, H> { impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
/// Create a performer /// Create a performer
#[inline] #[inline]
pub fn new<'b>(state: &'b mut ProcessorState, handler: &'b mut H) -> Performer<'b, H> { pub fn new<'b>(
state: &'b mut ProcessorState,
handler: &'b mut H,
writer: &'b mut W,
) -> Performer<'b, H, W> {
Performer { Performer {
_state: state, _state: state,
handler: handler handler: handler,
writer: writer,
} }
} }
} }
@ -76,8 +83,16 @@ impl Processor {
} }
#[inline] #[inline]
pub fn advance<H: Handler + TermInfo>(&mut self, handler: &mut H, byte: u8) { pub fn advance<H, W>(
let mut performer = Performer::new(&mut self.state, handler); &mut self,
handler: &mut H,
byte: u8,
writer: &mut W
)
where H: Handler + TermInfo,
W: io::Write
{
let mut performer = Performer::new(&mut self.state, handler, writer);
self.parser.advance(&mut performer, byte); self.parser.advance(&mut performer, byte);
} }
} }
@ -116,7 +131,9 @@ pub trait Handler {
fn move_down(&mut self, Line) {} fn move_down(&mut self, Line) {}
/// Identify the terminal (should write back to the pty stream) /// Identify the terminal (should write back to the pty stream)
fn identify_terminal(&mut self) {} ///
/// TODO this should probably return an io::Result
fn identify_terminal<W: io::Write>(&mut self, &mut W) {}
/// Move cursor forward `cols` /// Move cursor forward `cols`
fn move_forward(&mut self, Column) {} fn move_forward(&mut self, Column) {}
@ -415,7 +432,10 @@ pub enum Attr {
Background(Color), Background(Color),
} }
impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> { impl<'a, H, W> vte::Perform for Performer<'a, H, W>
where H: Handler + TermInfo + 'a,
W: io::Write + 'a
{
#[inline] #[inline]
fn print(&mut self, c: char) { fn print(&mut self, c: char) {
self.handler.input(c); self.handler.input(c);
@ -432,7 +452,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
C0::SUB => self.handler.substitute(), C0::SUB => self.handler.substitute(),
C1::NEL => self.handler.newline(), C1::NEL => self.handler.newline(),
C1::HTS => self.handler.set_horizontal_tabstop(), C1::HTS => self.handler.set_horizontal_tabstop(),
C1::DECID => self.handler.identify_terminal(), C1::DECID => self.handler.identify_terminal(self.writer),
_ => err_println!("[unhandled] execute byte={:02x}", byte) _ => err_println!("[unhandled] execute byte={:02x}", byte)
} }
} }
@ -469,9 +489,16 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
} }
#[inline] #[inline]
fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) { fn csi_dispatch(
&mut self,
args: &[i64],
intermediates: &[u8],
_ignore: bool,
action: char
) {
let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false); let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
let handler = &mut self.handler; let handler = &mut self.handler;
let writer = &mut self.writer;
macro_rules! unhandled { macro_rules! unhandled {
@ -500,7 +527,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize)); handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize));
}, },
'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'c' => handler.identify_terminal(), 'c' => handler.identify_terminal(writer),
'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)), 'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'D' => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)), 'D' => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'E' => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'E' => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
@ -650,7 +677,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
i += 1; // C-for expr i += 1; // C-for expr
} }
} }
'n' => handler.identify_terminal(), 'n' => handler.identify_terminal(writer),
'r' => { 'r' => {
if private { if private {
unhandled!(); unhandled!();
@ -679,7 +706,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
b'E' => self.handler.newline(), b'E' => self.handler.newline(),
b'H' => self.handler.set_horizontal_tabstop(), b'H' => self.handler.set_horizontal_tabstop(),
b'M' => self.handler.reverse_index(), b'M' => self.handler.reverse_index(),
b'Z' => self.handler.identify_terminal(), b'Z' => self.handler.identify_terminal(self.writer),
b'c' => self.handler.reset_state(), b'c' => self.handler.reset_state(),
b'7' => self.handler.save_cursor_position(), b'7' => self.handler.save_cursor_position(),
b'8' => self.handler.restore_cursor_position(), b'8' => self.handler.restore_cursor_position(),
@ -899,10 +926,26 @@ pub mod C1 {
// Byte sequences used in these tests are recording of pty stdout. // Byte sequences used in these tests are recording of pty stdout.
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io;
use index::{Line, Column}; use index::{Line, Column};
use super::{Processor, Handler, Attr, TermInfo, Color}; use super::{Processor, Handler, Attr, TermInfo, Color};
use ::Rgb; use ::Rgb;
/// The /dev/null of io::Write
struct Void;
impl io::Write for Void {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Default)] #[derive(Default)]
struct AttrHandler { struct AttrHandler {
attr: Option<Attr>, attr: Option<Attr>,
@ -934,7 +977,7 @@ mod tests {
let mut handler = AttrHandler::default(); let mut handler = AttrHandler::default();
for byte in &BYTES[..] { for byte in &BYTES[..] {
parser.advance(&mut handler, *byte); parser.advance(&mut handler, *byte, &mut Void);
} }
assert_eq!(handler.attr, Some(Attr::Bold)); assert_eq!(handler.attr, Some(Attr::Bold));
@ -951,7 +994,7 @@ mod tests {
let mut handler = AttrHandler::default(); let mut handler = AttrHandler::default();
for byte in &BYTES[..] { for byte in &BYTES[..] {
parser.advance(&mut handler, *byte); parser.advance(&mut handler, *byte, &mut Void);
} }
let spec = Rgb { let spec = Rgb {
@ -986,7 +1029,7 @@ mod tests {
let mut parser = Processor::new(); let mut parser = Processor::new();
for byte in &BYTES[..] { for byte in &BYTES[..] {
parser.advance(&mut handler, *byte); parser.advance(&mut handler, *byte, &mut Void);
} }
} }
} }

View File

@ -189,7 +189,7 @@ impl<Io> EventLoop<Io>
let mut terminal = self.terminal.lock(); let mut terminal = self.terminal.lock();
for byte in &buf[..got] { for byte in &buf[..got] {
state.parser.advance(&mut *terminal, *byte); state.parser.advance(&mut *terminal, *byte, &mut self.pty);
} }
terminal.dirty = true; terminal.dirty = true;

View File

@ -16,6 +16,7 @@
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
use std::ptr; use std::ptr;
use std::cmp; use std::cmp;
use std::io;
use ansi::{self, Attr, Handler}; use ansi::{self, Attr, Handler};
use grid::{Grid, ClearRegion}; use grid::{Grid, ClearRegion};
@ -557,8 +558,8 @@ impl ansi::Handler for Term {
} }
#[inline] #[inline]
fn identify_terminal(&mut self) { fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
err_println!("[unimplemented] identify_terminal"); let _ = writer.write_all("\x1b[?6c".as_bytes());
} }
#[inline] #[inline]

View File

@ -4,7 +4,7 @@ extern crate serde_json;
/// ref tests /// ref tests
mod reference { mod reference {
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::{self, Read};
use std::path::Path; use std::path::Path;
use serde_json as json; use serde_json as json;
@ -15,6 +15,19 @@ mod reference {
use alacritty::term::SizeInfo; use alacritty::term::SizeInfo;
use alacritty::ansi; use alacritty::ansi;
/// The /dev/null of io::Write
struct Void;
impl io::Write for Void {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
macro_rules! ref_file { macro_rules! ref_file {
($ref_name:ident, $file:expr) => { ($ref_name:ident, $file:expr) => {
concat!( concat!(
@ -59,7 +72,7 @@ mod reference {
let mut parser = ansi::Processor::new(); let mut parser = ansi::Processor::new();
for byte in recording { for byte in recording {
parser.advance(&mut terminal, byte); parser.advance(&mut terminal, byte, &mut Void);
} }
assert_eq!(grid, *terminal.grid()); assert_eq!(grid, *terminal.grid());