mirror of
https://github.com/wez/wezterm.git
synced 2024-12-24 13:52:55 +03:00
impl IRM insert mode and improve esctest conformance
I've had mixed results with esctest; the IRM and cursor save/restore tests fail for me in terminal.app, iterm2 and xterm, and fail in the same way on wezterm, so I'm not sure if I'm not running those tests correctly. However, they did encourage the discovery of some other real issues in the wezterm emulation.
This commit is contained in:
parent
6be7c74967
commit
6cbb3ba432
@ -3,6 +3,10 @@ set -x
|
|||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
cargo run -- start --front-end null -- python -B ./ci/esctest/esctest/esctest.py \
|
cargo run -- start --front-end null -- python -B ./ci/esctest/esctest/esctest.py \
|
||||||
--expected-terminal=xterm \
|
--expected-terminal=xterm \
|
||||||
|
--xterm-checksum=334 \
|
||||||
--v=3 \
|
--v=3 \
|
||||||
--timeout=0.1 \
|
--timeout=0.1 \
|
||||||
|
--no-print-logs \
|
||||||
--logfile=esctest.log
|
--logfile=esctest.log
|
||||||
|
|
||||||
|
#--stop-on-failure \
|
||||||
|
@ -40,7 +40,7 @@ fn channel<T: Send>(proxy: EventsLoopProxy) -> (GuiSender<T>, Receiver<T>) {
|
|||||||
// Set an upper bound on the number of items in the queue, so that
|
// Set an upper bound on the number of items in the queue, so that
|
||||||
// we don't swamp the gui loop; this puts back pressure on the
|
// we don't swamp the gui loop; this puts back pressure on the
|
||||||
// producer side so that we have a chance for eg: processing CTRL-C
|
// producer side so that we have a chance for eg: processing CTRL-C
|
||||||
let (tx, rx) = mpsc::sync_channel(4);
|
let (tx, rx) = mpsc::sync_channel(12);
|
||||||
(GuiSender { tx, proxy }, rx)
|
(GuiSender { tx, proxy }, rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +98,14 @@ impl Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_cell(&mut self, x: usize, y: VisibleRowIndex) {
|
pub fn insert_cell(&mut self, x: usize, y: VisibleRowIndex) {
|
||||||
|
let phys_cols = self.physical_cols;
|
||||||
|
|
||||||
let line_idx = self.phys_row(y);
|
let line_idx = self.phys_row(y);
|
||||||
let line = self.line_mut(line_idx);
|
let line = self.line_mut(line_idx);
|
||||||
line.insert_cell(x, Cell::default());
|
line.insert_cell(x, Cell::default());
|
||||||
|
if line.cells().len() > phys_cols {
|
||||||
|
line.resize(phys_cols);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn erase_cell(&mut self, x: usize, y: VisibleRowIndex) {
|
pub fn erase_cell(&mut self, x: usize, y: VisibleRowIndex) {
|
||||||
|
@ -8,7 +8,7 @@ use std::fmt::Write;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use termwiz::escape::csi::{
|
use termwiz::escape::csi::{
|
||||||
Cursor, DecPrivateMode, DecPrivateModeCode, Device, Edit, EraseInDisplay, EraseInLine, Mode,
|
Cursor, DecPrivateMode, DecPrivateModeCode, Device, Edit, EraseInDisplay, EraseInLine, Mode,
|
||||||
Sgr, Window,
|
Sgr, TerminalMode, TerminalModeCode, Window,
|
||||||
};
|
};
|
||||||
use termwiz::escape::osc::{ITermFileData, ITermProprietary};
|
use termwiz::escape::osc::{ITermFileData, ITermProprietary};
|
||||||
use termwiz::escape::{Action, ControlCode, Esc, EscCode, OperatingSystemCommand, CSI};
|
use termwiz::escape::{Action, ControlCode, Esc, EscCode, OperatingSystemCommand, CSI};
|
||||||
@ -56,6 +56,13 @@ impl TabStop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct SavedCursor {
|
||||||
|
position: CursorPosition,
|
||||||
|
wrap_next: bool,
|
||||||
|
insert: bool,
|
||||||
|
}
|
||||||
|
|
||||||
struct ScreenOrAlt {
|
struct ScreenOrAlt {
|
||||||
/// The primary screen + scrollback
|
/// The primary screen + scrollback
|
||||||
screen: Screen,
|
screen: Screen,
|
||||||
@ -63,6 +70,8 @@ struct ScreenOrAlt {
|
|||||||
alt_screen: Screen,
|
alt_screen: Screen,
|
||||||
/// Tells us which screen is active
|
/// Tells us which screen is active
|
||||||
alt_screen_is_active: bool,
|
alt_screen_is_active: bool,
|
||||||
|
saved_cursor: Option<SavedCursor>,
|
||||||
|
alt_saved_cursor: Option<SavedCursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for ScreenOrAlt {
|
impl Deref for ScreenOrAlt {
|
||||||
@ -96,6 +105,8 @@ impl ScreenOrAlt {
|
|||||||
screen,
|
screen,
|
||||||
alt_screen,
|
alt_screen,
|
||||||
alt_screen_is_active: false,
|
alt_screen_is_active: false,
|
||||||
|
saved_cursor: None,
|
||||||
|
alt_saved_cursor: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +126,14 @@ impl ScreenOrAlt {
|
|||||||
pub fn is_alt_screen_active(&self) -> bool {
|
pub fn is_alt_screen_active(&self) -> bool {
|
||||||
self.alt_screen_is_active
|
self.alt_screen_is_active
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn saved_cursor(&mut self) -> &mut Option<SavedCursor> {
|
||||||
|
if self.alt_screen_is_active {
|
||||||
|
&mut self.alt_saved_cursor
|
||||||
|
} else {
|
||||||
|
&mut self.saved_cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TerminalState {
|
pub struct TerminalState {
|
||||||
@ -125,12 +144,14 @@ pub struct TerminalState {
|
|||||||
/// The current cursor position, relative to the top left
|
/// The current cursor position, relative to the top left
|
||||||
/// of the screen. 0-based index.
|
/// of the screen. 0-based index.
|
||||||
cursor: CursorPosition,
|
cursor: CursorPosition,
|
||||||
saved_cursor: CursorPosition,
|
|
||||||
|
|
||||||
/// if true, implicitly move to the next line on the next
|
/// if true, implicitly move to the next line on the next
|
||||||
/// printed character
|
/// printed character
|
||||||
wrap_next: bool,
|
wrap_next: bool,
|
||||||
|
|
||||||
|
/// If true, writing a character inserts a new cell
|
||||||
|
insert: bool,
|
||||||
|
|
||||||
/// The scroll region
|
/// The scroll region
|
||||||
scroll_region: Range<VisibleRowIndex>,
|
scroll_region: Range<VisibleRowIndex>,
|
||||||
|
|
||||||
@ -218,9 +239,9 @@ impl TerminalState {
|
|||||||
screen,
|
screen,
|
||||||
pen: CellAttributes::default(),
|
pen: CellAttributes::default(),
|
||||||
cursor: CursorPosition::default(),
|
cursor: CursorPosition::default(),
|
||||||
saved_cursor: CursorPosition::default(),
|
|
||||||
scroll_region: 0..physical_rows as VisibleRowIndex,
|
scroll_region: 0..physical_rows as VisibleRowIndex,
|
||||||
wrap_next: false,
|
wrap_next: false,
|
||||||
|
insert: false,
|
||||||
application_cursor_keys: false,
|
application_cursor_keys: false,
|
||||||
application_keypad: false,
|
application_keypad: false,
|
||||||
bracketed_paste: false,
|
bracketed_paste: false,
|
||||||
@ -1309,6 +1330,15 @@ impl TerminalState {
|
|||||||
DecPrivateModeCode::StartBlinkingCursor,
|
DecPrivateModeCode::StartBlinkingCursor,
|
||||||
)) => {}
|
)) => {}
|
||||||
|
|
||||||
|
Mode::SetMode(TerminalMode::Code(TerminalModeCode::Insert)) => {
|
||||||
|
eprintln!("Enable insert");
|
||||||
|
self.insert = true;
|
||||||
|
}
|
||||||
|
Mode::ResetMode(TerminalMode::Code(TerminalModeCode::Insert)) => {
|
||||||
|
eprintln!("Disable insert");
|
||||||
|
self.insert = false;
|
||||||
|
}
|
||||||
|
|
||||||
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
|
Mode::SetDecPrivateMode(DecPrivateMode::Code(DecPrivateModeCode::BracketedPaste)) => {
|
||||||
self.bracketed_paste = true;
|
self.bracketed_paste = true;
|
||||||
}
|
}
|
||||||
@ -1316,6 +1346,23 @@ impl TerminalState {
|
|||||||
self.bracketed_paste = false;
|
self.bracketed_paste = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mode::SetDecPrivateMode(DecPrivateMode::Code(
|
||||||
|
DecPrivateModeCode::EnableAlternateScreen,
|
||||||
|
)) => {
|
||||||
|
if !self.screen.is_alt_screen_active() {
|
||||||
|
self.screen.activate_alt_screen();
|
||||||
|
self.set_scroll_viewport(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mode::ResetDecPrivateMode(DecPrivateMode::Code(
|
||||||
|
DecPrivateModeCode::EnableAlternateScreen,
|
||||||
|
)) => {
|
||||||
|
if self.screen.is_alt_screen_active() {
|
||||||
|
self.screen.activate_primary_screen();
|
||||||
|
self.set_scroll_viewport(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Mode::SetDecPrivateMode(DecPrivateMode::Code(
|
Mode::SetDecPrivateMode(DecPrivateMode::Code(
|
||||||
DecPrivateModeCode::ApplicationCursorKeys,
|
DecPrivateModeCode::ApplicationCursorKeys,
|
||||||
)) => {
|
)) => {
|
||||||
@ -1396,9 +1443,42 @@ impl TerminalState {
|
|||||||
| Mode::RestoreDecPrivateMode(DecPrivateMode::Unspecified(n)) => {
|
| Mode::RestoreDecPrivateMode(DecPrivateMode::Unspecified(n)) => {
|
||||||
eprintln!("unhandled DecPrivateMode {}", n);
|
eprintln!("unhandled DecPrivateMode {}", n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mode::SetMode(TerminalMode::Unspecified(n))
|
||||||
|
| Mode::ResetMode(TerminalMode::Unspecified(n)) => {
|
||||||
|
eprintln!("unhandled TerminalMode {}", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mode::SetMode(m) | Mode::ResetMode(m) => {
|
||||||
|
eprintln!("unhandled TerminalMode {:?}", m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn checksum_rectangle(&mut self, left: u32, top: u32, right: u32, bottom: u32) -> u16 {
|
||||||
|
let screen = self.screen_mut();
|
||||||
|
let mut checksum = 0;
|
||||||
|
debug!(
|
||||||
|
"checksum left={} top={} right={} bottom={}",
|
||||||
|
left, top, right, bottom
|
||||||
|
);
|
||||||
|
for y in top..=bottom {
|
||||||
|
let line_idx = screen.phys_row(y as VisibleRowIndex);
|
||||||
|
let line = screen.line_mut(line_idx);
|
||||||
|
for (col, cell) in line.cells().iter().enumerate().skip(left as usize) {
|
||||||
|
if col > right as usize {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ch = cell.str().chars().nth(0).unwrap() as u32;
|
||||||
|
debug!("y={} col={} ch={:x} cell={:?}", y, col, ch, cell);
|
||||||
|
|
||||||
|
checksum += (ch as u8) as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checksum
|
||||||
|
}
|
||||||
|
|
||||||
fn perform_csi_window(&mut self, window: Window, host: &mut TerminalHost) {
|
fn perform_csi_window(&mut self, window: Window, host: &mut TerminalHost) {
|
||||||
match window {
|
match window {
|
||||||
Window::ReportTextAreaSizeCells => {
|
Window::ReportTextAreaSizeCells => {
|
||||||
@ -1409,8 +1489,19 @@ impl TerminalState {
|
|||||||
let response = Window::ResizeWindowCells { width, height };
|
let response = Window::ResizeWindowCells { width, height };
|
||||||
write!(host.writer(), "{}", CSI::Window(response)).ok();
|
write!(host.writer(), "{}", CSI::Window(response)).ok();
|
||||||
}
|
}
|
||||||
Window::Iconify | Window::DeIconify => {},
|
Window::ChecksumRectangularArea {
|
||||||
Window::PopIconAndWindowTitle => {},
|
request_id,
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
bottom,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let checksum = self.checksum_rectangle(left, top, right, bottom);
|
||||||
|
write!(host.writer(), "\x1bP{}!~{:04x}\x1b\\", request_id, checksum).ok();
|
||||||
|
}
|
||||||
|
Window::Iconify | Window::DeIconify => {}
|
||||||
|
Window::PopIconAndWindowTitle => {}
|
||||||
_ => eprintln!("unhandled Window CSI {:?}", window),
|
_ => eprintln!("unhandled Window CSI {:?}", window),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1635,12 +1726,34 @@ impl TerminalState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn save_cursor(&mut self) {
|
fn save_cursor(&mut self) {
|
||||||
self.saved_cursor = self.cursor;
|
let saved = SavedCursor {
|
||||||
|
position: self.cursor,
|
||||||
|
insert: self.insert,
|
||||||
|
wrap_next: self.wrap_next,
|
||||||
|
};
|
||||||
|
debug!(
|
||||||
|
"saving cursor {:?} is_alt={}",
|
||||||
|
saved,
|
||||||
|
self.screen.is_alt_screen_active()
|
||||||
|
);
|
||||||
|
*self.screen.saved_cursor() = Some(saved);
|
||||||
}
|
}
|
||||||
fn restore_cursor(&mut self) {
|
fn restore_cursor(&mut self) {
|
||||||
let x = self.saved_cursor.x;
|
let saved = self.screen.saved_cursor().unwrap_or_else(|| SavedCursor {
|
||||||
let y = self.saved_cursor.y;
|
position: CursorPosition::default(),
|
||||||
|
insert: false,
|
||||||
|
wrap_next: false,
|
||||||
|
});
|
||||||
|
debug!(
|
||||||
|
"restore cursor {:?} is_alt={}",
|
||||||
|
saved,
|
||||||
|
self.screen.is_alt_screen_active()
|
||||||
|
);
|
||||||
|
let x = saved.position.x;
|
||||||
|
let y = saved.position.y;
|
||||||
self.set_cursor_pos(&Position::Absolute(x as i64), &Position::Absolute(y));
|
self.set_cursor_pos(&Position::Absolute(x as i64), &Position::Absolute(y));
|
||||||
|
self.wrap_next = saved.wrap_next;
|
||||||
|
self.insert = saved.insert;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_csi_sgr(&mut self, sgr: Sgr) {
|
fn perform_csi_sgr(&mut self, sgr: Sgr) {
|
||||||
@ -1726,8 +1839,10 @@ impl<'a> Performer<'a> {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut x_offset = 0;
|
||||||
|
|
||||||
for g in unicode_segmentation::UnicodeSegmentation::graphemes(p.as_str(), true) {
|
for g in unicode_segmentation::UnicodeSegmentation::graphemes(p.as_str(), true) {
|
||||||
if self.wrap_next {
|
if !self.insert && self.wrap_next {
|
||||||
self.new_line(true);
|
self.new_line(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1737,29 +1852,38 @@ impl<'a> Performer<'a> {
|
|||||||
|
|
||||||
let pen = self.pen.clone();
|
let pen = self.pen.clone();
|
||||||
|
|
||||||
// Assign the cell and extract its printable width
|
let cell = Cell::new_grapheme(g, pen.clone());
|
||||||
let print_width = {
|
// the max(1) here is to ensure that we advance to the next cell
|
||||||
let cell = self
|
// position for zero-width graphemes. We want to make sure that
|
||||||
.screen_mut()
|
// they occupy a cell so that we can re-emit them when we output them.
|
||||||
.set_cell(x, y, &Cell::new_grapheme(g, pen.clone()));
|
// If we didn't do this, then we'd effectively filter them out from
|
||||||
// the max(1) here is to ensure that we advance to the next cell
|
// the model, which seems like a lossy design choice.
|
||||||
// position for zero-width graphemes. We want to make sure that
|
let print_width = cell.width().max(1);
|
||||||
// they occupy a cell so that we can re-emit them when we output them.
|
|
||||||
// If we didn't do this, then we'd effectively filter them out from
|
if self.insert {
|
||||||
// the model, which seems like a lossy design choice.
|
let screen = self.screen_mut();
|
||||||
cell.width().max(1)
|
for _ in x..x + print_width as usize {
|
||||||
};
|
screen.insert_cell(x + x_offset, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the cell
|
||||||
|
self.screen_mut().set_cell(x + x_offset, y, &cell);
|
||||||
|
|
||||||
self.clear_selection_if_intersects(
|
self.clear_selection_if_intersects(
|
||||||
x..x + print_width,
|
x..x + print_width,
|
||||||
y as ScrollbackOrVisibleRowIndex,
|
y as ScrollbackOrVisibleRowIndex,
|
||||||
);
|
);
|
||||||
|
|
||||||
if x + print_width < width {
|
if self.insert {
|
||||||
self.cursor.x += print_width;
|
x_offset += print_width;
|
||||||
self.wrap_next = false;
|
|
||||||
} else {
|
} else {
|
||||||
self.wrap_next = true;
|
if x + print_width < width {
|
||||||
|
self.cursor.x += print_width;
|
||||||
|
self.wrap_next = false;
|
||||||
|
} else {
|
||||||
|
self.wrap_next = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,18 +28,27 @@ fn test_rep() {
|
|||||||
assert_visible_contents(&term, &["hhha", " ", " "]);
|
assert_visible_contents(&term, &["hhha", " ", " "]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_irm() {
|
||||||
|
let mut term = TestTerm::new(3, 8, 0);
|
||||||
|
term.print("foo");
|
||||||
|
term.cup(0, 0);
|
||||||
|
term.print("\x1b[4hBAR");
|
||||||
|
assert_visible_contents(&term, &["BARfoo ", " ", " "]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ich() {
|
fn test_ich() {
|
||||||
let mut term = TestTerm::new(3, 4, 0);
|
let mut term = TestTerm::new(3, 4, 0);
|
||||||
term.print("hey!wat?");
|
term.print("hey!wat?");
|
||||||
term.cup(1, 0);
|
term.cup(1, 0);
|
||||||
term.print("\x1b[2@");
|
term.print("\x1b[2@");
|
||||||
assert_visible_contents(&term, &["h ey!", "wat?", " "]);
|
assert_visible_contents(&term, &["h e", "wat?", " "]);
|
||||||
// check how we handle overflowing the width
|
// check how we handle overflowing the width
|
||||||
term.print("\x1b[12@");
|
term.print("\x1b[12@");
|
||||||
assert_visible_contents(&term, &["h ey!", "wat?", " "]);
|
assert_visible_contents(&term, &["h ", "wat?", " "]);
|
||||||
term.print("\x1b[-12@");
|
term.print("\x1b[-12@");
|
||||||
assert_visible_contents(&term, &["h ey!", "wat?", " "]);
|
assert_visible_contents(&term, &["h ", "wat?", " "]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -266,6 +266,15 @@ pub enum Window {
|
|||||||
PopIconAndWindowTitle,
|
PopIconAndWindowTitle,
|
||||||
PopIconTitle,
|
PopIconTitle,
|
||||||
PopWindowTitle,
|
PopWindowTitle,
|
||||||
|
/// DECRQCRA; used by esctest
|
||||||
|
ChecksumRectangularArea {
|
||||||
|
request_id: i64,
|
||||||
|
page_number: i64,
|
||||||
|
top: u32,
|
||||||
|
left: u32,
|
||||||
|
bottom: u32,
|
||||||
|
right: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn numstr_or_empty(x: &Option<i64>) -> String {
|
fn numstr_or_empty(x: &Option<i64>) -> String {
|
||||||
@ -320,6 +329,23 @@ impl Display for Window {
|
|||||||
Window::PopIconAndWindowTitle => write!(f, "23;0t"),
|
Window::PopIconAndWindowTitle => write!(f, "23;0t"),
|
||||||
Window::PopIconTitle => write!(f, "23;1t"),
|
Window::PopIconTitle => write!(f, "23;1t"),
|
||||||
Window::PopWindowTitle => write!(f, "23;2t"),
|
Window::PopWindowTitle => write!(f, "23;2t"),
|
||||||
|
Window::ChecksumRectangularArea {
|
||||||
|
request_id,
|
||||||
|
page_number,
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
bottom,
|
||||||
|
right,
|
||||||
|
} => write!(
|
||||||
|
f,
|
||||||
|
"{};{};{};{};{};{}*y",
|
||||||
|
request_id,
|
||||||
|
page_number,
|
||||||
|
top + 1,
|
||||||
|
left + 1,
|
||||||
|
bottom + 1,
|
||||||
|
right + 1
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,6 +414,8 @@ pub enum Mode {
|
|||||||
ResetDecPrivateMode(DecPrivateMode),
|
ResetDecPrivateMode(DecPrivateMode),
|
||||||
SaveDecPrivateMode(DecPrivateMode),
|
SaveDecPrivateMode(DecPrivateMode),
|
||||||
RestoreDecPrivateMode(DecPrivateMode),
|
RestoreDecPrivateMode(DecPrivateMode),
|
||||||
|
SetMode(TerminalMode),
|
||||||
|
ResetMode(TerminalMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Mode {
|
impl Display for Mode {
|
||||||
@ -401,11 +429,22 @@ impl Display for Mode {
|
|||||||
write!(f, "?{}{}", value, $flag)
|
write!(f, "?{}{}", value, $flag)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
macro_rules! emit_mode {
|
||||||
|
($flag:expr, $mode:expr) => {{
|
||||||
|
let value = match $mode {
|
||||||
|
TerminalMode::Code(mode) => mode.to_u16().ok_or_else(|| FmtError)?,
|
||||||
|
TerminalMode::Unspecified(mode) => *mode,
|
||||||
|
};
|
||||||
|
write!(f, "?{}{}", value, $flag)
|
||||||
|
}};
|
||||||
|
}
|
||||||
match self {
|
match self {
|
||||||
Mode::SetDecPrivateMode(mode) => emit!("h", mode),
|
Mode::SetDecPrivateMode(mode) => emit!("h", 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::SetMode(mode) => emit_mode!("h", mode),
|
||||||
|
Mode::ResetMode(mode) => emit_mode!("l", mode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -435,9 +474,24 @@ pub enum DecPrivateModeCode {
|
|||||||
/// will be encoded.
|
/// will be encoded.
|
||||||
SGRMouse = 1006,
|
SGRMouse = 1006,
|
||||||
ClearAndEnableAlternateScreen = 1049,
|
ClearAndEnableAlternateScreen = 1049,
|
||||||
|
EnableAlternateScreen = 47,
|
||||||
BracketedPaste = 2004,
|
BracketedPaste = 2004,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum TerminalMode {
|
||||||
|
Code(TerminalModeCode),
|
||||||
|
Unspecified(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||||
|
pub enum TerminalModeCode {
|
||||||
|
KeyboardAction = 2,
|
||||||
|
Insert = 4,
|
||||||
|
SendReceive = 12,
|
||||||
|
AutomaticNewline = 20,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Cursor {
|
pub enum Cursor {
|
||||||
/// CBT Moves cursor to the Ps tabs backward. The default value of Ps is 1.
|
/// CBT Moves cursor to the Ps tabs backward. The default value of Ps is 1.
|
||||||
@ -1153,8 +1207,14 @@ impl<'a> CSIParser<'a> {
|
|||||||
('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
|
||||||
|
.terminal_mode(params)
|
||||||
|
.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
|
||||||
|
.terminal_mode(params)
|
||||||
|
.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),
|
||||||
@ -1163,6 +1223,25 @@ impl<'a> CSIParser<'a> {
|
|||||||
('s', &[]) => noparams!(Cursor, SaveCursor, params),
|
('s', &[]) => noparams!(Cursor, SaveCursor, 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),
|
||||||
|
('y', &[b'*']) => {
|
||||||
|
fn p(params: &[i64], idx: usize) -> Result<i64, ()> {
|
||||||
|
params.get(idx).cloned().ok_or(())
|
||||||
|
}
|
||||||
|
let request_id = p(params, 0)?;
|
||||||
|
let page_number = p(params, 1)?;
|
||||||
|
let top = to_1b_u32(p(params, 2)?)?.saturating_sub(1);
|
||||||
|
let left = to_1b_u32(p(params, 3)?)?.saturating_sub(1);
|
||||||
|
let bottom = to_1b_u32(p(params, 4)?)?.saturating_sub(1);
|
||||||
|
let right = to_1b_u32(p(params, 5)?)?.saturating_sub(1);
|
||||||
|
Ok(CSI::Window(Window::ChecksumRectangularArea {
|
||||||
|
request_id,
|
||||||
|
page_number,
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
bottom,
|
||||||
|
right,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
('p', &[b'!']) => Ok(CSI::Device(Box::new(Device::SoftReset))),
|
('p', &[b'!']) => Ok(CSI::Device(Box::new(Device::SoftReset))),
|
||||||
|
|
||||||
@ -1379,6 +1458,13 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn terminal_mode(&mut self, params: &'a [i64]) -> Result<TerminalMode, ()> {
|
||||||
|
match num::FromPrimitive::from_i64(params[0]) {
|
||||||
|
None => Ok(TerminalMode::Unspecified(params[0].to_u16().ok_or(())?)),
|
||||||
|
Some(mode) => Ok(self.advance_by(1, params, TerminalMode::Code(mode))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_sgr_color(&mut self, params: &'a [i64]) -> Result<ColorSpec, ()> {
|
fn parse_sgr_color(&mut self, params: &'a [i64]) -> Result<ColorSpec, ()> {
|
||||||
if params.len() >= 5 && params[1] == 2 {
|
if params.len() >= 5 && params[1] == 2 {
|
||||||
let red = to_u8(params[2])?;
|
let red = to_u8(params[2])?;
|
||||||
|
Loading…
Reference in New Issue
Block a user