mirror of
https://github.com/wez/wezterm.git
synced 2024-11-25 21:07:39 +03:00
vtparse: parse APC sequences
These were parsed but swallowed. This commit expands the transitions to be able to track the APC start, data and end and then adds an `apc_dispatch` method to allow capturing APC sequences. APC sequences are used in the kitty image protocol. refs: #986
This commit is contained in:
parent
227e375a5f
commit
de1298f8b2
@ -185,6 +185,10 @@ impl<'a, F: FnMut(Action)> VTActor for Performer<'a, F> {
|
||||
}
|
||||
}
|
||||
|
||||
fn apc_dispatch(&mut self, _data: Vec<u8>) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
fn dcs_hook(
|
||||
&mut self,
|
||||
byte: u8,
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
pub enum Action {
|
||||
None = 0,
|
||||
Ignore = 1,
|
||||
@ -18,19 +19,20 @@ pub enum Action {
|
||||
OscPut = 13,
|
||||
OscEnd = 14,
|
||||
Utf8 = 15,
|
||||
ApcStart = 16,
|
||||
ApcPut = 17,
|
||||
ApcEnd = 18,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn from_u8(v: u8) -> Self {
|
||||
pub fn from_u16(v: u16) -> Self {
|
||||
unsafe { std::mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
pub enum State {
|
||||
Ground = 0,
|
||||
Escape = 1,
|
||||
@ -45,15 +47,16 @@ pub enum State {
|
||||
DcsPassthrough = 10,
|
||||
DcsIgnore = 11,
|
||||
OscString = 12,
|
||||
SosPmApcString = 13,
|
||||
Anywhere = 14,
|
||||
Utf8Sequence = 15,
|
||||
SosPmString = 13,
|
||||
ApcString = 14,
|
||||
// Special states, always last (no tables for these)
|
||||
Anywhere = 15,
|
||||
Utf8Sequence = 16,
|
||||
}
|
||||
|
||||
impl State {
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn from_u8(v: u8) -> Self {
|
||||
pub fn from_u16(v: u16) -> Self {
|
||||
unsafe { std::mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ fn lookup(state: State, b: u8) -> (Action, State) {
|
||||
.get_unchecked(state as usize)
|
||||
.get_unchecked(b as usize)
|
||||
};
|
||||
(Action::from_u8(v >> 4), State::from_u8(v & 0xf))
|
||||
(Action::from_u16(v >> 8), State::from_u16(v & 0xff))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -158,6 +158,10 @@ pub trait VTActor {
|
||||
/// that were passed as semicolon separated parameters to the operating
|
||||
/// system command.
|
||||
fn osc_dispatch(&mut self, params: &[&[u8]]);
|
||||
|
||||
/// Called when an APC string is terminated by ST
|
||||
/// `data` is the data contained within the APC sequence.
|
||||
fn apc_dispatch(&mut self, data: Vec<u8>);
|
||||
}
|
||||
|
||||
/// `VTAction` is an alternative way to work with the parser; rather
|
||||
@ -187,6 +191,7 @@ pub enum VTAction {
|
||||
byte: u8,
|
||||
},
|
||||
OscDispatch(Vec<Vec<u8>>),
|
||||
ApcDispatch(Vec<u8>),
|
||||
}
|
||||
|
||||
/// This is an implementation of `VTActor` that captures the events
|
||||
@ -273,6 +278,10 @@ impl VTActor for CollectingVTActor {
|
||||
params.iter().map(|i| i.to_vec()).collect(),
|
||||
));
|
||||
}
|
||||
|
||||
fn apc_dispatch(&mut self, data: Vec<u8>) {
|
||||
self.actions.push(VTAction::ApcDispatch(data));
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_INTERMEDIATES: usize = 2;
|
||||
@ -324,6 +333,7 @@ pub struct VTParser {
|
||||
num_params: usize,
|
||||
current_param: Option<CsiParam>,
|
||||
params_full: bool,
|
||||
apc_data: Vec<u8>,
|
||||
|
||||
utf8_parser: Utf8Parser,
|
||||
utf8_return_state: State,
|
||||
@ -409,6 +419,7 @@ impl VTParser {
|
||||
current_param: None,
|
||||
|
||||
utf8_parser: Utf8Parser::new(),
|
||||
apc_data: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,6 +476,7 @@ impl VTParser {
|
||||
self.num_params = 0;
|
||||
self.params_full = false;
|
||||
self.current_param.take();
|
||||
self.apc_data.clear();
|
||||
}
|
||||
Action::Collect => {
|
||||
if self.num_intermediates < MAX_INTERMEDIATES {
|
||||
@ -562,6 +574,16 @@ impl VTParser {
|
||||
}
|
||||
}
|
||||
|
||||
Action::ApcStart => {
|
||||
self.apc_data.clear();
|
||||
}
|
||||
Action::ApcPut => {
|
||||
self.apc_data.push(param);
|
||||
}
|
||||
Action::ApcEnd => {
|
||||
actor.apc_dispatch(std::mem::take(&mut self.apc_data));
|
||||
}
|
||||
|
||||
Action::Utf8 => self.next_utf8(actor, param),
|
||||
}
|
||||
}
|
||||
@ -1032,6 +1054,22 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_img() {
|
||||
assert_eq!(
|
||||
parse_as_vec("\x1b_Gf=24,s=10,v=20;payload\x1b\\".as_bytes()),
|
||||
vec![
|
||||
VTAction::ApcDispatch(b"Gf=24,s=10,v=20;payload".to_vec()),
|
||||
VTAction::EscDispatch {
|
||||
params: vec![],
|
||||
intermediates: vec![],
|
||||
ignored_excess_intermediates: false,
|
||||
byte: b'\\',
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sixel() {
|
||||
assert_eq!(
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
use crate::enums::{Action, State};
|
||||
|
||||
/// Apply all u8 values to `fn(u8) -> u8`, return `[u8; 256]`.
|
||||
/// Apply all u8 values to `fn(u8) -> u16`, return `[u16; 256]`.
|
||||
macro_rules! define_table {
|
||||
( $func:tt ) => {{
|
||||
const fn gen() -> [u8; 256] {
|
||||
const fn gen() -> [u16; 256] {
|
||||
let mut arr = [0; 256];
|
||||
|
||||
let mut i = 0;
|
||||
@ -20,11 +20,11 @@ macro_rules! define_table {
|
||||
}};
|
||||
}
|
||||
|
||||
const fn pack(action: Action, state: State) -> u8 {
|
||||
((action as u8) << 4) | (state as u8)
|
||||
const fn pack(action: Action, state: State) -> u16 {
|
||||
((action as u16) << 8) | (state as u16)
|
||||
}
|
||||
|
||||
const fn anywhere_or(i: u8, state: State) -> u8 {
|
||||
const fn anywhere_or(i: u8, state: State) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -36,9 +36,9 @@ const fn anywhere_or(i: u8, state: State) -> u8 {
|
||||
0x9a => pack(Execute, Ground),
|
||||
0x9c => pack(None, Ground),
|
||||
0x1b => pack(None, Escape),
|
||||
0x98 => pack(None, SosPmApcString),
|
||||
0x9e => pack(None, SosPmApcString),
|
||||
0x9f => pack(None, SosPmApcString),
|
||||
0x98 => pack(None, SosPmString),
|
||||
0x9e => pack(None, SosPmString),
|
||||
0x9f => pack(None, SosPmString),
|
||||
0x90 => pack(None, DcsEntry),
|
||||
0x9d => pack(None, OscString),
|
||||
0x9b => pack(None, CsiEntry),
|
||||
@ -46,7 +46,7 @@ const fn anywhere_or(i: u8, state: State) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn ground(i: u8) -> u8 {
|
||||
const fn ground(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -65,7 +65,7 @@ const fn ground(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn escape(i: u8) -> u8 {
|
||||
const fn escape(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -83,14 +83,14 @@ const fn escape(i: u8) -> u8 {
|
||||
0x5b => pack(None, CsiEntry),
|
||||
0x5d => pack(None, OscString),
|
||||
0x50 => pack(None, DcsEntry),
|
||||
0x58 => pack(None, SosPmApcString),
|
||||
0x5e => pack(None, SosPmApcString),
|
||||
0x5f => pack(None, SosPmApcString),
|
||||
0x58 => pack(None, SosPmString),
|
||||
0x5e => pack(None, SosPmString),
|
||||
0x5f => pack(None, ApcString),
|
||||
_ => anywhere_or(i, Escape),
|
||||
}
|
||||
}
|
||||
|
||||
const fn escape_intermediate(i: u8) -> u8 {
|
||||
const fn escape_intermediate(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -104,7 +104,7 @@ const fn escape_intermediate(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn csi_entry(i: u8) -> u8 {
|
||||
const fn csi_entry(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -122,7 +122,7 @@ const fn csi_entry(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn csi_param(i: u8) -> u8 {
|
||||
const fn csi_param(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -138,7 +138,7 @@ const fn csi_param(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn csi_intermediate(i: u8) -> u8 {
|
||||
const fn csi_intermediate(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -153,7 +153,7 @@ const fn csi_intermediate(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn csi_ignore(i: u8) -> u8 {
|
||||
const fn csi_ignore(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -167,7 +167,7 @@ const fn csi_ignore(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn dcs_entry(i: u8) -> u8 {
|
||||
const fn dcs_entry(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -185,7 +185,7 @@ const fn dcs_entry(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn dcs_param(i: u8) -> u8 {
|
||||
const fn dcs_param(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -203,7 +203,7 @@ const fn dcs_param(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn dcs_intermediate(i: u8) -> u8 {
|
||||
const fn dcs_intermediate(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -218,7 +218,7 @@ const fn dcs_intermediate(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn dcs_passthrough(i: u8) -> u8 {
|
||||
const fn dcs_passthrough(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -231,7 +231,7 @@ const fn dcs_passthrough(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn dcs_ignore(i: u8) -> u8 {
|
||||
const fn dcs_ignore(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -243,7 +243,7 @@ const fn dcs_ignore(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn osc_string(i: u8) -> u8 {
|
||||
const fn osc_string(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
@ -266,19 +266,31 @@ const fn osc_string(i: u8) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn sos_pm_apc_string(i: u8) -> u8 {
|
||||
const fn sos_pm_string(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
0x00..=0x17 => pack(Ignore, SosPmApcString),
|
||||
0x19 => pack(Ignore, SosPmApcString),
|
||||
0x1c..=0x1f => pack(Ignore, SosPmApcString),
|
||||
0x20..=0x7f => pack(Ignore, SosPmApcString),
|
||||
_ => anywhere_or(i, SosPmApcString),
|
||||
0x00..=0x17 => pack(Ignore, SosPmString),
|
||||
0x19 => pack(Ignore, SosPmString),
|
||||
0x1c..=0x1f => pack(Ignore, SosPmString),
|
||||
0x20..=0x7f => pack(Ignore, SosPmString),
|
||||
_ => anywhere_or(i, SosPmString),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) static TRANSITIONS: [[u8; 256]; 14] = [
|
||||
const fn apc_string(i: u8) -> u16 {
|
||||
use Action::*;
|
||||
use State::*;
|
||||
match i {
|
||||
0x00..=0x17 => pack(ApcPut, ApcString),
|
||||
0x19 => pack(ApcPut, ApcString),
|
||||
0x1c..=0x1f => pack(ApcPut, ApcString),
|
||||
0x20..=0x7f => pack(ApcPut, ApcString),
|
||||
_ => anywhere_or(i, ApcString),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) static TRANSITIONS: [[u16; 256]; 15] = [
|
||||
define_table!(ground),
|
||||
define_table!(escape),
|
||||
define_table!(escape_intermediate),
|
||||
@ -292,10 +304,11 @@ pub(crate) static TRANSITIONS: [[u8; 256]; 14] = [
|
||||
define_table!(dcs_passthrough),
|
||||
define_table!(dcs_ignore),
|
||||
define_table!(osc_string),
|
||||
define_table!(sos_pm_apc_string),
|
||||
define_table!(sos_pm_string),
|
||||
define_table!(apc_string),
|
||||
];
|
||||
|
||||
pub(crate) static ENTRY: [Action; 14] = [
|
||||
pub(crate) static ENTRY: [Action; 15] = [
|
||||
Action::None, // Ground
|
||||
Action::Clear, // Escape
|
||||
Action::None, // EscapeIntermediate
|
||||
@ -309,10 +322,11 @@ pub(crate) static ENTRY: [Action; 14] = [
|
||||
Action::Hook, // DcsPassthrough
|
||||
Action::None, // DcsIgnore
|
||||
Action::OscStart, // OscString
|
||||
Action::None, // SosPmApcString
|
||||
Action::None, // SosPmString
|
||||
Action::ApcStart, // ApcString
|
||||
];
|
||||
|
||||
pub(crate) static EXIT: [Action; 14] = [
|
||||
pub(crate) static EXIT: [Action; 15] = [
|
||||
Action::None, // Ground
|
||||
Action::None, // Escape
|
||||
Action::None, // EscapeIntermediate
|
||||
@ -326,7 +340,8 @@ pub(crate) static EXIT: [Action; 14] = [
|
||||
Action::Unhook, // DcsPassthrough
|
||||
Action::None, // DcsIgnore
|
||||
Action::OscEnd, // OscString
|
||||
Action::None, // SosPmApcString
|
||||
Action::None, // SosPmString
|
||||
Action::ApcEnd, // ApcString
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
@ -342,7 +357,7 @@ mod tests {
|
||||
hash(&v, 5381, 33), // djb2
|
||||
hash(&v, 0, 65599), // sdbm
|
||||
),
|
||||
(14021, 626090, 11884276359605205711, 6929800990073628062)
|
||||
(17385, 799944, 12647816782590382477, 3641575052870461598)
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user