diff --git a/term/src/terminalstate.rs b/term/src/terminalstate.rs index c3f27f935..d112a1505 100644 --- a/term/src/terminalstate.rs +++ b/term/src/terminalstate.rs @@ -15,7 +15,8 @@ use std::sync::mpsc::{channel, Sender}; use std::sync::Arc; use termwiz::escape::csi::{ Cursor, CursorStyle, DecPrivateMode, DecPrivateModeCode, Device, Edit, EraseInDisplay, - EraseInLine, Mode, Sgr, TabulationClear, TerminalMode, TerminalModeCode, Window, + EraseInLine, Mode, Sgr, TabulationClear, TerminalMode, TerminalModeCode, Window, XtSmGraphics, + XtSmGraphicsAction, XtSmGraphicsItem, XtSmGraphicsStatus, }; use termwiz::escape::osc::{ ChangeColorPair, ColorOrQuery, FinalTermSemanticPrompt, ITermFileData, ITermProprietary, @@ -1778,6 +1779,48 @@ impl TerminalState { self.writer.write(b"\x1b[0n").ok(); self.writer.flush().ok(); } + Device::XtSmGraphics(g) => { + let response = if matches!(g.item, XtSmGraphicsItem::Unspecified(_)) { + XtSmGraphics { + item: g.item, + action_or_status: XtSmGraphicsStatus::InvalidItem.to_i64(), + value: vec![], + } + } else { + match g.action() { + None | Some(XtSmGraphicsAction::SetToValue) => XtSmGraphics { + item: g.item, + action_or_status: XtSmGraphicsStatus::InvalidAction.to_i64(), + value: vec![], + }, + Some(XtSmGraphicsAction::ResetToDefault) => XtSmGraphics { + item: g.item, + action_or_status: XtSmGraphicsStatus::Success.to_i64(), + value: vec![], + }, + Some(XtSmGraphicsAction::ReadMaximumAllowedValue) + | Some(XtSmGraphicsAction::ReadAttribute) => match g.item { + XtSmGraphicsItem::Unspecified(_) => unreachable!("checked above"), + XtSmGraphicsItem::NumberOfColorRegisters => XtSmGraphics { + item: g.item, + action_or_status: XtSmGraphicsStatus::Success.to_i64(), + value: vec![65536], + }, + XtSmGraphicsItem::RegisGraphicsGeometry + | XtSmGraphicsItem::SixelGraphicsGeometry => XtSmGraphics { + item: g.item, + action_or_status: XtSmGraphicsStatus::Success.to_i64(), + value: vec![self.pixel_width as i64, self.pixel_height as i64], + }, + }, + } + }; + + let dev = Device::XtSmGraphics(response); + + write!(self.writer, "\x1b[{}", dev).ok(); + self.writer.flush().ok(); + } } } diff --git a/termwiz/src/escape/csi.rs b/termwiz/src/escape/csi.rs index 357e05e3d..d084325b8 100644 --- a/termwiz/src/escape/csi.rs +++ b/termwiz/src/escape/csi.rs @@ -169,6 +169,115 @@ pub enum DeviceAttributes { Vt420(DeviceAttributeFlags), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum XtSmGraphicsItem { + NumberOfColorRegisters, + SixelGraphicsGeometry, + RegisGraphicsGeometry, + Unspecified(i64), +} + +impl Display for XtSmGraphicsItem { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + match self { + Self::NumberOfColorRegisters => write!(f, "1"), + Self::SixelGraphicsGeometry => write!(f, "2"), + Self::RegisGraphicsGeometry => write!(f, "3"), + Self::Unspecified(n) => write!(f, "{}", n), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum XtSmGraphicsAction { + ReadAttribute, + ResetToDefault, + SetToValue, + ReadMaximumAllowedValue, +} + +impl XtSmGraphicsAction { + pub fn to_i64(&self) -> i64 { + match self { + Self::ReadAttribute => 1, + Self::ResetToDefault => 2, + Self::SetToValue => 3, + Self::ReadMaximumAllowedValue => 4, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum XtSmGraphicsStatus { + Success, + InvalidItem, + InvalidAction, + Failure, +} + +impl XtSmGraphicsStatus { + pub fn to_i64(&self) -> i64 { + match self { + Self::Success => 0, + Self::InvalidItem => 1, + Self::InvalidAction => 2, + Self::Failure => 3, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct XtSmGraphics { + pub item: XtSmGraphicsItem, + pub action_or_status: i64, + pub value: Vec, +} + +impl XtSmGraphics { + pub fn action(&self) -> Option { + match self.action_or_status { + 1 => Some(XtSmGraphicsAction::ReadAttribute), + 2 => Some(XtSmGraphicsAction::ResetToDefault), + 3 => Some(XtSmGraphicsAction::SetToValue), + 4 => Some(XtSmGraphicsAction::ReadMaximumAllowedValue), + _ => None, + } + } + + pub fn status(&self) -> Option { + match self.action_or_status { + 0 => Some(XtSmGraphicsStatus::Success), + 1 => Some(XtSmGraphicsStatus::InvalidItem), + 2 => Some(XtSmGraphicsStatus::InvalidAction), + 3 => Some(XtSmGraphicsStatus::Failure), + _ => None, + } + } + + pub fn parse(params: &[CsiParam]) -> Result { + Ok(CSI::Device(Box::new(Device::XtSmGraphics(XtSmGraphics { + item: match params.get(0).ok_or(())? { + CsiParam::Integer(1) => XtSmGraphicsItem::NumberOfColorRegisters, + CsiParam::Integer(2) => XtSmGraphicsItem::SixelGraphicsGeometry, + CsiParam::Integer(3) => XtSmGraphicsItem::RegisGraphicsGeometry, + CsiParam::Integer(n) => XtSmGraphicsItem::Unspecified(*n), + _ => return Err(()), + }, + action_or_status: match params.get(1).ok_or(())? { + CsiParam::Integer(n) => *n, + _ => return Err(()), + }, + value: params[2..] + .iter() + .filter_map(|p| match p { + CsiParam::Integer(n) => Some(*n), + _ => None, + }) + .collect(), + })))) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Device { DeviceAttributes(DeviceAttributes), @@ -180,6 +289,7 @@ pub enum Device { /// https://github.com/mintty/mintty/issues/881 /// https://gitlab.gnome.org/GNOME/vte/-/issues/235 RequestTerminalNameAndVersion, + XtSmGraphics(XtSmGraphics), } impl Display for Device { @@ -198,6 +308,13 @@ impl Display for Device { Device::RequestSecondaryDeviceAttributes => write!(f, ">c")?, Device::RequestTerminalNameAndVersion => write!(f, ">q")?, Device::StatusReport => write!(f, "5n")?, + Device::XtSmGraphics(g) => { + write!(f, "?{};{}", g.item, g.action_or_status)?; + for v in &g.value { + write!(f, ";{}", v)?; + } + write!(f, "S")?; + } }; Ok(()) } @@ -1369,6 +1486,7 @@ impl<'a> CSIParser<'a> { ('P', &[]) => parse!(Edit, DeleteCharacter, params), ('R', &[]) => parse!(Cursor, ActivePositionReport, line, col, params), ('S', &[]) => parse!(Edit, ScrollUp, params), + ('S', &[b'?']) => XtSmGraphics::parse(params), ('T', &[]) => parse!(Edit, ScrollDown, params), ('W', &[]) => parse!(Cursor, TabulationControl, params), ('X', &[]) => parse!(Edit, EraseCharacter, params),