Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ pub use attrs::Color;
pub use callbacks::Callbacks;
pub use cell::Cell;
pub use parser::Parser;
pub use screen::{MouseProtocolEncoding, MouseProtocolMode, Screen};
pub use screen::{
CursorStyle, MouseProtocolEncoding, MouseProtocolMode, Screen,
};
14 changes: 14 additions & 0 deletions src/perform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@ impl<CB: crate::callbacks::Callbacks> vte::Perform for WrappedScreen<CB> {
);
}
},
Some(b' ') => match c {
'q' => self
.screen
.decscusr(canonicalize_params_1(params, 0), unhandled),
_ => {
self.callbacks.unhandled_csi(
&mut self.screen,
None,
None,
&params.iter().collect::<Vec<_>>(),
c,
);
}
},
Some(b'?') => match c {
'J' => self
.screen
Expand Down
63 changes: 63 additions & 0 deletions src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,32 @@ pub enum MouseProtocolEncoding {
// Urxvt,
}

/// The cursor's visual style.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub enum CursorStyle {
/// The cursor should match the terminal's default.
#[default]
Default,

/// The cursor should be a blinking block.
BlinkBlock,

/// The cursor should be a steady block.
SteadyBlock,

/// The cursor should be a blinking underline.
BlinkUnderline,

/// The cursor should be a steady underline.
SteadyUnderline,

/// The cursor should be a blinking vertical bar.
BlinkVerticalBar,

/// The cursor should be a steady vertical bar.
SteadyVerticalBar,
}

/// Represents the overall terminal state.
#[derive(Clone, Debug)]
pub struct Screen {
Expand All @@ -62,6 +88,7 @@ pub struct Screen {
modes: u8,
mouse_protocol_mode: MouseProtocolMode,
mouse_protocol_encoding: MouseProtocolEncoding,
cursor_style: CursorStyle,
}

impl Screen {
Expand All @@ -81,6 +108,7 @@ impl Screen {
modes: 0,
mouse_protocol_mode: MouseProtocolMode::default(),
mouse_protocol_encoding: MouseProtocolEncoding::default(),
cursor_style: CursorStyle::default(),
}
}

Expand Down Expand Up @@ -403,6 +431,11 @@ impl Screen {
MouseProtocolEncoding::Default,
)
.write_buf(contents);
crate::term::CursorStyle::new(
self.cursor_style,
CursorStyle::Default,
)
.write_buf(contents);
}

/// Returns terminal escape sequences sufficient to change the previous
Expand Down Expand Up @@ -447,6 +480,8 @@ impl Screen {
prev.mouse_protocol_encoding,
)
.write_buf(contents);
crate::term::CursorStyle::new(self.cursor_style, prev.cursor_style)
.write_buf(contents);
}

/// Returns terminal escape sequences sufficient to set the current
Expand Down Expand Up @@ -585,6 +620,12 @@ impl Screen {
self.mouse_protocol_encoding
}

/// Returns the currently active [`CursorStyle`].
#[must_use]
pub fn cursor_style(&self) -> CursorStyle {
self.cursor_style
}

/// Returns the currently active foreground color.
#[must_use]
pub fn fgcolor(&self) -> crate::Color {
Expand Down Expand Up @@ -699,6 +740,10 @@ impl Screen {
self.mouse_protocol_encoding = MouseProtocolEncoding::default();
}
}

fn set_cursor_style(&mut self, style: CursorStyle) {
self.cursor_style = style;
}
}

impl Screen {
Expand Down Expand Up @@ -1001,6 +1046,24 @@ impl Screen {

// csi codes

// CSI SP
pub(crate) fn decscusr(
&mut self,
cursor_style: u16,
mut unhandled: impl FnMut(&mut Self),
) {
match cursor_style {
0 => self.set_cursor_style(CursorStyle::Default),
1 => self.set_cursor_style(CursorStyle::BlinkBlock),
2 => self.set_cursor_style(CursorStyle::SteadyBlock),
3 => self.set_cursor_style(CursorStyle::BlinkUnderline),
4 => self.set_cursor_style(CursorStyle::SteadyUnderline),
5 => self.set_cursor_style(CursorStyle::BlinkVerticalBar),
6 => self.set_cursor_style(CursorStyle::SteadyVerticalBar),
_ => unhandled(self),
}
}

// CSI @
pub(crate) fn ich(&mut self, count: u16) {
self.grid_mut().insert_cells(count);
Expand Down
45 changes: 45 additions & 0 deletions src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,51 @@ impl BufWrite for MouseProtocolEncoding {
}
}

#[derive(Default, Debug)]
#[must_use = "this struct does nothing unless you call write_buf"]
pub struct CursorStyle {
style: crate::CursorStyle,
prev: crate::CursorStyle,
}

impl CursorStyle {
pub fn new(style: crate::CursorStyle, prev: crate::CursorStyle) -> Self {
Self { style, prev }
}
}

impl BufWrite for CursorStyle {
fn write_buf(&self, buf: &mut Vec<u8>) {
if self.style == self.prev {
return;
}

match self.style {
crate::CursorStyle::Default => {
buf.extend_from_slice(b"\x1b[0 q");
}
crate::CursorStyle::BlinkBlock => {
buf.extend_from_slice(b"\x1b[1 q");
}
crate::CursorStyle::SteadyBlock => {
buf.extend_from_slice(b"\x1b[2 q");
}
crate::CursorStyle::BlinkUnderline => {
buf.extend_from_slice(b"\x1b[3 q");
}
crate::CursorStyle::SteadyUnderline => {
buf.extend_from_slice(b"\x1b[4 q");
}
crate::CursorStyle::BlinkVerticalBar => {
buf.extend_from_slice(b"\x1b[5 q");
}
crate::CursorStyle::SteadyVerticalBar => {
buf.extend_from_slice(b"\x1b[6 q");
}
}
}
}

fn extend_itoa<I: itoa::Integer>(buf: &mut Vec<u8>, i: I) {
let mut itoa_buf = itoa::Buffer::new();
buf.extend_from_slice(itoa_buf.format(i).as_bytes());
Expand Down
9 changes: 9 additions & 0 deletions tests/data/fixtures/modes/21.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contents": "",
"cells": {},
"cursor_position": [
0,
0
],
"cursor_style": "blinkblock"
}
1 change: 1 addition & 0 deletions tests/data/fixtures/modes/21.typescript
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1 q
47 changes: 47 additions & 0 deletions tests/helpers/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ pub struct FixtureScreen {
skip_serializing_if = "is_default"
)]
mouse_protocol_encoding: vt100::MouseProtocolEncoding,
#[serde(
default,
deserialize_with = "deserialize_cursor_style",
serialize_with = "serialize_cursor_style",
skip_serializing_if = "is_default"
)]
cursor_style: vt100::CursorStyle,
}

impl FixtureScreen {
Expand Down Expand Up @@ -113,6 +120,7 @@ impl FixtureScreen {
bracketed_paste: screen.bracketed_paste(),
mouse_protocol_mode: screen.mouse_protocol_mode(),
mouse_protocol_encoding: screen.mouse_protocol_encoding(),
cursor_style: screen.cursor_style(),
}
}
}
Expand Down Expand Up @@ -228,6 +236,44 @@ where
serializer.serialize_str(s)
}

fn deserialize_cursor_style<'a, D>(
deserializer: D,
) -> std::result::Result<vt100::CursorStyle, D::Error>
where
D: serde::de::Deserializer<'a>,
{
let name = <String>::deserialize(deserializer)?;
match name.as_ref() {
"default" => Ok(vt100::CursorStyle::Default),
"blinkblock" => Ok(vt100::CursorStyle::BlinkBlock),
"steadyblock" => Ok(vt100::CursorStyle::SteadyBlock),
"blinkunderline" => Ok(vt100::CursorStyle::BlinkUnderline),
"steadyunderline" => Ok(vt100::CursorStyle::SteadyUnderline),
"blinkverticalbar" => Ok(vt100::CursorStyle::BlinkVerticalBar),
"steadyverticalbar" => Ok(vt100::CursorStyle::SteadyVerticalBar),
_ => unimplemented!(),
}
}

fn serialize_cursor_style<S>(
style: &vt100::CursorStyle,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = match style {
vt100::CursorStyle::Default => "default",
vt100::CursorStyle::BlinkBlock => "blinkblock",
vt100::CursorStyle::SteadyBlock => "steadyblock",
vt100::CursorStyle::BlinkUnderline => "blinkunderline",
vt100::CursorStyle::SteadyUnderline => "steadyunderline",
vt100::CursorStyle::BlinkVerticalBar => "blinkverticalbar",
vt100::CursorStyle::SteadyVerticalBar => "steadyverticalbar",
};
serializer.serialize_str(s)
}

fn load_input(name: &str, i: usize) -> Option<Vec<u8>> {
let mut file = std::fs::File::open(format!(
"tests/data/fixtures/{name}/{i}.typescript"
Expand Down Expand Up @@ -269,6 +315,7 @@ fn assert_produces(input: &[u8], expected: &FixtureScreen) {
parser.screen().mouse_protocol_encoding(),
expected.mouse_protocol_encoding
);
assert_eq!(parser.screen().cursor_style(), expected.cursor_style);

let (rows, cols) = parser.screen().size();
for row in 0..rows {
Expand Down
1 change: 1 addition & 0 deletions tests/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ pub fn compare_screens(
got.mouse_protocol_encoding(),
expected.mouse_protocol_encoding()
);
is!(got.cursor_style(), expected.cursor_style());

true
}
Expand Down
1 change: 1 addition & 0 deletions tests/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ fn init() {
parser.screen().mouse_protocol_encoding(),
vt100::MouseProtocolEncoding::Default
);
assert_eq!(parser.screen().cursor_style(), vt100::CursorStyle::Default)
}