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(&mut self, buf: &[u8], writer: &mut dyn ScreenWriter) {
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(&mut self, bytes: &[u8], writer: &mut dyn ScreenWriter) {
247 debug_assert_eq!(self.state, InputState::Ground);
248 let set = if self.cell.set == 0 {
249 self.cell.g0set
250 } else {
251 self.cell.g1set
252 };
253 let acs = set != 0;
254 writer.collect_add_ascii_run(bytes, &self.cell, acs);
255 if let Some(&last) = bytes.last() {
256 self.last_char = Some(char::from(last));
257 }
258 self.flags |= INPUT_LAST;
259 }
260
261 fn handle_ground_c0_fast_path(&mut self, byte: u8, writer: &mut dyn ScreenWriter) -> bool {
262 match byte {
263 0x0a..=0x0c => {
264 writer.collect_end();
265 writer.linefeed(false, self.cell.bg());
266 if writer.current_mode() & mode::MODE_CRLF != 0 {
267 writer.carriage_return();
268 }
269 }
270 0x0d => {
271 writer.collect_end();
272 writer.carriage_return();
273 }
274 _ => return false,
275 }
276 self.flags &= !INPUT_LAST;
277 true
278 }
279
280 fn find_transition(&self) -> Transition {
281 let table = self.state.transition_table();
282 for entry in table {
283 if self.ch >= entry.first && self.ch <= entry.last {
284 return Transition {
285 handler: entry.handler,
286 next_state: entry.next_state,
287 };
288 }
289 }
290 Transition {
292 handler: states::Handler::None,
293 next_state: None,
294 }
295 }
296
297 fn execute_transition(&mut self, trans: Transition, writer: &mut dyn ScreenWriter) {
298 if !matches!(
300 trans.handler,
301 states::Handler::Print | states::Handler::TopBitSet
302 ) {
303 writer.collect_end();
304 }
305
306 let skip_state = match trans.handler {
308 states::Handler::None => false,
309 states::Handler::Print => self.handle_print(writer),
310 states::Handler::C0Dispatch => self.handle_c0_dispatch(writer),
311 states::Handler::EscDispatch => self.handle_esc_dispatch(writer),
312 states::Handler::CsiDispatch => self.handle_csi_dispatch(writer),
313 states::Handler::DcsDispatch => self.handle_dcs_dispatch(writer),
314 states::Handler::Intermediate => self.handle_intermediate(),
315 states::Handler::Parameter => self.handle_parameter(),
316 states::Handler::Input => self.handle_input(),
317 states::Handler::TopBitSet => self.handle_top_bit_set(writer),
318 states::Handler::EndBel => self.handle_end_bel(),
319 };
320
321 if skip_state {
322 return;
323 }
324
325 if let Some(next) = trans.next_state {
326 self.set_state(next, writer);
327 }
328
329 if self.state != InputState::Ground {
331 self.since_ground.push(self.ch);
332 }
333 }
334
335 fn set_state(&mut self, next: InputState, writer: &mut dyn ScreenWriter) {
336 self.exit_state(writer);
338 self.state = next;
339 self.enter_state(writer);
341 }
342
343 fn enter_state(&mut self, writer: &mut dyn ScreenWriter) {
344 match self.state {
345 InputState::Ground => self.enter_ground(),
346 InputState::EscEnter => self.clear(),
347 InputState::CsiEnter => self.clear(),
348 InputState::DcsEnter => self.enter_dcs(),
349 InputState::OscString => self.enter_osc(),
350 InputState::ApcString => self.enter_apc(),
351 InputState::RenameString => self.enter_rename(),
352 InputState::ConsumeSt => self.enter_rename(), _ => {}
354 }
355 let _ = writer; }
357
358 fn exit_state(&mut self, writer: &mut dyn ScreenWriter) {
359 match self.state {
360 InputState::OscString => self.exit_osc(writer),
361 InputState::ApcString => self.exit_apc(writer),
362 InputState::RenameString => self.exit_rename(writer),
363 _ => {}
364 }
365 }
366
367 fn clear(&mut self) {
368 self.ground_timer_active = false;
369 self.interm_buf = [0; INTERM_BUF_MAX];
370 self.interm_len = 0;
371 self.param_buf = [0; PARAM_BUF_MAX];
372 self.param_len = 0;
373 self.input_buf.clear();
374 self.input_end = InputEndType::St;
375 self.flags &= !INPUT_DISCARD;
376 }
377
378 fn enter_ground(&mut self) {
379 self.ground_timer_active = false;
380 self.since_ground.clear();
381 if self.input_buf.capacity() > INPUT_BUF_START {
383 self.input_buf = Vec::with_capacity(INPUT_BUF_START);
384 }
385 }
386
387 fn enter_dcs(&mut self) {
388 self.clear();
389 self.ground_timer_active = true;
390 self.flags &= !INPUT_LAST;
391 }
392
393 fn enter_osc(&mut self) {
394 self.clear();
395 self.ground_timer_active = true;
396 self.flags &= !INPUT_LAST;
397 }
398
399 fn enter_apc(&mut self) {
400 self.clear();
401 self.ground_timer_active = true;
402 self.flags &= !INPUT_LAST;
403 }
404
405 fn enter_rename(&mut self) {
406 self.clear();
407 self.ground_timer_active = true;
408 self.flags &= !INPUT_LAST;
409 }
410
411 fn exit_osc(&mut self, writer: &mut dyn ScreenWriter) {
412 if self.flags & INPUT_DISCARD != 0 {
413 return;
414 }
415 dispatch::dispatch_osc(self, writer);
416 }
417
418 fn exit_apc(&mut self, writer: &mut dyn ScreenWriter) {
419 if self.flags & INPUT_DISCARD != 0 {
420 return;
421 }
422 if passthrough::is_kitty_graphics_apc(&self.input_buf) {
423 writer.apc_passthrough(&self.input_buf);
424 return;
425 }
426 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
427 writer.set_title(&buf);
428 }
429
430 fn exit_rename(&mut self, writer: &mut dyn ScreenWriter) {
431 if self.flags & INPUT_DISCARD != 0 {
432 return;
433 }
434 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
435 writer.set_window_name(&buf);
436 }
437
438 fn stop_utf8(&mut self, writer: &mut dyn ScreenWriter) {
440 if self.utf8_started {
441 writer.collect_add('\u{FFFD}', &self.cell);
442 self.utf8_started = false;
443 self.utf8_len = 0;
444 self.utf8_expected = 0;
445 }
446 }
447
448 fn handle_print(&mut self, writer: &mut dyn ScreenWriter) -> bool {
449 self.stop_utf8(writer);
450
451 let ch = self.ch as char;
452 let set = if self.cell.set == 0 {
453 self.cell.g0set
454 } else {
455 self.cell.g1set
456 };
457
458 writer.collect_add_with_charset(ch, &self.cell, set != 0);
459
460 self.last_char = Some(ch);
461 self.flags |= INPUT_LAST;
462
463 false
464 }
465
466 fn handle_intermediate(&mut self) -> bool {
467 if self.interm_len >= INTERM_BUF_MAX - 1 {
468 self.flags |= INPUT_DISCARD;
469 } else {
470 self.interm_buf[self.interm_len] = self.ch;
471 self.interm_len += 1;
472 }
473 false
474 }
475
476 fn handle_parameter(&mut self) -> bool {
477 if self.param_len >= PARAM_BUF_MAX - 1 {
478 self.flags |= INPUT_DISCARD;
479 } else {
480 self.param_buf[self.param_len] = self.ch;
481 self.param_len += 1;
482 }
483 false
484 }
485
486 fn handle_input(&mut self) -> bool {
487 let escaped_dcs_byte = self.state == InputState::DcsEscape;
488 let bytes_to_push = if escaped_dcs_byte && self.ch != 0x1b {
489 2
490 } else {
491 1
492 };
493 let input_limit = self.input_buffer_limit();
494 if self.input_buf.len() + bytes_to_push >= input_limit {
495 if self.flags & INPUT_DISCARD == 0 && self.is_terminal_passthrough_string() {
496 self.terminal_passthrough_dropped_count =
497 self.terminal_passthrough_dropped_count.saturating_add(1);
498 }
499 self.flags |= INPUT_DISCARD;
500 } else if escaped_dcs_byte && self.ch == 0x1b {
501 self.input_buf.push(0x1b);
502 } else if escaped_dcs_byte {
503 self.input_buf.push(0x1b);
504 self.input_buf.push(self.ch);
505 } else {
506 self.input_buf.push(self.ch);
507 }
508 false
509 }
510
511 fn input_buffer_limit(&self) -> usize {
512 if self.is_terminal_passthrough_string() {
513 return MAX_TERMINAL_PASSTHROUGH_PAYLOAD_BYTES;
514 }
515 self.input_buf_max
516 }
517
518 fn is_terminal_passthrough_string(&self) -> bool {
519 (self.state == InputState::ApcString && passthrough::is_kitty_graphics_apc(&self.input_buf))
520 || (matches!(self.state, InputState::DcsHandler | InputState::DcsEscape)
521 && self.interm_len == 0
522 && (self.input_buf.first() == Some(&b'q') || self.input_buf.starts_with(b"tmux;")))
523 }
524
525 fn handle_end_bel(&mut self) -> bool {
526 self.input_end = InputEndType::Bel;
527 false
528 }
529
530 fn handle_c0_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
531 self.stop_utf8(writer);
532 dispatch::dispatch_c0(self, writer);
533 self.flags &= !INPUT_LAST;
534 false
535 }
536
537 fn handle_esc_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
538 if self.flags & INPUT_DISCARD != 0 {
539 return false;
540 }
541 dispatch::dispatch_esc(self, writer);
542 self.flags &= !INPUT_LAST;
543 false
544 }
545
546 fn handle_csi_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
547 if self.flags & INPUT_DISCARD != 0 {
548 return false;
549 }
550 dispatch::dispatch_csi(self, writer);
551 self.flags &= !INPUT_LAST;
552 false
553 }
554
555 fn handle_dcs_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
556 if self.flags & INPUT_DISCARD != 0 {
557 return false;
558 }
559 dispatch::dispatch_dcs(self, writer);
560 false
561 }
562
563 fn handle_top_bit_set(&mut self, writer: &mut dyn ScreenWriter) -> bool {
564 self.flags &= !INPUT_LAST;
565
566 if !self.utf8_started {
567 self.utf8_started = true;
568 self.utf8_len = 0;
569 let expected = if self.ch & 0xE0 == 0xC0 {
571 2
572 } else if self.ch & 0xF0 == 0xE0 {
573 3
574 } else if self.ch & 0xF8 == 0xF0 {
575 4
576 } else {
577 self.stop_utf8(writer);
579 return false;
580 };
581 self.utf8_expected = expected;
582 self.utf8_buf[0] = self.ch;
583 self.utf8_len = 1;
584 return false;
585 }
586
587 if self.ch & 0xC0 != 0x80 {
589 self.stop_utf8(writer);
591 if self.ch >= 0x80 {
593 return self.handle_top_bit_set(writer);
594 }
595 return false;
596 }
597
598 self.utf8_buf[self.utf8_len as usize] = self.ch;
599 self.utf8_len += 1;
600
601 if self.utf8_len < self.utf8_expected {
602 return false; }
604
605 self.utf8_started = false;
607 let bytes = &self.utf8_buf[..self.utf8_len as usize];
608 let s = match std::str::from_utf8(bytes) {
609 Ok(s) => s,
610 Err(_) => {
611 writer.collect_add('\u{FFFD}', &self.cell);
612 return false;
613 }
614 };
615 let c = match s.chars().next() {
616 Some(c) => c,
617 None => {
618 writer.collect_add('\u{FFFD}', &self.cell);
619 return false;
620 }
621 };
622
623 writer.collect_add(c, &self.cell);
624
625 self.last_char = Some(c);
626 self.flags |= INPUT_LAST;
627
628 false
629 }
630
631 fn reply(&mut self, s: &str) {
633 self.reply_buf.extend_from_slice(s.as_bytes());
634 }
635
636 fn interm_str(&self) -> &[u8] {
638 &self.interm_buf[..self.interm_len]
639 }
640}
641
642impl Default for InputParser {
643 fn default() -> Self {
644 Self::new()
645 }
646}