1mod cell;
9mod colour;
10mod commands;
11mod csi_helpers;
12mod dispatch;
13pub mod mode;
14mod params;
15mod passthrough;
16mod sgr;
17mod states;
18mod tables;
19#[cfg(test)]
20mod tests;
21mod writer;
22
23pub use cell::{CellState, GridAttr, SavedState};
24pub use colour::{
25 colour_join_rgb, Colour, COLOUR_DEFAULT, COLOUR_FLAG_256, COLOUR_FLAG_RGB, COLOUR_NONE,
26 COLOUR_TERMINAL,
27};
28pub use dispatch::{CsiCommand, DcsPayload, EscCommand, InputAction, OscCommand, ScreenWriter};
29pub use params::{InputParam, ParamType};
30pub use states::InputState;
31
32use params::ParamList;
33use states::Transition;
34
35use crate::terminal_passthrough::MAX_TERMINAL_PASSTHROUGH_PAYLOAD_BYTES;
36
37const PARAM_LIST_MAX: usize = 24;
39
40const INTERM_BUF_MAX: usize = 4;
42
43const INPUT_BUF_START: usize = 32;
45
46const INPUT_BUF_MAX: usize = 1_048_576;
48
49const PARAM_BUF_MAX: usize = 64;
51
52const INPUT_DISCARD: u32 = 0x1;
54const INPUT_LAST: u32 = 0x2;
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum InputEndType {
60 St,
62 Bel,
64}
65
66pub struct InputParser {
68 state: InputState,
70 flags: u32,
72
73 ch: u8,
75
76 interm_buf: [u8; INTERM_BUF_MAX],
78 interm_len: usize,
79
80 param_buf: [u8; PARAM_BUF_MAX],
82 param_len: usize,
83
84 input_buf: Vec<u8>,
86 input_buf_max: usize,
87 input_end: InputEndType,
89
90 param_list: ParamList,
92
93 cell: CellState,
95 saved: SavedState,
97
98 utf8_buf: [u8; 4],
100 utf8_len: u8,
101 utf8_expected: u8,
102 utf8_started: bool,
103
104 last_char: Option<char>,
106
107 since_ground: Vec<u8>,
109
110 ground_timer_active: bool,
113
114 reply_buf: Vec<u8>,
116 terminal_passthrough_dropped_count: u64,
118}
119
120impl InputParser {
121 #[must_use]
123 pub fn new() -> Self {
124 Self {
125 state: InputState::Ground,
126 flags: 0,
127 ch: 0,
128 interm_buf: [0; INTERM_BUF_MAX],
129 interm_len: 0,
130 param_buf: [0; PARAM_BUF_MAX],
131 param_len: 0,
132 input_buf: Vec::with_capacity(INPUT_BUF_START),
133 input_buf_max: INPUT_BUF_MAX,
134 input_end: InputEndType::St,
135 param_list: ParamList::new(),
136 cell: CellState::default(),
137 saved: SavedState::default(),
138 utf8_buf: [0; 4],
139 utf8_len: 0,
140 utf8_expected: 0,
141 utf8_started: false,
142 last_char: None,
143 since_ground: Vec::new(),
144 ground_timer_active: false,
145 reply_buf: Vec::new(),
146 terminal_passthrough_dropped_count: 0,
147 }
148 }
149
150 pub fn set_input_buffer_limit(&mut self, limit: usize) {
152 self.input_buf_max = limit.max(INPUT_BUF_START);
153 }
154
155 pub(crate) const fn configured_input_buffer_limit(&self) -> usize {
156 self.input_buf_max
157 }
158
159 #[must_use]
161 pub fn state(&self) -> InputState {
162 self.state
163 }
164
165 pub fn take_replies(&mut self) -> Vec<u8> {
167 std::mem::take(&mut self.reply_buf)
168 }
169
170 pub(crate) fn take_terminal_passthrough_dropped_count(&mut self) -> u64 {
172 let dropped = self.terminal_passthrough_dropped_count;
173 self.terminal_passthrough_dropped_count = 0;
174 dropped
175 }
176
177 pub fn take_since_ground(&mut self) -> Vec<u8> {
179 std::mem::take(&mut self.since_ground)
180 }
181
182 #[must_use]
184 pub fn pending_bytes(&self) -> Vec<u8> {
185 if self.state != InputState::Ground {
186 return self.since_ground.clone();
187 }
188 if self.utf8_started {
189 return self.utf8_buf[..usize::from(self.utf8_len)].to_vec();
190 }
191 Vec::new()
192 }
193
194 #[must_use]
196 pub fn ground_timer_active(&self) -> bool {
197 self.ground_timer_active
198 }
199
200 pub fn ground_timer_expired(&mut self) {
202 self.reset_to_ground();
203 }
204
205 pub fn reset_to_ground(&mut self) {
207 self.clear();
208 self.state = InputState::Ground;
209 self.flags = 0;
210 self.enter_ground();
211 }
212
213 #[must_use]
215 pub fn cell_state(&self) -> &CellState {
216 &self.cell
217 }
218
219 pub fn parse<W: ScreenWriter + ?Sized>(&mut self, buf: &[u8], writer: &mut W) {
221 let mut index = 0;
222 while index < buf.len() {
223 if self.state == InputState::Ground && !self.utf8_started {
224 let printable_end = buf[index..]
225 .iter()
226 .position(|byte| !byte.is_ascii_graphic() && *byte != b' ')
227 .map_or(buf.len(), |offset| index + offset);
228 if printable_end > index {
229 self.handle_printable_ascii_run(&buf[index..printable_end], writer);
230 index = printable_end;
231 continue;
232 }
233 if self.handle_ground_c0_fast_path(buf[index], writer) {
234 index += 1;
235 continue;
236 }
237 }
238
239 self.ch = buf[index];
240 let transition = self.find_transition();
241 self.execute_transition(transition, writer);
242 index += 1;
243 }
244 }
245
246 fn handle_printable_ascii_run<W: ScreenWriter + ?Sized>(
247 &mut self,
248 bytes: &[u8],
249 writer: &mut W,
250 ) {
251 debug_assert_eq!(self.state, InputState::Ground);
252 let set = if self.cell.set == 0 {
253 self.cell.g0set
254 } else {
255 self.cell.g1set
256 };
257 let acs = set != 0;
258 writer.collect_add_ascii_run(bytes, &self.cell, acs);
259 if let Some(&last) = bytes.last() {
260 self.last_char = Some(char::from(last));
261 }
262 self.flags |= INPUT_LAST;
263 }
264
265 fn handle_ground_c0_fast_path<W: ScreenWriter + ?Sized>(
266 &mut self,
267 byte: u8,
268 writer: &mut W,
269 ) -> bool {
270 match byte {
271 0x0a..=0x0c => {
272 writer.collect_end();
273 writer.linefeed(false, self.cell.bg());
274 if writer.current_mode() & mode::MODE_CRLF != 0 {
275 writer.carriage_return();
276 }
277 }
278 0x0d => {
279 writer.collect_end();
280 writer.carriage_return();
281 }
282 _ => return false,
283 }
284 self.flags &= !INPUT_LAST;
285 true
286 }
287
288 fn find_transition(&self) -> Transition {
289 self.state.transition_for_byte(self.ch)
290 }
291
292 fn execute_transition<W: ScreenWriter + ?Sized>(&mut self, trans: Transition, writer: &mut W) {
293 if !matches!(
295 trans.handler,
296 states::Handler::Print | states::Handler::TopBitSet
297 ) {
298 writer.collect_end();
299 }
300
301 let skip_state = match trans.handler {
303 states::Handler::None => false,
304 states::Handler::Print => self.handle_print(writer),
305 states::Handler::C0Dispatch => self.handle_c0_dispatch(writer),
306 states::Handler::EscDispatch => self.handle_esc_dispatch(writer),
307 states::Handler::CsiDispatch => self.handle_csi_dispatch(writer),
308 states::Handler::DcsDispatch => self.handle_dcs_dispatch(writer),
309 states::Handler::Intermediate => self.handle_intermediate(),
310 states::Handler::Parameter => self.handle_parameter(),
311 states::Handler::Input => self.handle_input(),
312 states::Handler::TopBitSet => self.handle_top_bit_set(writer),
313 states::Handler::EndBel => self.handle_end_bel(),
314 };
315
316 if skip_state {
317 return;
318 }
319
320 if let Some(next) = trans.next_state {
321 self.set_state(next, writer);
322 }
323
324 if self.state != InputState::Ground && self.since_ground.len() < self.input_buf_max {
326 self.since_ground.push(self.ch);
327 }
328 }
329
330 fn set_state<W: ScreenWriter + ?Sized>(&mut self, next: InputState, writer: &mut W) {
331 self.exit_state(writer);
333 self.state = next;
334 self.enter_state(writer);
336 }
337
338 fn enter_state<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) {
339 match self.state {
340 InputState::Ground => self.enter_ground(),
341 InputState::EscEnter => self.clear(),
342 InputState::CsiEnter => self.clear(),
343 InputState::DcsEnter => self.enter_dcs(),
344 InputState::OscString => self.enter_osc(),
345 InputState::ApcString => self.enter_apc(),
346 InputState::RenameString => self.enter_rename(),
347 InputState::ConsumeSt => self.enter_rename(), _ => {}
349 }
350 let _ = writer; }
352
353 fn exit_state<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) {
354 match self.state {
355 InputState::OscString => self.exit_osc(writer),
356 InputState::ApcString => self.exit_apc(writer),
357 InputState::RenameString => self.exit_rename(writer),
358 _ => {}
359 }
360 }
361
362 fn clear(&mut self) {
363 self.ground_timer_active = false;
364 self.interm_buf = [0; INTERM_BUF_MAX];
365 self.interm_len = 0;
366 self.param_buf = [0; PARAM_BUF_MAX];
367 self.param_len = 0;
368 self.input_buf.clear();
369 self.input_end = InputEndType::St;
370 self.flags &= !INPUT_DISCARD;
371 }
372
373 fn enter_ground(&mut self) {
374 self.ground_timer_active = false;
375 self.since_ground.clear();
376 if self.input_buf.capacity() > INPUT_BUF_START {
378 self.input_buf = Vec::with_capacity(INPUT_BUF_START);
379 }
380 }
381
382 fn enter_dcs(&mut self) {
383 self.clear();
384 self.ground_timer_active = true;
385 self.flags &= !INPUT_LAST;
386 }
387
388 fn enter_osc(&mut self) {
389 self.clear();
390 self.ground_timer_active = true;
391 self.flags &= !INPUT_LAST;
392 }
393
394 fn enter_apc(&mut self) {
395 self.clear();
396 self.ground_timer_active = true;
397 self.flags &= !INPUT_LAST;
398 }
399
400 fn enter_rename(&mut self) {
401 self.clear();
402 self.ground_timer_active = true;
403 self.flags &= !INPUT_LAST;
404 }
405
406 fn exit_osc<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) {
407 if self.flags & INPUT_DISCARD != 0 {
408 return;
409 }
410 dispatch::dispatch_osc(self, writer);
411 }
412
413 fn exit_apc<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) {
414 if self.flags & INPUT_DISCARD != 0 {
415 return;
416 }
417 if passthrough::is_kitty_graphics_apc(&self.input_buf) {
418 writer.apc_passthrough(&self.input_buf);
419 return;
420 }
421 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
422 writer.set_title(&buf);
423 }
424
425 fn exit_rename<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) {
426 if self.flags & INPUT_DISCARD != 0 {
427 return;
428 }
429 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
430 writer.set_window_name(&buf);
431 }
432
433 fn stop_utf8<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) {
435 if self.utf8_started {
436 writer.collect_add('\u{FFFD}', &self.cell);
437 self.utf8_started = false;
438 self.utf8_len = 0;
439 self.utf8_expected = 0;
440 }
441 }
442
443 fn handle_print<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) -> bool {
444 self.stop_utf8(writer);
445
446 let ch = self.ch as char;
447 let set = if self.cell.set == 0 {
448 self.cell.g0set
449 } else {
450 self.cell.g1set
451 };
452
453 writer.collect_add_with_charset(ch, &self.cell, set != 0);
454
455 self.last_char = Some(ch);
456 self.flags |= INPUT_LAST;
457
458 false
459 }
460
461 fn handle_intermediate(&mut self) -> bool {
462 if self.interm_len >= INTERM_BUF_MAX - 1 {
463 self.flags |= INPUT_DISCARD;
464 } else {
465 self.interm_buf[self.interm_len] = self.ch;
466 self.interm_len += 1;
467 }
468 false
469 }
470
471 fn handle_parameter(&mut self) -> bool {
472 if self.param_len >= PARAM_BUF_MAX - 1 {
473 self.flags |= INPUT_DISCARD;
474 } else {
475 self.param_buf[self.param_len] = self.ch;
476 self.param_len += 1;
477 }
478 false
479 }
480
481 fn handle_input(&mut self) -> bool {
482 let escaped_dcs_byte = self.state == InputState::DcsEscape;
483 let bytes_to_push = if escaped_dcs_byte && self.ch != 0x1b {
484 2
485 } else {
486 1
487 };
488 let input_limit = self.input_buffer_limit();
489 if self.input_buf.len() + bytes_to_push >= input_limit {
490 if self.flags & INPUT_DISCARD == 0 && self.is_terminal_passthrough_string() {
491 self.terminal_passthrough_dropped_count =
492 self.terminal_passthrough_dropped_count.saturating_add(1);
493 }
494 self.flags |= INPUT_DISCARD;
495 } else if escaped_dcs_byte && self.ch == 0x1b {
496 self.input_buf.push(0x1b);
497 } else if escaped_dcs_byte {
498 self.input_buf.push(0x1b);
499 self.input_buf.push(self.ch);
500 } else {
501 self.input_buf.push(self.ch);
502 }
503 false
504 }
505
506 fn input_buffer_limit(&self) -> usize {
507 if self.is_terminal_passthrough_string() {
508 return MAX_TERMINAL_PASSTHROUGH_PAYLOAD_BYTES;
509 }
510 self.input_buf_max
511 }
512
513 fn is_terminal_passthrough_string(&self) -> bool {
514 (self.state == InputState::ApcString && passthrough::is_kitty_graphics_apc(&self.input_buf))
515 || (matches!(self.state, InputState::DcsHandler | InputState::DcsEscape)
516 && self.interm_len == 0
517 && (self.input_buf.first() == Some(&b'q') || self.input_buf.starts_with(b"tmux;")))
518 }
519
520 fn handle_end_bel(&mut self) -> bool {
521 self.input_end = InputEndType::Bel;
522 false
523 }
524
525 fn handle_c0_dispatch<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) -> bool {
526 self.stop_utf8(writer);
527 dispatch::dispatch_c0(self, writer);
528 self.flags &= !INPUT_LAST;
529 false
530 }
531
532 fn handle_esc_dispatch<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) -> bool {
533 if self.flags & INPUT_DISCARD != 0 {
534 return false;
535 }
536 dispatch::dispatch_esc(self, writer);
537 self.flags &= !INPUT_LAST;
538 false
539 }
540
541 fn handle_csi_dispatch<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) -> bool {
542 if self.flags & INPUT_DISCARD != 0 {
543 return false;
544 }
545 dispatch::dispatch_csi(self, writer);
546 self.flags &= !INPUT_LAST;
547 false
548 }
549
550 fn handle_dcs_dispatch<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) -> bool {
551 if self.flags & INPUT_DISCARD != 0 {
552 return false;
553 }
554 dispatch::dispatch_dcs(self, writer);
555 false
556 }
557
558 fn handle_top_bit_set<W: ScreenWriter + ?Sized>(&mut self, writer: &mut W) -> bool {
559 self.flags &= !INPUT_LAST;
560
561 if !self.utf8_started {
562 self.utf8_started = true;
563 self.utf8_len = 0;
564 let expected = if self.ch & 0xE0 == 0xC0 {
566 2
567 } else if self.ch & 0xF0 == 0xE0 {
568 3
569 } else if self.ch & 0xF8 == 0xF0 {
570 4
571 } else {
572 self.stop_utf8(writer);
574 return false;
575 };
576 self.utf8_expected = expected;
577 self.utf8_buf[0] = self.ch;
578 self.utf8_len = 1;
579 return false;
580 }
581
582 if self.ch & 0xC0 != 0x80 {
584 self.stop_utf8(writer);
586 if self.ch >= 0x80 {
588 return self.handle_top_bit_set(writer);
589 }
590 return false;
591 }
592
593 self.utf8_buf[self.utf8_len as usize] = self.ch;
594 self.utf8_len += 1;
595
596 if self.utf8_len < self.utf8_expected {
597 return false; }
599
600 self.utf8_started = false;
602 let bytes = &self.utf8_buf[..self.utf8_len as usize];
603 let s = match std::str::from_utf8(bytes) {
604 Ok(s) => s,
605 Err(_) => {
606 writer.collect_add('\u{FFFD}', &self.cell);
607 return false;
608 }
609 };
610 let c = match s.chars().next() {
611 Some(c) => c,
612 None => {
613 writer.collect_add('\u{FFFD}', &self.cell);
614 return false;
615 }
616 };
617
618 writer.collect_add(c, &self.cell);
619
620 self.last_char = Some(c);
621 self.flags |= INPUT_LAST;
622
623 false
624 }
625
626 fn reply(&mut self, s: &str) {
628 self.reply_buf.extend_from_slice(s.as_bytes());
629 }
630
631 fn interm_str(&self) -> &[u8] {
633 &self.interm_buf[..self.interm_len]
634 }
635}
636
637impl Default for InputParser {
638 fn default() -> Self {
639 Self::new()
640 }
641}