blob: 3a323ccebb89e7cde2c7f63261e55eb8d6fa9e30 [file]
//! Device Control String (DCS) escape sequences.
//!
//! Device Control String sequences are framed by [`DCS`] and [`ST`]. Termina currently models the
//! [DECRQSS] request and [DECRPSS] response forms used for terminal state queries, along with
//! the XTVERSION name-and-version response.
//!
//! # Examples
//!
//! ```
//! use termina::escape::dcs::{Dcs, DcsRequest};
//!
//! let request = Dcs::Request(DcsRequest::GraphicRendition);
//! assert_eq!(request.to_string(), "\x1bP$qm\x1b\\");
//! ```
//!
//! [`DCS`]: super::DCS
//! [DECRPSS]: https://vt100.net/docs/vt510-rm/DECRPSS.html
//! [DECRQSS]: https://vt100.net/docs/vt510-rm/DECRQSS.html
//! [`ST`]: super::ST
use std::fmt::{self, Display};
use crate::style::CursorStyle;
#[cfg(doc)]
use crate::escape::csi::Sgr;
/// A Device Control String command.
///
/// Termina uses DCS for terminal state queries that have structured request and response forms.
/// Formatting writes the DCS introducer, the request or response payload, and the string
/// terminator.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Dcs {
/// The raw terminal name and version from an XTVERSION response (`DCS >|text ST`).
///
/// The parser does not infer a terminal product or capabilities from this string.
TerminalNameAndVersion(String),
/// Request a terminal setting with [DECRQSS] using a [`DcsRequest`] selector.
///
/// For example, [`DcsRequest::GraphicRendition`] asks for the current [`Sgr`] state.
///
/// [DECRQSS]: https://vt100.net/docs/vt510-rm/DECRQSS.html
Request(DcsRequest),
/// Report a terminal setting with [DECRPSS] using a [`DcsResponse`] payload.
///
/// Terminals use this response to answer a DECRQSS request. A valid response contains the
/// setting encoded as the terminal would send it in the corresponding control sequence.
///
/// [DECRPSS]: https://vt100.net/docs/vt510-rm/DECRPSS.html
Response {
/// Whether the terminal recognized the original request.
is_request_valid: bool,
/// The setting value returned by the terminal.
value: DcsResponse,
},
}
impl Display for Dcs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// DCS
f.write_str(super::DCS)?;
match self {
Self::TerminalNameAndVersion(value) => write!(f, ">|{value}")?,
// DCS $ q D...D ST
Self::Request(request) => write!(f, "$q{request}")?,
// DCS Ps $ r D...D ST
Self::Response {
is_request_valid,
value,
} => write!(f, "{}$r{value}", if *is_request_valid { 1 } else { 0 })?,
}
// ST
f.write_str(super::ST)
}
}
/// Request selectors for [DECRQSS].
///
/// Each variant names the setting being queried and shows the selector bytes sent after `DCS $ q`.
/// The selector bytes come from [DECRQSS].
///
/// [DECRQSS]: https://vt100.net/docs/vt510-rm/DECRQSS.html
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DcsRequest {
/// DECRQSS `$}`: request the active status display.
ActiveStatusDisplay,
/// DECRQSS `*x`: request the attribute change extent.
AttributeChangeExtent,
/// DECRQSS `"q`: request the character attribute.
CharacterAttribute,
/// DECRQSS `"p`: request the conformance level.
ConformanceLevel,
/// DECRQSS `$|`: request the number of columns per page.
ColumnsPerPage,
/// DECRQSS `t`: request the number of lines per page.
LinesPerPage,
/// DECRQSS `*|`: request the number of lines per screen.
NumberOfLinesPerScreen,
/// DECRQSS `$~`: request the status line type.
StatusLineType,
/// DECRQSS `s`: request the left and right margins.
LeftAndRightMargins,
/// DECRQSS `r`: request the top and bottom margins.
TopAndBottomMargins,
/// DECRQSS `m`: request [`Sgr`] state.
GraphicRendition,
/// DECRQSS `p`: request the setup language.
SetUpLanguage,
/// DECRQSS `$s`: request the printer type.
PrinterType,
/// DECRQSS `"t`: request the refresh rate.
RefreshRate,
/// DECRQSS `(p`: request the digital printed data type.
DigitalPrintedDataType,
/// DECRQSS `*p`: request the ProPrinter character set.
ProPrinterCharacterSet,
/// DECRQSS `*r`: request the communication speed.
CommunicationSpeed,
/// DECRQSS `*u`: request the communication port.
CommunicationPort,
/// DECRQSS ` p`: request the scroll speed.
ScrollSpeed,
/// DECRQSS ` q`: request the cursor style.
CursorStyle,
/// DECRQSS ` r`: request the key-click volume.
KeyClickVolume,
/// DECRQSS ` t`: request the warning-bell volume.
WarningBellVolume,
/// DECRQSS ` u`: request the margin-bell volume.
MarginBellVolume,
/// DECRQSS ` v`: request the lock-key style.
LockKeyStyle,
/// DECRQSS `*s`: request the flow-control type.
FlowControlType,
/// DECRQSS `$q`: request the disconnect delay time.
DisconnectDelayTime,
/// DECRQSS `"u`: request the transmit-rate limit.
TransmitRateLimit,
/// DECRQSS `+w`: request the port parameter.
PortParameter,
}
impl Display for DcsRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ActiveStatusDisplay => f.write_str("$}"),
Self::AttributeChangeExtent => write!(f, "*x"),
Self::CharacterAttribute => write!(f, "\"q"),
Self::ConformanceLevel => write!(f, "\"p"),
Self::ColumnsPerPage => write!(f, "$|"),
Self::LinesPerPage => write!(f, "t"),
Self::NumberOfLinesPerScreen => write!(f, "*|"),
Self::StatusLineType => write!(f, "$~"),
Self::LeftAndRightMargins => write!(f, "s"),
Self::TopAndBottomMargins => write!(f, "r"),
Self::GraphicRendition => write!(f, "m"),
Self::SetUpLanguage => write!(f, "p"),
Self::PrinterType => write!(f, "$s"),
Self::RefreshRate => write!(f, "\"t"),
Self::DigitalPrintedDataType => write!(f, "(p"),
Self::ProPrinterCharacterSet => write!(f, "*p"),
Self::CommunicationSpeed => write!(f, "*r"),
Self::CommunicationPort => write!(f, "*u"),
// NOTE: space char is intentional - written as SP in
// <https://vt100.net/docs/vt510-rm/DECRPSS.html>
Self::ScrollSpeed => write!(f, " p"),
Self::CursorStyle => write!(f, " q"),
Self::KeyClickVolume => write!(f, " r"),
Self::WarningBellVolume => write!(f, " t"),
Self::MarginBellVolume => write!(f, " u"),
Self::LockKeyStyle => write!(f, " v"),
Self::FlowControlType => write!(f, "*s"),
Self::DisconnectDelayTime => write!(f, "$q"),
Self::TransmitRateLimit => write!(f, "\"u"),
Self::PortParameter => write!(f, "+w"),
}
}
}
/// Response payloads from [DECRPSS] parsed by Termina.
///
/// [DECRPSS] is the response form terminals use for [DECRQSS] status-string queries.
///
/// [DECRPSS]: https://vt100.net/docs/vt510-rm/DECRPSS.html
/// [DECRQSS]: https://vt100.net/docs/vt510-rm/DECRQSS.html
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DcsResponse {
/// A DECRPSS response containing [`Sgr`] attributes.
///
/// [`DcsRequest::GraphicRendition`] produces this response. The payload carries the same
/// values that would appear in a [`Sgr`] sequence.
///
/// [`Sgr`]: crate::escape::csi::Sgr
GraphicRendition(Vec<super::csi::Sgr>),
/// A DECRPSS response containing the terminal's current cursor style.
///
/// [`DcsRequest::CursorStyle`] produces this response. The payload corresponds to the
/// [`CursorStyle`] setting.
CursorStyle(CursorStyle),
// There are others but adding them would mean adding a lot of parsing code...
}
impl Display for DcsResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::GraphicRendition(sgrs) => {
let mut first = true;
for sgr in sgrs {
if !first {
write!(f, ";")?;
}
first = false;
write!(f, "{sgr}")?;
}
Ok(())
}
Self::CursorStyle(style) => write!(f, "{style} q"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn encoding() {
assert_eq!(
Dcs::Request(DcsRequest::GraphicRendition).to_string(),
"\x1bP$qm\x1b\\"
);
assert_eq!(
Dcs::Request(DcsRequest::CursorStyle).to_string(),
"\x1bP$q q\x1b\\"
);
}
}