1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 22:01:47 +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) => { Ok(size) => {
parser.parse(&buf[0..size], |action| { parser.parse(&buf[0..size], |action| {
let mut flush = false;
match &action { match &action {
Action::CSI(CSI::Mode(Mode::SetDecPrivateMode(DecPrivateMode::Code( Action::CSI(CSI::Mode(Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SynchronizedOutput, DecPrivateModeCode::SynchronizedOutput,
)))) => { )))) => {
hold = true; 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( Action::CSI(CSI::Mode(Mode::ResetDecPrivateMode(
DecPrivateMode::Code(DecPrivateModeCode::SynchronizedOutput), DecPrivateMode::Code(DecPrivateModeCode::SynchronizedOutput),
))) => { ))) => {
hold = false; hold = false;
flush = true;
} }
Action::CSI(CSI::Device(dev)) if matches!(**dev, Device::SoftReset) => { Action::CSI(CSI::Device(dev)) if matches!(**dev, Device::SoftReset) => {
hold = false; 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 { if !actions.is_empty() && !hold {
send_actions_to_mux(pane_id, dead, std::mem::take(&mut actions)); 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::imageops::FilterType;
use image::ImageFormat; use image::ImageFormat;
use log::{debug, error}; use log::{debug, error};
use num_traits::FromPrimitive; use num_traits::{FromPrimitive, ToPrimitive};
use ordered_float::NotNan; use ordered_float::NotNan;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; 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) { fn perform_csi_mode(&mut self, mode: Mode) {
match mode { match mode {
Mode::SetDecPrivateMode(DecPrivateMode::Code( Mode::SetDecPrivateMode(DecPrivateMode::Code(
@ -1877,6 +1908,11 @@ impl TerminalState {
| Mode::ResetDecPrivateMode(DecPrivateMode::Code( | Mode::ResetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::StartBlinkingCursor, DecPrivateModeCode::StartBlinkingCursor,
)) => {} )) => {}
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::StartBlinkingCursor,
)) => {
self.decqrm_response(mode, true, false);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoRepeat)) Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoRepeat))
| Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoRepeat)) => { | Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AutoRepeat)) => {
@ -1895,6 +1931,12 @@ impl TerminalState {
self.reverse_wraparound_mode = false; self.reverse_wraparound_mode = false;
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ReverseWraparound,
)) => {
self.decqrm_response(mode, true, self.reverse_wraparound_mode);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::LeftRightMarginMode, DecPrivateModeCode::LeftRightMarginMode,
)) => { )) => {
@ -1908,6 +1950,12 @@ impl TerminalState {
self.left_and_right_margins = 0..self.screen().physical_cols; 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SaveCursor)) => {
self.dec_save_cursor(); self.dec_save_cursor();
} }
@ -1924,6 +1972,10 @@ impl TerminalState {
self.dec_auto_wrap = false; 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::OriginMode)) => {
self.dec_origin_mode = true; self.dec_origin_mode = true;
self.set_cursor_pos(&Position::Absolute(0), &Position::Absolute(0)); 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)); 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( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic, DecPrivateModeCode::UsePrivateColorRegistersForEachGraphic,
)) => { )) => {
@ -1944,6 +2000,15 @@ impl TerminalState {
)) => { )) => {
self.use_private_color_registers_for_each_graphic = false; 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( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SynchronizedOutput, DecPrivateModeCode::SynchronizedOutput,
@ -1955,6 +2020,13 @@ impl TerminalState {
)) => { )) => {
// This is handled in wezterm's mux // 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::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SmoothScroll))
| Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SmoothScroll)) => { | Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SmoothScroll)) => {
@ -1988,6 +2060,9 @@ impl TerminalState {
Mode::ResetMode(TerminalMode::Code(TerminalModeCode::Insert)) => { Mode::ResetMode(TerminalMode::Code(TerminalModeCode::Insert)) => {
self.insert = false; self.insert = false;
} }
Mode::QueryMode(TerminalMode::Code(TerminalModeCode::Insert)) => {
self.decqrm_response(mode, true, self.insert);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
self.bracketed_paste = true; self.bracketed_paste = true;
@ -1995,6 +2070,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => { Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
self.bracketed_paste = false; self.bracketed_paste = false;
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
self.decqrm_response(mode, true, self.bracketed_paste);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::OptEnableAlternateScreen, DecPrivateModeCode::OptEnableAlternateScreen,
@ -2036,6 +2114,11 @@ impl TerminalState {
)) => { )) => {
self.application_cursor_keys = false; 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => {
self.sixel_scrolling = true; self.sixel_scrolling = true;
@ -2043,6 +2126,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => { Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => {
self.sixel_scrolling = false; self.sixel_scrolling = false;
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SixelScrolling)) => {
self.decqrm_response(mode, true, self.sixel_scrolling);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => {
self.dec_ansi_mode = true; self.dec_ansi_mode = true;
@ -2050,6 +2136,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => { Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::DecAnsiMode)) => {
self.dec_ansi_mode = false; 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => {
self.cursor_visible = true; self.cursor_visible = true;
@ -2057,6 +2146,9 @@ impl TerminalState {
Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => { Mode::ResetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => {
self.cursor_visible = false; self.cursor_visible = false;
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::ShowCursor)) => {
self.decqrm_response(mode, true, self.cursor_visible);
}
Mode::SetMode(TerminalMode::Code(TerminalModeCode::ShowCursor)) => { Mode::SetMode(TerminalMode::Code(TerminalModeCode::ShowCursor)) => {
self.cursor_visible = true; self.cursor_visible = true;
} }
@ -2072,6 +2164,9 @@ impl TerminalState {
self.mouse_tracking = false; self.mouse_tracking = false;
self.last_mouse_move.take(); self.last_mouse_move.take();
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::MouseTracking)) => {
self.decqrm_response(mode, true, self.mouse_tracking);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::HighlightMouseTracking, DecPrivateModeCode::HighlightMouseTracking,
@ -2090,6 +2185,11 @@ impl TerminalState {
self.button_event_mouse = false; self.button_event_mouse = false;
self.last_mouse_move.take(); 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::AnyEventMouse)) => {
self.any_event_mouse = true; self.any_event_mouse = true;
@ -2099,6 +2199,9 @@ impl TerminalState {
self.any_event_mouse = false; self.any_event_mouse = false;
self.last_mouse_move.take(); 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::FocusTracking)) => {
self.focus_tracking = true; self.focus_tracking = true;
@ -2108,6 +2211,9 @@ impl TerminalState {
self.focus_tracking = false; self.focus_tracking = false;
self.last_mouse_move.take(); 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)) => { Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SGRMouse)) => {
self.sgr_mouse = true; self.sgr_mouse = true;
@ -2117,6 +2223,9 @@ impl TerminalState {
self.sgr_mouse = false; self.sgr_mouse = false;
self.last_mouse_move.take(); self.last_mouse_move.take();
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::SGRMouse)) => {
self.decqrm_response(mode, true, self.sgr_mouse);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SixelScrollsRight, DecPrivateModeCode::SixelScrollsRight,
@ -2128,6 +2237,11 @@ impl TerminalState {
)) => { )) => {
self.sixel_scrolls_right = false; self.sixel_scrolls_right = false;
} }
Mode::QueryDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::SixelScrollsRight,
)) => {
self.decqrm_response(mode, true, self.sixel_scrolls_right);
}
Mode::SetDecPrivateMode(DecPrivateMode::Code( Mode::SetDecPrivateMode(DecPrivateMode::Code(
DecPrivateModeCode::ClearAndEnableAlternateScreen, DecPrivateModeCode::ClearAndEnableAlternateScreen,
@ -2153,25 +2267,24 @@ impl TerminalState {
log::warn!("save/restore dec mode {:?} unimplemented", n) log::warn!("save/restore dec mode {:?} unimplemented", n)
} }
Mode::SetDecPrivateMode(DecPrivateMode::Unspecified(n)) Mode::SetDecPrivateMode(DecPrivateMode::Unspecified(_))
| Mode::ResetDecPrivateMode(DecPrivateMode::Unspecified(n)) | Mode::ResetDecPrivateMode(DecPrivateMode::Unspecified(_))
| Mode::SaveDecPrivateMode(DecPrivateMode::Unspecified(n)) | Mode::SaveDecPrivateMode(DecPrivateMode::Unspecified(_))
| Mode::RestoreDecPrivateMode(DecPrivateMode::Unspecified(n)) => { | Mode::RestoreDecPrivateMode(DecPrivateMode::Unspecified(_)) => {
log::warn!("unhandled DecPrivateMode {}", n); log::warn!("unhandled DecPrivateMode {:?}", mode);
} }
Mode::SetMode(TerminalMode::Unspecified(n)) mode @ Mode::SetMode(_) | mode @ Mode::ResetMode(_) => {
| Mode::ResetMode(TerminalMode::Unspecified(n)) => { log::warn!("unhandled {:?}", mode);
log::warn!("unhandled TerminalMode {}", n);
}
Mode::SetMode(m) | Mode::ResetMode(m) => {
log::warn!("unhandled TerminalMode {:?}", m);
} }
Mode::XtermKeyMode { resource, value } => { Mode::XtermKeyMode { resource, value } => {
log::warn!("unhandled 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, ()> { pub fn parse(params: &[CsiParam]) -> Result<CSI, ()> {
let params = Cracked::parse(&params[1..])?;
Ok(CSI::Device(Box::new(Device::XtSmGraphics(XtSmGraphics { Ok(CSI::Device(Box::new(Device::XtSmGraphics(XtSmGraphics {
item: match params.get(0).ok_or(())? { item: match params.get(0).ok_or(())? {
CsiParam::Integer(1) => XtSmGraphicsItem::NumberOfColorRegisters, CsiParam::Integer(1) => XtSmGraphicsItem::NumberOfColorRegisters,
@ -259,10 +260,10 @@ impl XtSmGraphics {
CsiParam::Integer(n) => *n, CsiParam::Integer(n) => *n,
_ => return Err(()), _ => return Err(()),
}, },
value: params[2..] value: params.params[2..]
.iter() .iter()
.filter_map(|p| match p { .filter_map(|p| match p {
CsiParam::Integer(n) => Some(*n), Some(CsiParam::Integer(n)) => Some(*n),
_ => None, _ => None,
}) })
.collect(), .collect(),
@ -561,8 +562,10 @@ pub enum Mode {
ResetDecPrivateMode(DecPrivateMode), ResetDecPrivateMode(DecPrivateMode),
SaveDecPrivateMode(DecPrivateMode), SaveDecPrivateMode(DecPrivateMode),
RestoreDecPrivateMode(DecPrivateMode), RestoreDecPrivateMode(DecPrivateMode),
QueryDecPrivateMode(DecPrivateMode),
SetMode(TerminalMode), SetMode(TerminalMode),
ResetMode(TerminalMode), ResetMode(TerminalMode),
QueryMode(TerminalMode),
XtermKeyMode { XtermKeyMode {
resource: XtermKeyModifierResource, resource: XtermKeyModifierResource,
value: Option<i64>, value: Option<i64>,
@ -594,8 +597,18 @@ impl Display for Mode {
Mode::ResetDecPrivateMode(mode) => emit!("l", mode), Mode::ResetDecPrivateMode(mode) => emit!("l", mode),
Mode::SaveDecPrivateMode(mode) => emit!("s", mode), Mode::SaveDecPrivateMode(mode) => emit!("s", mode),
Mode::RestoreDecPrivateMode(mode) => emit!("r", 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::SetMode(mode) => emit_mode!("h", mode),
Mode::ResetMode(mode) => emit_mode!("l", 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 } => { Mode::XtermKeyMode { resource, value } => {
write!( write!(
f, f,
@ -1378,6 +1391,7 @@ struct CSIParser<'a> {
/// default values, especially for SGR, so we need to be careful not /// default values, especially for SGR, so we need to be careful not
/// to update params to an empty slice. /// to update params to an empty slice.
params: Option<&'a [CsiParam]>, params: Option<&'a [CsiParam]>,
orig_params: &'a [CsiParam],
} }
impl CSI { impl CSI {
@ -1396,6 +1410,7 @@ impl CSI {
parameters_truncated, parameters_truncated,
control, control,
params: Some(params), params: Some(params),
orig_params: params,
} }
} }
} }
@ -1502,7 +1517,7 @@ macro_rules! parse {
impl<'a> CSIParser<'a> { impl<'a> CSIParser<'a> {
fn parse_next(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> { 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), ('q', [.., CsiParam::P(b' ')]) => self.cursor_style(params),
('y', [.., CsiParam::P(b'*')]) => self.checksum_area(params), ('y', [.., CsiParam::P(b'*')]) => self.checksum_area(params),
@ -1522,76 +1537,82 @@ impl<'a> CSIParser<'a> {
.map(|dev| CSI::Device(Box::new(dev))), .map(|dev| CSI::Device(Box::new(dev))),
('S', [CsiParam::P(b'?'), ..]) => XtSmGraphics::parse(params), ('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 ('h', [CsiParam::P(b'?'), ..]) => self
.dec(params) .dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::SetDecPrivateMode(mode))), .map(|mode| CSI::Mode(Mode::SetDecPrivateMode(mode))),
('l', [CsiParam::P(b'?'), ..]) => self ('l', [CsiParam::P(b'?'), ..]) => self
.dec(params) .dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::ResetDecPrivateMode(mode))), .map(|mode| CSI::Mode(Mode::ResetDecPrivateMode(mode))),
('r', [CsiParam::P(b'?'), ..]) => self ('r', [CsiParam::P(b'?'), ..]) => self
.dec(params) .dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::RestoreDecPrivateMode(mode))), .map(|mode| CSI::Mode(Mode::RestoreDecPrivateMode(mode))),
('q', [CsiParam::P(b'>'), ..]) => self ('q', [CsiParam::P(b'>'), ..]) => self
.req_terminal_name_and_version(params) .req_terminal_name_and_version(params)
.map(|dev| CSI::Device(Box::new(dev))), .map(|dev| CSI::Device(Box::new(dev))),
('s', [CsiParam::P(b'?'), ..]) => self ('s', [CsiParam::P(b'?'), ..]) => self
.dec(params) .dec(self.focus(params, 1, 0))
.map(|mode| CSI::Mode(Mode::SaveDecPrivateMode(mode))), .map(|mode| CSI::Mode(Mode::SaveDecPrivateMode(mode))),
('m', [CsiParam::P(b'>'), ..]) => self.xterm_key_modifier(params), ('m', [CsiParam::P(b'>'), ..]) => self.xterm_key_modifier(params),
('p', [CsiParam::P(b'!')]) => Ok(CSI::Device(Box::new(Device::SoftReset))), ('p', [CsiParam::P(b'!')]) => Ok(CSI::Device(Box::new(Device::SoftReset))),
('c', _) => self _ => match self.control {
.req_primary_device_attributes(params) 'c' => self
.map(|dev| CSI::Device(Box::new(dev))), .req_primary_device_attributes(params)
.map(|dev| CSI::Device(Box::new(dev))),
('@', _) => parse!(Edit, InsertCharacter, params), '@' => parse!(Edit, InsertCharacter, params),
('`', _) => parse!(Cursor, CharacterPositionAbsolute, params), '`' => parse!(Cursor, CharacterPositionAbsolute, params),
('A', _) => parse!(Cursor, Up, params), 'A' => parse!(Cursor, Up, params),
('B', _) => parse!(Cursor, Down, params), 'B' => parse!(Cursor, Down, params),
('C', _) => parse!(Cursor, Right, params), 'C' => parse!(Cursor, Right, params),
('D', _) => parse!(Cursor, Left, params), 'D' => parse!(Cursor, Left, params),
('E', _) => parse!(Cursor, NextLine, params), 'E' => parse!(Cursor, NextLine, params),
('F', _) => parse!(Cursor, PrecedingLine, params), 'F' => parse!(Cursor, PrecedingLine, params),
('G', _) => parse!(Cursor, CharacterAbsolute, params), 'G' => parse!(Cursor, CharacterAbsolute, params),
('H', _) => parse!(Cursor, Position, line, col, params), 'H' => parse!(Cursor, Position, line, col, params),
('I', _) => parse!(Cursor, ForwardTabulation, params), 'I' => parse!(Cursor, ForwardTabulation, params),
('J', _) => parse!(Edit, EraseInDisplay, params), 'J' => parse!(Edit, EraseInDisplay, params),
('K', _) => parse!(Edit, EraseInLine, params), 'K' => parse!(Edit, EraseInLine, params),
('L', _) => parse!(Edit, InsertLine, params), 'L' => parse!(Edit, InsertLine, params),
('M', _) => parse!(Edit, DeleteLine, params), 'M' => parse!(Edit, DeleteLine, params),
('P', _) => parse!(Edit, DeleteCharacter, params), 'P' => parse!(Edit, DeleteCharacter, params),
('R', _) => parse!(Cursor, ActivePositionReport, line, col, params), 'R' => parse!(Cursor, ActivePositionReport, line, col, params),
('S', _) => parse!(Edit, ScrollUp, params), 'S' => parse!(Edit, ScrollUp, params),
('T', _) => parse!(Edit, ScrollDown, params), 'T' => parse!(Edit, ScrollDown, params),
('W', _) => parse!(Cursor, TabulationControl, params), 'W' => parse!(Cursor, TabulationControl, params),
('X', _) => parse!(Edit, EraseCharacter, params), 'X' => parse!(Edit, EraseCharacter, params),
('Y', _) => parse!(Cursor, LineTabulation, params), 'Y' => parse!(Cursor, LineTabulation, params),
('Z', _) => parse!(Cursor, BackwardTabulation, params), 'Z' => parse!(Cursor, BackwardTabulation, params),
('a', _) => parse!(Cursor, CharacterPositionForward, params), 'a' => parse!(Cursor, CharacterPositionForward, params),
('b', _) => parse!(Edit, Repeat, params), 'b' => parse!(Edit, Repeat, params),
('d', _) => parse!(Cursor, LinePositionAbsolute, params), 'd' => parse!(Cursor, LinePositionAbsolute, params),
('e', _) => parse!(Cursor, LinePositionForward, params), 'e' => parse!(Cursor, LinePositionForward, params),
('f', _) => parse!(Cursor, CharacterAndLinePosition, line, col, params), 'f' => parse!(Cursor, CharacterAndLinePosition, line, col, params),
('g', _) => parse!(Cursor, TabulationClear, params), 'g' => parse!(Cursor, TabulationClear, params),
('h', _) => self 'h' => self
.terminal_mode(params) .terminal_mode(params)
.map(|mode| CSI::Mode(Mode::SetMode(mode))), .map(|mode| CSI::Mode(Mode::SetMode(mode))),
('j', _) => parse!(Cursor, CharacterPositionBackward, params), 'j' => parse!(Cursor, CharacterPositionBackward, params),
('k', _) => parse!(Cursor, LinePositionBackward, params), 'k' => parse!(Cursor, LinePositionBackward, params),
('l', _) => self 'l' => self
.terminal_mode(params) .terminal_mode(params)
.map(|mode| CSI::Mode(Mode::ResetMode(mode))), .map(|mode| CSI::Mode(Mode::ResetMode(mode))),
('m', _) => self.sgr(params).map(CSI::Sgr), 'm' => self.sgr(params).map(CSI::Sgr),
('n', _) => self.dsr(params), 'n' => self.dsr(params),
('r', _) => self.decstbm(params), 'r' => self.decstbm(params),
('s', _) => self.decslrm(params), 's' => self.decslrm(params),
('t', _) => self.window(params).map(CSI::Window), 't' => self.window(params).map(CSI::Window),
('u', _) => noparams!(Cursor, RestoreCursor, params), 'u' => noparams!(Cursor, RestoreCursor, params),
_ => Err(()), _ => Err(()),
},
} }
} }
@ -1613,6 +1634,15 @@ impl<'a> CSIParser<'a> {
result 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, ()> { fn cursor_style(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
match params { match params {
[CsiParam::Integer(p), CsiParam::P(b' ')] => match FromPrimitive::from_i64(*p) { [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, ()> { fn req_terminal_name_and_version(&mut self, params: &'a [CsiParam]) -> Result<Device, ()> {
match params { match params {
[] => Ok(Device::RequestTerminalNameAndVersion), [_] => Ok(Device::RequestTerminalNameAndVersion),
[CsiParam::Integer(0)] => { [_, CsiParam::Integer(0)] => {
Ok(self.advance_by(1, params, Device::RequestTerminalNameAndVersion)) Ok(self.advance_by(2, params, Device::RequestTerminalNameAndVersion))
} }
_ => Err(()), _ => 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, ()> { fn dec(&mut self, params: &'a [CsiParam]) -> Result<DecPrivateMode, ()> {
match params { 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( None => Ok(self.advance_by(
2, 1,
params, params,
DecPrivateMode::Unspecified(p0.to_u16().ok_or(())?), 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(()), _ => Err(()),
} }

View File

@ -429,7 +429,8 @@ mod test {
use crate::cell::{Intensity, Underline}; use crate::cell::{Intensity, Underline};
use crate::color::ColorSpec; use crate::color::ColorSpec;
use crate::escape::csi::{ 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 crate::escape::{EscCode, OneBased};
use std::io::Write; use std::io::Write;
@ -730,6 +731,14 @@ mod test {
actions 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] #[test]
fn xterm_key() { fn xterm_key() {
assert_eq!( 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] #[test]
fn req_attr() { fn req_attr() {
assert_eq!( assert_eq!(

View File

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