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 for &byte in buf {
222 self.ch = byte;
223 let transition = self.find_transition();
224 self.execute_transition(transition, writer);
225 }
226 }
227
228 fn find_transition(&self) -> Transition {
229 let table = self.state.transition_table();
230 for entry in table {
231 if self.ch >= entry.first && self.ch <= entry.last {
232 return Transition {
233 handler: entry.handler,
234 next_state: entry.next_state,
235 };
236 }
237 }
238 Transition {
240 handler: states::Handler::None,
241 next_state: None,
242 }
243 }
244
245 fn execute_transition(&mut self, trans: Transition, writer: &mut dyn ScreenWriter) {
246 if !matches!(
248 trans.handler,
249 states::Handler::Print | states::Handler::TopBitSet
250 ) {
251 writer.collect_end();
252 }
253
254 let skip_state = match trans.handler {
256 states::Handler::None => false,
257 states::Handler::Print => self.handle_print(writer),
258 states::Handler::C0Dispatch => self.handle_c0_dispatch(writer),
259 states::Handler::EscDispatch => self.handle_esc_dispatch(writer),
260 states::Handler::CsiDispatch => self.handle_csi_dispatch(writer),
261 states::Handler::DcsDispatch => self.handle_dcs_dispatch(writer),
262 states::Handler::Intermediate => self.handle_intermediate(),
263 states::Handler::Parameter => self.handle_parameter(),
264 states::Handler::Input => self.handle_input(),
265 states::Handler::TopBitSet => self.handle_top_bit_set(writer),
266 states::Handler::EndBel => self.handle_end_bel(),
267 };
268
269 if skip_state {
270 return;
271 }
272
273 if let Some(next) = trans.next_state {
274 self.set_state(next, writer);
275 }
276
277 if self.state != InputState::Ground {
279 self.since_ground.push(self.ch);
280 }
281 }
282
283 fn set_state(&mut self, next: InputState, writer: &mut dyn ScreenWriter) {
284 self.exit_state(writer);
286 self.state = next;
287 self.enter_state(writer);
289 }
290
291 fn enter_state(&mut self, writer: &mut dyn ScreenWriter) {
292 match self.state {
293 InputState::Ground => self.enter_ground(),
294 InputState::EscEnter => self.clear(),
295 InputState::CsiEnter => self.clear(),
296 InputState::DcsEnter => self.enter_dcs(),
297 InputState::OscString => self.enter_osc(),
298 InputState::ApcString => self.enter_apc(),
299 InputState::RenameString => self.enter_rename(),
300 InputState::ConsumeSt => self.enter_rename(), _ => {}
302 }
303 let _ = writer; }
305
306 fn exit_state(&mut self, writer: &mut dyn ScreenWriter) {
307 match self.state {
308 InputState::OscString => self.exit_osc(writer),
309 InputState::ApcString => self.exit_apc(writer),
310 InputState::RenameString => self.exit_rename(writer),
311 _ => {}
312 }
313 }
314
315 fn clear(&mut self) {
316 self.ground_timer_active = false;
317 self.interm_buf = [0; INTERM_BUF_MAX];
318 self.interm_len = 0;
319 self.param_buf = [0; PARAM_BUF_MAX];
320 self.param_len = 0;
321 self.input_buf.clear();
322 self.input_end = InputEndType::St;
323 self.flags &= !INPUT_DISCARD;
324 }
325
326 fn enter_ground(&mut self) {
327 self.ground_timer_active = false;
328 self.since_ground.clear();
329 if self.input_buf.capacity() > INPUT_BUF_START {
331 self.input_buf = Vec::with_capacity(INPUT_BUF_START);
332 }
333 }
334
335 fn enter_dcs(&mut self) {
336 self.clear();
337 self.ground_timer_active = true;
338 self.flags &= !INPUT_LAST;
339 }
340
341 fn enter_osc(&mut self) {
342 self.clear();
343 self.ground_timer_active = true;
344 self.flags &= !INPUT_LAST;
345 }
346
347 fn enter_apc(&mut self) {
348 self.clear();
349 self.ground_timer_active = true;
350 self.flags &= !INPUT_LAST;
351 }
352
353 fn enter_rename(&mut self) {
354 self.clear();
355 self.ground_timer_active = true;
356 self.flags &= !INPUT_LAST;
357 }
358
359 fn exit_osc(&mut self, writer: &mut dyn ScreenWriter) {
360 if self.flags & INPUT_DISCARD != 0 {
361 return;
362 }
363 dispatch::dispatch_osc(self, writer);
364 }
365
366 fn exit_apc(&mut self, writer: &mut dyn ScreenWriter) {
367 if self.flags & INPUT_DISCARD != 0 {
368 return;
369 }
370 if passthrough::is_kitty_graphics_apc(&self.input_buf) {
371 writer.apc_passthrough(&self.input_buf);
372 return;
373 }
374 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
375 writer.set_title(&buf);
376 }
377
378 fn exit_rename(&mut self, writer: &mut dyn ScreenWriter) {
379 if self.flags & INPUT_DISCARD != 0 {
380 return;
381 }
382 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
383 writer.set_window_name(&buf);
384 }
385
386 fn stop_utf8(&mut self, writer: &mut dyn ScreenWriter) {
388 if self.utf8_started {
389 writer.collect_add('\u{FFFD}', &self.cell);
390 self.utf8_started = false;
391 self.utf8_len = 0;
392 self.utf8_expected = 0;
393 }
394 }
395
396 fn handle_print(&mut self, writer: &mut dyn ScreenWriter) -> bool {
397 self.stop_utf8(writer);
398
399 let ch = self.ch as char;
400 let set = if self.cell.set == 0 {
401 self.cell.g0set
402 } else {
403 self.cell.g1set
404 };
405
406 writer.collect_add_with_charset(ch, &self.cell, set != 0);
407
408 self.last_char = Some(ch);
409 self.flags |= INPUT_LAST;
410
411 false
412 }
413
414 fn handle_intermediate(&mut self) -> bool {
415 if self.interm_len >= INTERM_BUF_MAX - 1 {
416 self.flags |= INPUT_DISCARD;
417 } else {
418 self.interm_buf[self.interm_len] = self.ch;
419 self.interm_len += 1;
420 }
421 false
422 }
423
424 fn handle_parameter(&mut self) -> bool {
425 if self.param_len >= PARAM_BUF_MAX - 1 {
426 self.flags |= INPUT_DISCARD;
427 } else {
428 self.param_buf[self.param_len] = self.ch;
429 self.param_len += 1;
430 }
431 false
432 }
433
434 fn handle_input(&mut self) -> bool {
435 let input_limit = self.input_buffer_limit();
436 if self.input_buf.len() + 1 >= input_limit {
437 if self.flags & INPUT_DISCARD == 0 && self.is_terminal_passthrough_string() {
438 self.terminal_passthrough_dropped_count =
439 self.terminal_passthrough_dropped_count.saturating_add(1);
440 }
441 self.flags |= INPUT_DISCARD;
442 } else {
443 self.input_buf.push(self.ch);
444 }
445 false
446 }
447
448 fn input_buffer_limit(&self) -> usize {
449 if self.is_terminal_passthrough_string() {
450 return MAX_TERMINAL_PASSTHROUGH_PAYLOAD_BYTES;
451 }
452 self.input_buf_max
453 }
454
455 fn is_terminal_passthrough_string(&self) -> bool {
456 (self.state == InputState::ApcString && passthrough::is_kitty_graphics_apc(&self.input_buf))
457 || (self.state == InputState::DcsHandler
458 && self.interm_len == 0
459 && self.input_buf.first() == Some(&b'q'))
460 }
461
462 fn handle_end_bel(&mut self) -> bool {
463 self.input_end = InputEndType::Bel;
464 false
465 }
466
467 fn handle_c0_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
468 self.stop_utf8(writer);
469 dispatch::dispatch_c0(self, writer);
470 self.flags &= !INPUT_LAST;
471 false
472 }
473
474 fn handle_esc_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
475 if self.flags & INPUT_DISCARD != 0 {
476 return false;
477 }
478 dispatch::dispatch_esc(self, writer);
479 self.flags &= !INPUT_LAST;
480 false
481 }
482
483 fn handle_csi_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
484 if self.flags & INPUT_DISCARD != 0 {
485 return false;
486 }
487 dispatch::dispatch_csi(self, writer);
488 self.flags &= !INPUT_LAST;
489 false
490 }
491
492 fn handle_dcs_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
493 if self.flags & INPUT_DISCARD != 0 {
494 return false;
495 }
496 dispatch::dispatch_dcs(self, writer);
497 false
498 }
499
500 fn handle_top_bit_set(&mut self, writer: &mut dyn ScreenWriter) -> bool {
501 self.flags &= !INPUT_LAST;
502
503 if !self.utf8_started {
504 self.utf8_started = true;
505 self.utf8_len = 0;
506 let expected = if self.ch & 0xE0 == 0xC0 {
508 2
509 } else if self.ch & 0xF0 == 0xE0 {
510 3
511 } else if self.ch & 0xF8 == 0xF0 {
512 4
513 } else {
514 self.stop_utf8(writer);
516 return false;
517 };
518 self.utf8_expected = expected;
519 self.utf8_buf[0] = self.ch;
520 self.utf8_len = 1;
521 return false;
522 }
523
524 if self.ch & 0xC0 != 0x80 {
526 self.stop_utf8(writer);
528 if self.ch >= 0x80 {
530 return self.handle_top_bit_set(writer);
531 }
532 return false;
533 }
534
535 self.utf8_buf[self.utf8_len as usize] = self.ch;
536 self.utf8_len += 1;
537
538 if self.utf8_len < self.utf8_expected {
539 return false; }
541
542 self.utf8_started = false;
544 let bytes = &self.utf8_buf[..self.utf8_len as usize];
545 let s = match std::str::from_utf8(bytes) {
546 Ok(s) => s,
547 Err(_) => {
548 writer.collect_add('\u{FFFD}', &self.cell);
549 return false;
550 }
551 };
552 let c = match s.chars().next() {
553 Some(c) => c,
554 None => {
555 writer.collect_add('\u{FFFD}', &self.cell);
556 return false;
557 }
558 };
559
560 writer.collect_add(c, &self.cell);
561
562 self.last_char = Some(c);
563 self.flags |= INPUT_LAST;
564
565 false
566 }
567
568 fn reply(&mut self, s: &str) {
570 self.reply_buf.extend_from_slice(s.as_bytes());
571 }
572
573 fn interm_str(&self) -> &[u8] {
575 &self.interm_buf[..self.interm_len]
576 }
577}
578
579impl Default for InputParser {
580 fn default() -> Self {
581 Self::new()
582 }
583}