1#![allow(clippy::useless_attribute)]
4#![allow(clippy::upper_case_acronyms)]
5#![cfg_attr(not(feature = "std"), no_std)]
6#[cfg(feature = "tmux_cc")]
11use crate::tmux_cc::Event;
12use core::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
13use num_derive::*;
14use wezterm_color_types::LinearRgba;
15
16#[cfg_attr(not(feature = "std"), macro_use)]
17extern crate alloc;
18
19mod allocate;
20use allocate::*;
21
22pub mod apc;
23pub mod color;
24pub mod csi;
25pub mod error;
26pub mod esc;
27pub mod hyperlink;
28pub mod osc;
29pub mod parser;
30#[cfg(feature = "tmux_cc")]
31pub mod tmux_cc;
32
33pub use self::apc::KittyImage;
34pub use self::csi::CSI;
35pub use self::error::{Error, Result};
36pub use self::esc::{Esc, EscCode};
37pub use self::osc::OperatingSystemCommand;
38
39use vtparse::CsiParam;
40
41#[derive(Debug, Clone, PartialEq)]
42pub enum Action {
43 Print(char),
45 PrintString(String),
47 Control(ControlCode),
49 DeviceControl(DeviceControlMode),
51 OperatingSystemCommand(Box<OperatingSystemCommand>),
55 CSI(CSI),
56 Esc(Esc),
57 Sixel(Box<Sixel>),
58 XtGetTcap(Vec<String>),
61 KittyImage(Box<KittyImage>),
62}
63
64impl Action {
65 pub fn append_to(self, dest: &mut Vec<Self>) {
70 if let Action::Print(c) = &self {
71 match dest.last_mut() {
72 Some(Action::PrintString(s)) => {
73 s.push(*c);
74 return;
75 }
76 Some(Action::Print(prior)) => {
77 let mut s = prior.to_string();
78 dest.pop();
79 s.push(*c);
80 dest.push(Action::PrintString(s));
81 return;
82 }
83 _ => {}
84 }
85 }
86 dest.push(self);
87 }
88}
89
90#[cfg(all(test, target_pointer_width = "64"))]
91#[test]
92fn action_size() {
93 assert_eq!(core::mem::size_of::<Action>(), 32);
94 assert_eq!(core::mem::size_of::<DeviceControlMode>(), 16);
95 assert_eq!(core::mem::size_of::<ControlCode>(), 1);
96 assert_eq!(core::mem::size_of::<CSI>(), 32);
97 assert_eq!(core::mem::size_of::<Esc>(), 4);
98}
99
100impl Display for Action {
103 fn fmt(&self, f: &mut Formatter) -> FmtResult {
104 match self {
105 Action::Print(c) => write!(f, "{}", c),
106 Action::PrintString(s) => write!(f, "{}", s),
107 Action::Control(c) => f.write_char(*c as u8 as char),
108 Action::DeviceControl(c) => c.fmt(f),
109 Action::OperatingSystemCommand(osc) => osc.fmt(f),
110 Action::CSI(csi) => csi.fmt(f),
111 Action::Esc(esc) => esc.fmt(f),
112 Action::Sixel(sixel) => sixel.fmt(f),
113 Action::XtGetTcap(names) => {
114 write!(f, "\x1bP+q")?;
115 for (i, name) in names.iter().enumerate() {
116 if i > 0 {
117 write!(f, ";")?;
118 }
119 for &b in name.as_bytes() {
120 write!(f, "{:x}", b)?;
121 }
122 }
123
124 Ok(())
125 }
126 Action::KittyImage(img) => img.fmt(f),
127 }
128 }
129}
130
131#[derive(Clone, PartialEq, Eq)]
137pub struct ShortDeviceControl {
138 pub params: Vec<i64>,
140 pub intermediates: Vec<u8>,
142 pub byte: u8,
144 pub data: Vec<u8>,
146}
147
148impl core::fmt::Debug for ShortDeviceControl {
149 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
150 write!(
151 fmt,
152 "ShortDeviceControl(params: {:?}, intermediates: [",
153 &self.params
154 )?;
155 for b in &self.intermediates {
156 write!(fmt, "{:?} 0x{:x}, ", *b as char, *b)?;
157 }
158 write!(
159 fmt,
160 "], byte: {:?} 0x{:x}, data=[",
161 self.byte as char, self.byte
162 )?;
163
164 for b in &self.data {
165 write!(fmt, "{:?} 0x{:x}, ", *b as char, *b)?;
166 }
167
168 write!(fmt, ")")
169 }
170}
171
172impl Display for ShortDeviceControl {
173 fn fmt(&self, f: &mut Formatter) -> FmtResult {
174 write!(f, "\x1bP")?;
175 for (idx, p) in self.params.iter().enumerate() {
176 if idx > 0 {
177 write!(f, ";")?;
178 }
179 write!(f, "{}", p)?;
180 }
181 for b in &self.intermediates {
182 f.write_char(*b as char)?;
183 }
184 f.write_char(self.byte as char)?;
185 for b in &self.data {
186 f.write_char(*b as char)?;
187 }
188 write!(f, "\x1b\\")
189 }
190}
191
192#[derive(Clone, PartialEq, Eq)]
193pub struct EnterDeviceControlMode {
194 pub byte: u8,
196 pub params: Vec<i64>,
197 pub intermediates: Vec<u8>,
198 pub ignored_extra_intermediates: bool,
201}
202
203impl core::fmt::Debug for EnterDeviceControlMode {
204 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
205 write!(
206 fmt,
207 "EnterDeviceControlMode(params: {:?}, intermediates: [",
208 &self.params
209 )?;
210 for b in &self.intermediates {
211 write!(fmt, "{:?} 0x{:x}, ", *b as char, *b)?;
212 }
213 write!(
214 fmt,
215 "], byte: {:?} 0x{:x}, ignored_extra_intermediates={})",
216 self.byte as char, self.byte, self.ignored_extra_intermediates
217 )
218 }
219}
220
221#[derive(Clone, PartialEq, Eq)]
222pub enum DeviceControlMode {
223 Enter(Box<EnterDeviceControlMode>),
229 Exit,
231 Data(u8),
233 ShortDeviceControl(Box<ShortDeviceControl>),
235 #[cfg(feature = "tmux_cc")]
237 TmuxEvents(Box<Vec<Event>>),
238}
239
240impl Display for DeviceControlMode {
241 fn fmt(&self, f: &mut Formatter) -> FmtResult {
242 match self {
243 Self::Enter(mode) => {
244 write!(f, "\x1bP")?;
245 for (idx, p) in mode.params.iter().enumerate() {
246 if idx > 0 {
247 write!(f, ";")?;
248 }
249 write!(f, "{}", p)?;
250 }
251 for b in &mode.intermediates {
252 f.write_char(*b as char)?;
253 }
254 f.write_char(mode.byte as char)
255 }
256 Self::Exit => Ok(()),
259 Self::Data(c) => f.write_char(*c as char),
260 Self::ShortDeviceControl(s) => s.fmt(f),
261 #[cfg(feature = "tmux_cc")]
262 Self::TmuxEvents(_) => write!(f, "tmux event"),
263 }
264 }
265}
266
267impl core::fmt::Debug for DeviceControlMode {
268 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
269 match self {
270 Self::Enter(mode) => write!(fmt, "Enter({:?})", mode),
271 Self::Exit => write!(fmt, "Exit"),
272 Self::Data(b) => write!(fmt, "Data({:?} 0x{:x})", *b as char, *b),
273 Self::ShortDeviceControl(s) => write!(fmt, "ShortDeviceControl({:?})", s),
274 #[cfg(feature = "tmux_cc")]
275 Self::TmuxEvents(_) => write!(fmt, "tmux event"),
276 }
277 }
278}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
282pub struct Sixel {
283 pub pan: i64,
285
286 pub pad: i64,
288
289 pub pixel_width: Option<u32>,
291
292 pub pixel_height: Option<u32>,
294
295 pub background_is_transparent: bool,
299
300 pub horizontal_grid_size: Option<i64>,
302
303 pub data: Vec<SixelData>,
305}
306
307impl Sixel {
308 pub fn dimensions(&self) -> (u32, u32) {
310 if let (Some(w), Some(h)) = (self.pixel_width, self.pixel_height) {
311 return (w, h);
312 }
313
314 let mut max_x = 0;
316 let mut max_y = 0;
317 let mut x: u32 = 0;
318 let mut rows: u32 = 1;
319
320 for d in &self.data {
321 match d {
322 SixelData::Data(_) => {
323 max_y = max_y.max(rows * 6);
324 x = x.saturating_add(1);
325 max_x = max_x.max(x);
326 }
327 SixelData::Repeat { repeat_count, .. } => {
328 max_y = max_y.max(rows * 6);
329 x = x.saturating_add(*repeat_count);
330 max_x = max_x.max(x);
331 }
332 SixelData::SelectColorMapEntry(_)
333 | SixelData::DefineColorMapRGB { .. }
334 | SixelData::DefineColorMapHSL { .. } => {}
335 SixelData::NewLine => {
336 max_x = max_x.max(x);
337 x = 0;
338 rows = rows.saturating_add(1);
339 }
340 SixelData::CarriageReturn => {
341 max_x = max_x.max(x);
342 x = 0;
343 }
344 }
345 }
346
347 (max_x, max_y)
348 }
349}
350
351impl Display for Sixel {
352 fn fmt(&self, f: &mut Formatter) -> FmtResult {
353 if self.pixel_width.is_some() {
354 write!(
355 f,
356 "\x1bP;{}{}q\"{};{};{};{}",
357 if self.background_is_transparent { 1 } else { 0 },
358 match self.horizontal_grid_size {
359 Some(h) => format!(";{}", h),
360 None => "".to_string(),
361 },
362 self.pan,
363 self.pad,
364 self.pixel_width.unwrap_or(0),
365 self.pixel_height.unwrap_or(0)
366 )?;
367 } else {
368 write!(
369 f,
370 "\x1bP{};{}{}q",
371 match (self.pan, self.pad) {
372 (2, 1) => 0,
373 (5, 1) => 2,
374 (3, 1) => 3,
375 (1, 1) => 7,
376 _ => {
377 log::error!("bad pad/pan combo: {:?}", self);
378 return Err(core::fmt::Error);
379 }
380 },
381 if self.background_is_transparent { 1 } else { 0 },
382 match self.horizontal_grid_size {
383 Some(h) => format!(";{}", h),
384 None => "".to_string(),
385 },
386 )?;
387 }
388 for d in &self.data {
389 d.fmt(f)?;
390 }
391 Ok(())
394 }
395}
396
397pub type SixelValue = u8;
401
402#[derive(Debug, Clone, PartialEq, Eq)]
403pub enum SixelData {
404 Data(SixelValue),
406
407 Repeat { repeat_count: u32, data: SixelValue },
410
411 DefineColorMapRGB {
414 color_number: u16,
415 rgb: crate::color::RgbColor,
416 },
417
418 DefineColorMapHSL {
419 color_number: u16,
420 hue_angle: u16,
422 lightness: u8,
424 saturation: u8,
426 },
427
428 SelectColorMapEntry(u16),
430
431 CarriageReturn,
434
435 NewLine,
438}
439
440impl Display for SixelData {
441 fn fmt(&self, f: &mut Formatter) -> FmtResult {
442 match self {
443 Self::Data(value) => write!(f, "{}", (value + 0x3f) as char),
444 Self::Repeat { repeat_count, data } => {
445 write!(f, "!{}{}", repeat_count, (data + 0x3f) as char)
446 }
447 Self::DefineColorMapRGB { color_number, rgb } => {
448 let LinearRgba(r, g, b, _) = rgb.to_linear_tuple_rgba();
449 write!(
450 f,
451 "#{};2;{};{};{}",
452 color_number,
453 (r * 100.) as u8,
454 (g * 100.) as u8,
455 (b * 100.0) as u8
456 )
457 }
458 Self::DefineColorMapHSL {
459 color_number,
460 hue_angle,
461 lightness,
462 saturation,
463 } => write!(
464 f,
465 "#{};1;{};{};{}",
466 color_number, hue_angle, lightness, saturation
467 ),
468 Self::SelectColorMapEntry(n) => write!(f, "#{}", n),
469 Self::CarriageReturn => write!(f, "$"),
470 Self::NewLine => write!(f, "-"),
471 }
472 }
473}
474
475#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)]
477#[repr(u8)]
478pub enum ControlCode {
479 Null = 0,
480 StartOfHeading = 1,
481 StartOfText = 2,
482 EndOfText = 3,
483 EndOfTransmission = 4,
484 Enquiry = 5,
485 Acknowledge = 6,
486 Bell = 7,
487 Backspace = 8,
488 HorizontalTab = b'\t',
489 LineFeed = b'\n',
490 VerticalTab = 0xb,
491 FormFeed = 0xc,
492 CarriageReturn = b'\r',
493 ShiftOut = 0xe,
494 ShiftIn = 0xf,
495 DataLinkEscape = 0x10,
496 DeviceControlOne = 0x11,
497 DeviceControlTwo = 0x12,
498 DeviceControlThree = 0x13,
499 DeviceControlFour = 0x14,
500 NegativeAcknowledge = 0x15,
501 SynchronousIdle = 0x16,
502 EndOfTransmissionBlock = 0x17,
503 Cancel = 0x18,
504 EndOfMedium = 0x19,
505 Substitute = 0x1a,
506 Escape = 0x1b,
507 FileSeparator = 0x1c,
508 GroupSeparator = 0x1d,
509 RecordSeparator = 0x1e,
510 UnitSeparator = 0x1f,
511
512 BPH = 0x82,
514 NBH = 0x83,
515 IND = 0x84,
516 NEL = 0x85,
517 SSA = 0x86,
518 ESA = 0x87,
519 HTS = 0x88,
520 HTJ = 0x89,
521 VTS = 0x8a,
522 PLD = 0x8b,
523 PLU = 0x8c,
524 RI = 0x8d,
525 SS2 = 0x8e,
526 SS3 = 0x8f,
527 DCS = 0x90,
528 PU1 = 0x91,
529 PU2 = 0x92,
530 STS = 0x93,
531 CCH = 0x94,
532 MW = 0x95,
533 SPA = 0x96,
534 EPA = 0x97,
535 SOS = 0x98,
536 SCI = 0x9a,
537 CSI = 0x9b,
538 ST = 0x9c,
539 OSC = 0x9d,
540 PM = 0x9e,
541 APC = 0x9f,
542}
543
544#[derive(Debug, Clone, Copy, PartialEq, Eq)]
547pub struct OneBased {
548 value: u32,
549}
550
551impl OneBased {
552 pub fn new(value: u32) -> Self {
553 debug_assert!(
554 value != 0,
555 "programmer error: deliberately assigning zero to a OneBased"
556 );
557 Self { value }
558 }
559
560 pub fn from_zero_based(value: u32) -> Self {
561 Self { value: value + 1 }
562 }
563
564 pub fn from_esc_param(v: &CsiParam) -> core::result::Result<Self, ()> {
567 match v {
568 CsiParam::Integer(v) if *v == 0 => Ok(Self {
569 value: num_traits::one(),
570 }),
571 CsiParam::Integer(v) if *v > 0 && *v <= i64::from(u32::max_value()) => {
572 Ok(Self { value: *v as u32 })
573 }
574 _ => Err(()),
575 }
576 }
577
578 pub fn from_esc_param_with_big_default(v: &CsiParam) -> core::result::Result<Self, ()> {
581 match v {
582 CsiParam::Integer(v) if *v == 0 => Ok(Self {
583 value: u32::max_value(),
584 }),
585 CsiParam::Integer(v) if *v > 0 && *v <= i64::from(u32::max_value()) => {
586 Ok(Self { value: *v as u32 })
587 }
588 _ => Err(()),
589 }
590 }
591
592 pub fn from_optional_esc_param(o: Option<&CsiParam>) -> core::result::Result<Self, ()> {
594 Self::from_esc_param(o.unwrap_or(&CsiParam::Integer(1)))
595 }
596
597 pub fn as_zero_based(self) -> u32 {
599 self.value.saturating_sub(1)
600 }
601
602 pub fn as_one_based(self) -> u32 {
603 self.value
604 }
605}
606
607impl Display for OneBased {
608 fn fmt(&self, f: &mut Formatter) -> FmtResult {
609 self.value.fmt(f)
610 }
611}