1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 13:52:55 +03:00

Wrap up synchronized output handling, parser changes

This commit hooks up DECRQM so that we can report that we implement
synchronized updates, and then refines the code that manages sending
data to the terminal model; the first cut at synchronized updates
was a bit simplistic, and now we make a point of "flushing" pending
actions when we start a sync point, and again as soon as we release
the sync point.

This smooths out the jaggies around the orca that I mentioned in
dcbbda7702

and while testing this, I realized that recent parser changes had
mangled processing bundled dec private mode sequences where multiple
modes were specified in the same overall escape sequence.  I've
added the missing unit test case for this and made that work again.

refs: https://github.com/wez/wezterm/issues/955
refs: https://github.com/wez/wezterm/issues/882
This commit is contained in:
Wez Furlong 2021-07-24 17:01:21 -07:00
parent a249021920
commit 365a68dfb8
5 changed files with 287 additions and 76 deletions

View File

@ -116,23 +116,35 @@ fn parse_buffered_data(pane_id: PaneId, dead: &Arc<AtomicBool>, mut rx: FileDesc
}
Ok(size) => {
parser.parse(&buf[0..size], |action| {
let mut flush = false;
match &action {
Action::CSI(CSI::Mode(Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SynchronizedOutput,
)))) => {
hold = true;
// Flush prior actions
if !actions.is_empty() {
send_actions_to_mux(pane_id, dead, std::mem::take(&mut actions));
}
}
Action::CSI(CSI::Mode(Mode::ResetDecPrivateMode(
DecPrivateMode::Code(DecPrivateModeCode::SynchronizedOutput),
))) => {
hold = false;
flush = true;
}
Action::CSI(CSI::Device(dev)) if matches!(**dev, Device::SoftReset) => {
hold = false;
flush = true;
}
_ => {}
};
actions.push(action)
actions.push(action);
if flush && !actions.is_empty() {
send_actions_to_mux(pane_id, dead, std::mem::take(&mut actions));
}
});
if !actions.is_empty() && !hold {
send_actions_to_mux(pane_id, dead, std::mem::take(&mut actions));

View File

@ -7,7 +7,7 @@ use anyhow::bail;
use image::imageops::FilterType;
use image::ImageFormat;
use log::{debug, error};
use num_traits::FromPrimitive;
use num_traits::{FromPrimitive, ToPrimitive};
use ordered_float::NotNan;
use std::collections::HashMap;
use std::fmt::Write;
@ -1869,6 +1869,37 @@ impl TerminalState {
}
}
fn decqrm_response(&mut self, mode: Mode, mut recognized: bool, enabled: bool) {
let (is_dec, number) = match &mode {
Mode::QueryDecPrivateMode(DecPrivateMode::Code(code)) => (true, code.to_u16().unwrap()),
Mode::QueryDecPrivateMode(DecPrivateMode::Unspecified(code)) => {
recognized = false;
(true, *code)
}
Mode::QueryMode(TerminalMode::Code(code)) => (false, code.to_u16().unwrap()),
Mode::QueryMode(TerminalMode::Unspecified(code)) => {
recognized = false;
(false, *code)
}
_ => unreachable!(),
};
let prefix = if is_dec { "?" } else { "" };
let status = if recognized {
if enabled {
1 // set
} else {
2 // reset
}
} else {
0
};
log::trace!("{:?} -> recognized={} status={}", mode, recognized, status);
write!(self.writer, "\x1b[{}{};{}$y", prefix, number, status).ok();
}
fn perform_csi_mode(&mut self, mode: Mode) {
match mode {
Mode::SetDecPrivateMode(DecPrivateMode::Code(
@ -1877,6 +1908,11 @@ impl TerminalState {
| Mode::ResetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::StartBlinkingCursor,
)) => {}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::StartBlinkingCursor,
)) => {
self.decqrm_response(mode, true, false);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoRepeat))
| Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoRepeat)) => {
@ -1895,6 +1931,12 @@ impl TerminalState {
self.reverse_wraparound_mode = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ReverseWraparound,
)) => {
self.decqrm_response(mode, true, self.reverse_wraparound_mode);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::LeftRightMarginMode,
)) => {
@ -1908,6 +1950,12 @@ impl TerminalState {
self.left_and_right_margins = 0..self.screen().physical_cols;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::LeftRightMarginMode,
)) => {
self.decqrm_response(mode, true, self.left_and_right_margin_mode);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SaveCursor)) => {
self.dec_save_cursor();
}
@ -1924,6 +1972,10 @@ impl TerminalState {
self.dec_auto_wrap = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoWrap)) => {
self.decqrm_response(mode, true, self.dec_auto_wrap);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::OriginMode)) => {
self.dec_origin_mode = true;
self.set_cursor_pos(&Position::Absolute(0), &Position::Absolute(0));
@ -1934,6 +1986,10 @@ impl TerminalState {
self.set_cursor_pos(&Position::Absolute(0), &Position::Absolute(0));
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::OriginMode)) => {
self.decqrm_response(mode, true, self.dec_origin_mode);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic,
)) => {
@ -1944,6 +2000,15 @@ impl TerminalState {
)) => {
self.use_private_color_registers_for_each_graphic = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic,
)) => {
self.decqrm_response(
mode,
true,
self.use_private_color_registers_for_each_graphic,
);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SynchronizedOutput,
@ -1955,6 +2020,13 @@ impl TerminalState {
)) => {
// This is handled in wezterm's mux
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SynchronizedOutput,
)) => {
// This is handled in wezterm's mux; if we get here, then it isn't enabled,
// so we always report false
self.decqrm_response(mode, true, false);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SmoothScroll))
| Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SmoothScroll)) => {
@ -1988,6 +2060,9 @@ impl TerminalState {
Mode::ResetMode(TerminalMode::Code(TerminalModeCode::Insert)) => {
self.insert = false;
}
Mode::QueryMode(TerminalMode::Code(TerminalModeCode::Insert)) => {
self.decqrm_response(mode, true, self.insert);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
self.bracketed_paste = true;
@ -1995,6 +2070,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
self.bracketed_paste = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
self.decqrm_response(mode, true, self.bracketed_paste);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::OptEnableAlternateScreen,
@ -2036,6 +2114,11 @@ impl TerminalState {
)) => {
self.application_cursor_keys = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ApplicationCursorKeys,
)) => {
self.decqrm_response(mode, true, self.application_cursor_keys);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => {
self.sixel_scrolling = true;
@ -2043,6 +2126,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => {
self.sixel_scrolling = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => {
self.decqrm_response(mode, true, self.sixel_scrolling);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => {
self.dec_ansi_mode = true;
@ -2050,6 +2136,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => {
self.dec_ansi_mode = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => {
self.decqrm_response(mode, true, self.dec_ansi_mode);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => {
self.cursor_visible = true;
@ -2057,6 +2146,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => {
self.cursor_visible = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => {
self.decqrm_response(mode, true, self.cursor_visible);
}
Mode::SetMode(TerminalMode::Code(TerminalModeCode::ShowCursor)) => {
self.cursor_visible = true;
}
@ -2072,6 +2164,9 @@ impl TerminalState {
self.mouse_tracking = false;
self.last_mouse_move.take();
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::MouseTracking)) => {
self.decqrm_response(mode, true, self.mouse_tracking);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::HighlightMouseTracking,
@ -2090,6 +2185,11 @@ impl TerminalState {
self.button_event_mouse = false;
self.last_mouse_move.take();
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ButtonEventMouse,
)) => {
self.decqrm_response(mode, true, self.button_event_mouse);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AnyEventMouse)) => {
self.any_event_mouse = true;
@ -2099,6 +2199,9 @@ impl TerminalState {
self.any_event_mouse = false;
self.last_mouse_move.take();
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AnyEventMouse)) => {
self.decqrm_response(mode, true, self.any_event_mouse);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::FocusTracking)) => {
self.focus_tracking = true;
@ -2108,6 +2211,9 @@ impl TerminalState {
self.focus_tracking = false;
self.last_mouse_move.take();
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::FocusTracking)) => {
self.decqrm_response(mode, true, self.focus_tracking);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SGRMouse)) => {
self.sgr_mouse = true;
@ -2117,6 +2223,9 @@ impl TerminalState {
self.sgr_mouse = false;
self.last_mouse_move.take();
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SGRMouse)) => {
self.decqrm_response(mode, true, self.sgr_mouse);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SixelScrollsRight,
@ -2128,6 +2237,11 @@ impl TerminalState {
)) => {
self.sixel_scrolls_right = false;
}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SixelScrollsRight,
)) => {
self.decqrm_response(mode, true, self.sixel_scrolls_right);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ClearAndEnableAlternateScreen,
@ -2153,25 +2267,24 @@ impl TerminalState {
log::warn!("save/restore dec mode {:?} unimplemented", n)
}
Mode::SetDecPrivateMode(DecPrivateMode::Unspecified(n))
| Mode::ResetDecPrivateMode(DecPrivateMode::Unspecified(n))
| Mode::SaveDecPrivateMode(DecPrivateMode::Unspecified(n))
| Mode::RestoreDecPrivateMode(DecPrivateMode::Unspecified(n)) => {
log::warn!("unhandled DecPrivateMode {}", n);
Mode::SetDecPrivateMode(DecPrivateMode::Unspecified(_))
| Mode::ResetDecPrivateMode(DecPrivateMode::Unspecified(_))
| Mode::SaveDecPrivateMode(DecPrivateMode::Unspecified(_))
| Mode::RestoreDecPrivateMode(DecPrivateMode::Unspecified(_)) => {
log::warn!("unhandled DecPrivateMode {:?}", mode);
}
Mode::SetMode(TerminalMode::Unspecified(n))
| Mode::ResetMode(TerminalMode::Unspecified(n)) => {
log::warn!("unhandled TerminalMode {}", n);
}
Mode::SetMode(m) | Mode::ResetMode(m) => {
log::warn!("unhandled TerminalMode {:?}", m);
mode @ Mode::SetMode(_) | mode @ Mode::ResetMode(_) => {
log::warn!("unhandled {:?}", mode);
}
Mode::XtermKeyMode { resource, value } => {
log::warn!("unhandled XtermKeyMode {:?} {:?}", resource, value);
}
Mode::QueryDecPrivateMode(_) | Mode::QueryMode(_) => {
self.decqrm_response(mode, false, false);
}
}
}

View File

@ -247,6 +247,7 @@ impl XtSmGraphics {
}
pub fn parse(params: &[CsiParam]) -> Result<CSI, ()> {
let params = Cracked::parse(&params[1..])?;
Ok(CSI::Device(Box::new(Device::XtSmGraphics(XtSmGraphics {
item: match params.get(0).ok_or(())? {
CsiParam::Integer(1) => XtSmGraphicsItem::NumberOfColorRegisters,
@ -259,10 +260,10 @@ impl XtSmGraphics {
CsiParam::Integer(n) => *n,
_ => return Err(()),
},
value: params[2..]
value: params.params[2..]
.iter()
.filter_map(|p| match p {
CsiParam::Integer(n) => Some(*n),
Some(CsiParam::Integer(n)) => Some(*n),
_ => None,
})
.collect(),
@ -561,8 +562,10 @@ pub enum Mode {
ResetDecPrivateMode(DecPrivateMode),
SaveDecPrivateMode(DecPrivateMode),
RestoreDecPrivateMode(DecPrivateMode),
QueryDecPrivateMode(DecPrivateMode),
SetMode(TerminalMode),
ResetMode(TerminalMode),
QueryMode(TerminalMode),
XtermKeyMode {
resource: XtermKeyModifierResource,
value: Option<i64>,
@ -594,8 +597,18 @@ impl Display for Mode {
Mode::ResetDecPrivateMode(mode) => emit!("l", mode),
Mode::SaveDecPrivateMode(mode) => emit!("s", mode),
Mode::RestoreDecPrivateMode(mode) => emit!("r", mode),
Mode::QueryDecPrivateMode(DecPrivateMode::Code(mode)) => {
write!(f, "?{}$p", mode.to_u16().ok_or_else(|| FmtError)?)
}
Mode::QueryDecPrivateMode(DecPrivateMode::Unspecified(mode)) => {
write!(f, "?{}$p", mode)
}
Mode::SetMode(mode) => emit_mode!("h", mode),
Mode::ResetMode(mode) => emit_mode!("l", mode),
Mode::QueryMode(TerminalMode::Code(mode)) => {
write!(f, "?{}$p", mode.to_u16().ok_or_else(|| FmtError)?)
}
Mode::QueryMode(TerminalMode::Unspecified(mode)) => write!(f, "?{}$p", mode),
Mode::XtermKeyMode { resource, value } => {
write!(
f,
@ -1378,6 +1391,7 @@ struct CSIParser<'a> {
/// default values, especially for SGR, so we need to be careful not
/// to update params to an empty slice.
params: Option<&'a [CsiParam]>,
orig_params: &'a [CsiParam],
}
impl CSI {
@ -1396,6 +1410,7 @@ impl CSI {
parameters_truncated,
control,
params: Some(params),
orig_params: params,
}
}
}
@ -1502,7 +1517,7 @@ macro_rules! parse {
impl<'a> CSIParser<'a> {
fn parse_next(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
match (self.control, params) {
match (self.control, self.orig_params) {
('q', [.., CsiParam::P(b' ')]) => self.cursor_style(params),
('y', [.., CsiParam::P(b'*')]) => self.checksum_area(params),
@ -1522,76 +1537,82 @@ impl<'a> CSIParser<'a> {
.map(|dev| CSI::Device(Box::new(dev))),
('S', [CsiParam::P(b'?'), ..]) => XtSmGraphics::parse(params),
('p', [CsiParam::Integer(_), CsiParam::P(b'$')])
| ('p', [CsiParam::P(b'?'), CsiParam::Integer(_), CsiParam::P(b'$')]) => {
self.decrqm(params)
}
('h', [CsiParam::P(b'?'), ..]) => self
.dec(params)
.dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::SetDecPrivateMode(mode))),
('l', [CsiParam::P(b'?'), ..]) => self
.dec(params)
.dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::ResetDecPrivateMode(mode))),
('r', [CsiParam::P(b'?'), ..]) => self
.dec(params)
.dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::RestoreDecPrivateMode(mode))),
('q', [CsiParam::P(b'>'), ..]) => self
.req_terminal_name_and_version(params)
.map(|dev| CSI::Device(Box::new(dev))),
('s', [CsiParam::P(b'?'), ..]) => self
.dec(params)
.dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::SaveDecPrivateMode(mode))),
('m', [CsiParam::P(b'>'), ..]) => self.xterm_key_modifier(params),
('p', [CsiParam::P(b'!')]) => Ok(CSI::Device(Box::new(Device::SoftReset))),
('c', _) => self
.req_primary_device_attributes(params)
.map(|dev| CSI::Device(Box::new(dev))),
_ => match self.control {
'c' => self
.req_primary_device_attributes(params)
.map(|dev| CSI::Device(Box::new(dev))),
('@', _) => parse!(Edit, InsertCharacter, params),
('`', _) => parse!(Cursor, CharacterPositionAbsolute, params),
('A', _) => parse!(Cursor, Up, params),
('B', _) => parse!(Cursor, Down, params),
('C', _) => parse!(Cursor, Right, params),
('D', _) => parse!(Cursor, Left, params),
('E', _) => parse!(Cursor, NextLine, params),
('F', _) => parse!(Cursor, PrecedingLine, params),
('G', _) => parse!(Cursor, CharacterAbsolute, params),
('H', _) => parse!(Cursor, Position, line, col, params),
('I', _) => parse!(Cursor, ForwardTabulation, params),
('J', _) => parse!(Edit, EraseInDisplay, params),
('K', _) => parse!(Edit, EraseInLine, params),
('L', _) => parse!(Edit, InsertLine, params),
('M', _) => parse!(Edit, DeleteLine, params),
('P', _) => parse!(Edit, DeleteCharacter, params),
('R', _) => parse!(Cursor, ActivePositionReport, line, col, params),
('S', _) => parse!(Edit, ScrollUp, params),
('T', _) => parse!(Edit, ScrollDown, params),
('W', _) => parse!(Cursor, TabulationControl, params),
('X', _) => parse!(Edit, EraseCharacter, params),
('Y', _) => parse!(Cursor, LineTabulation, params),
('Z', _) => parse!(Cursor, BackwardTabulation, params),
'@' => parse!(Edit, InsertCharacter, params),
'`' => parse!(Cursor, CharacterPositionAbsolute, params),
'A' => parse!(Cursor, Up, params),
'B' => parse!(Cursor, Down, params),
'C' => parse!(Cursor, Right, params),
'D' => parse!(Cursor, Left, params),
'E' => parse!(Cursor, NextLine, params),
'F' => parse!(Cursor, PrecedingLine, params),
'G' => parse!(Cursor, CharacterAbsolute, params),
'H' => parse!(Cursor, Position, line, col, params),
'I' => parse!(Cursor, ForwardTabulation, params),
'J' => parse!(Edit, EraseInDisplay, params),
'K' => parse!(Edit, EraseInLine, params),
'L' => parse!(Edit, InsertLine, params),
'M' => parse!(Edit, DeleteLine, params),
'P' => parse!(Edit, DeleteCharacter, params),
'R' => parse!(Cursor, ActivePositionReport, line, col, params),
'S' => parse!(Edit, ScrollUp, params),
'T' => parse!(Edit, ScrollDown, params),
'W' => parse!(Cursor, TabulationControl, params),
'X' => parse!(Edit, EraseCharacter, params),
'Y' => parse!(Cursor, LineTabulation, params),
'Z' => parse!(Cursor, BackwardTabulation, params),
('a', _) => parse!(Cursor, CharacterPositionForward, params),
('b', _) => parse!(Edit, Repeat, params),
('d', _) => parse!(Cursor, LinePositionAbsolute, params),
('e', _) => parse!(Cursor, LinePositionForward, params),
('f', _) => parse!(Cursor, CharacterAndLinePosition, line, col, params),
('g', _) => parse!(Cursor, TabulationClear, params),
('h', _) => self
.terminal_mode(params)
.map(|mode| CSI::Mode(Mode::SetMode(mode))),
('j', _) => parse!(Cursor, CharacterPositionBackward, params),
('k', _) => parse!(Cursor, LinePositionBackward, params),
('l', _) => self
.terminal_mode(params)
.map(|mode| CSI::Mode(Mode::ResetMode(mode))),
'a' => parse!(Cursor, CharacterPositionForward, params),
'b' => parse!(Edit, Repeat, params),
'd' => parse!(Cursor, LinePositionAbsolute, params),
'e' => parse!(Cursor, LinePositionForward, params),
'f' => parse!(Cursor, CharacterAndLinePosition, line, col, params),
'g' => parse!(Cursor, TabulationClear, params),
'h' => self
.terminal_mode(params)
.map(|mode| CSI::Mode(Mode::SetMode(mode))),
'j' => parse!(Cursor, CharacterPositionBackward, params),
'k' => parse!(Cursor, LinePositionBackward, params),
'l' => self
.terminal_mode(params)
.map(|mode| CSI::Mode(Mode::ResetMode(mode))),
('m', _) => self.sgr(params).map(CSI::Sgr),
('n', _) => self.dsr(params),
('r', _) => self.decstbm(params),
('s', _) => self.decslrm(params),
('t', _) => self.window(params).map(CSI::Window),
('u', _) => noparams!(Cursor, RestoreCursor, params),
'm' => self.sgr(params).map(CSI::Sgr),
'n' => self.dsr(params),
'r' => self.decstbm(params),
's' => self.decslrm(params),
't' => self.window(params).map(CSI::Window),
'u' => noparams!(Cursor, RestoreCursor, params),
_ => Err(()),
_ => Err(()),
},
}
}
@ -1613,6 +1634,15 @@ impl<'a> CSIParser<'a> {
result
}
fn focus(&self, params: &'a [CsiParam], from_start: usize, from_end: usize) -> &'a [CsiParam] {
if params == self.orig_params {
let len = params.len();
&params[from_start..len - from_end]
} else {
params
}
}
fn cursor_style(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
match params {
[CsiParam::Integer(p), CsiParam::P(b' ')] => match FromPrimitive::from_i64(*p) {
@ -1755,10 +1785,10 @@ impl<'a> CSIParser<'a> {
fn req_terminal_name_and_version(&mut self, params: &'a [CsiParam]) -> Result<Device, ()> {
match params {
[] => Ok(Device::RequestTerminalNameAndVersion),
[_] => Ok(Device::RequestTerminalNameAndVersion),
[CsiParam::Integer(0)] => {
Ok(self.advance_by(1, params, Device::RequestTerminalNameAndVersion))
[_, CsiParam::Integer(0)] => {
Ok(self.advance_by(2, params, Device::RequestTerminalNameAndVersion))
}
_ => Err(()),
}
@ -1887,15 +1917,33 @@ impl<'a> CSIParser<'a> {
))
}
fn decrqm(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
Ok(CSI::Mode(match params {
[CsiParam::Integer(p), CsiParam::P(b'$')] => {
Mode::QueryMode(match FromPrimitive::from_i64(*p) {
None => TerminalMode::Unspecified(p.to_u16().ok_or(())?),
Some(mode) => TerminalMode::Code(mode),
})
}
[CsiParam::P(b'?'), CsiParam::Integer(p), CsiParam::P(b'$')] => {
Mode::QueryDecPrivateMode(match FromPrimitive::from_i64(*p) {
None => DecPrivateMode::Unspecified(p.to_u16().ok_or(())?),
Some(mode) => DecPrivateMode::Code(mode),
})
}
_ => return Err(()),
}))
}
fn dec(&mut self, params: &'a [CsiParam]) -> Result<DecPrivateMode, ()> {
match params {
[CsiParam::P(b'?'), CsiParam::Integer(p0), ..] => match FromPrimitive::from_i64(*p0) {
[CsiParam::Integer(p0), ..] => match FromPrimitive::from_i64(*p0) {
None => Ok(self.advance_by(
2,
1,
params,
DecPrivateMode::Unspecified(p0.to_u16().ok_or(())?),
)),
Some(mode) => Ok(self.advance_by(2, params, DecPrivateMode::Code(mode))),
Some(mode) => Ok(self.advance_by(1, params, DecPrivateMode::Code(mode))),
},
_ => Err(()),
}

View File

@ -429,7 +429,8 @@ mod test {
use crate::cell::{Intensity, Underline};
use crate::color::ColorSpec;
use crate::escape::csi::{
DecPrivateMode, DecPrivateModeCode, Device, Mode, Sgr, Window, XtermKeyModifierResource,
DecPrivateMode, DecPrivateModeCode, Device, Mode, Sgr, Window, XtSmGraphics,
XtSmGraphicsItem, XtermKeyModifierResource,
};
use crate::escape::{EscCode, OneBased};
use std::io::Write;
@ -730,6 +731,14 @@ mod test {
actions
}
fn parse_as(s: &str, expected: &str) -> Vec<Action> {
let mut p = Parser::new();
let actions = p.parse_as_vec(s.as_bytes());
println!("actions: {:?}", actions);
assert_eq!(expected, encode(&actions));
actions
}
#[test]
fn xterm_key() {
assert_eq!(
@ -764,6 +773,35 @@ mod test {
);
}
#[test]
fn dec_private_modes() {
assert_eq!(
parse_as("\x1b[?1;1006h", "\x1b[?1h\x1b[?1006h"),
vec![
Action::CSI(CSI::Mode(Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ApplicationCursorKeys
),))),
Action::CSI(CSI::Mode(Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SGRMouse
),))),
]
);
}
#[test]
fn xtsmgraphics() {
assert_eq!(
round_trip_parse("\x1b[?1;3;256S"),
vec![Action::CSI(CSI::Device(Box::new(Device::XtSmGraphics(
XtSmGraphics {
item: XtSmGraphicsItem::NumberOfColorRegisters,
action_or_status: 3,
value: vec![256]
}
))))]
);
}
#[test]
fn req_attr() {
assert_eq!(

View File

@ -353,7 +353,7 @@ impl FontConfigInner {
}
.show();
} else {
log::warn!(
log::debug!(
"No fonts contain glyphs for these codepoints: {}",
fallback_str.escape_unicode()
);