1mod cell;
9mod colour;
10mod commands;
11mod csi_helpers;
12mod dispatch;
13mod params;
14mod passthrough;
15mod sgr;
16mod states;
17mod tables;
18#[cfg(test)]
19mod tests;
20mod writer;
21
22pub use cell::{CellState, GridAttr, SavedState};
23pub use colour::{
24 colour_join_rgb, Colour, COLOUR_DEFAULT, COLOUR_FLAG_256, COLOUR_FLAG_RGB, COLOUR_NONE,
25 COLOUR_TERMINAL,
26};
27pub use dispatch::{CsiCommand, DcsPayload, EscCommand, InputAction, OscCommand, ScreenWriter};
28pub use params::{InputParam, ParamType};
29pub use states::InputState;
30
31use params::ParamList;
32use states::Transition;
33
34use crate::terminal_passthrough::MAX_TERMINAL_PASSTHROUGH_PAYLOAD_BYTES;
35
36const PARAM_LIST_MAX: usize = 24;
38
39const INTERM_BUF_MAX: usize = 4;
41
42const INPUT_BUF_START: usize = 32;
44
45const INPUT_BUF_MAX: usize = 1_048_576;
47
48const PARAM_BUF_MAX: usize = 64;
50
51const INPUT_DISCARD: u32 = 0x1;
53const INPUT_LAST: u32 = 0x2;
55
56pub mod mode {
58 pub const MODE_CURSOR: u32 = 0x1;
60 pub const MODE_INSERT: u32 = 0x2;
62 pub const MODE_KCURSOR: u32 = 0x4;
64 pub const MODE_KKEYPAD: u32 = 0x8;
66 pub const MODE_WRAP: u32 = 0x10;
68 pub const MODE_MOUSE_STANDARD: u32 = 0x20;
70 pub const MODE_MOUSE_BUTTON: u32 = 0x40;
72 pub const MODE_CURSOR_BLINKING: u32 = 0x80;
74 pub const MODE_MOUSE_UTF8: u32 = 0x100;
76 pub const MODE_MOUSE_SGR: u32 = 0x200;
78 pub const MODE_BRACKETPASTE: u32 = 0x400;
80 pub const MODE_FOCUSON: u32 = 0x800;
82 pub const MODE_MOUSE_ALL: u32 = 0x1000;
84 pub const MODE_ORIGIN: u32 = 0x2000;
86 pub const MODE_CRLF: u32 = 0x4000;
88 pub const MODE_KEYS_EXTENDED: u32 = 0x8000;
90 pub const MODE_CURSOR_VERY_VISIBLE: u32 = 0x1_0000;
92 pub const MODE_CURSOR_BLINKING_SET: u32 = 0x2_0000;
94 pub const MODE_KEYS_EXTENDED_2: u32 = 0x4_0000;
96 pub const MODE_THEME_UPDATES: u32 = 0x8_0000;
98 pub const MODE_SYNC: u32 = 0x10_0000;
100
101 pub const ALL_MOUSE_MODES: u32 = MODE_MOUSE_STANDARD | MODE_MOUSE_BUTTON | MODE_MOUSE_ALL;
103 pub const EXTENDED_KEY_MODES: u32 = MODE_KEYS_EXTENDED | MODE_KEYS_EXTENDED_2;
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum InputEndType {
110 St,
112 Bel,
114}
115
116pub struct InputParser {
118 state: InputState,
120 flags: u32,
122
123 ch: u8,
125
126 interm_buf: [u8; INTERM_BUF_MAX],
128 interm_len: usize,
129
130 param_buf: [u8; PARAM_BUF_MAX],
132 param_len: usize,
133
134 input_buf: Vec<u8>,
136 input_end: InputEndType,
138
139 param_list: ParamList,
141
142 cell: CellState,
144 saved: SavedState,
146
147 utf8_buf: [u8; 4],
149 utf8_len: u8,
150 utf8_expected: u8,
151 utf8_started: bool,
152
153 last_char: Option<char>,
155
156 since_ground: Vec<u8>,
158
159 ground_timer_active: bool,
162
163 reply_buf: Vec<u8>,
165 terminal_passthrough_dropped_count: u64,
167}
168
169impl InputParser {
170 #[must_use]
172 pub fn new() -> Self {
173 Self {
174 state: InputState::Ground,
175 flags: 0,
176 ch: 0,
177 interm_buf: [0; INTERM_BUF_MAX],
178 interm_len: 0,
179 param_buf: [0; PARAM_BUF_MAX],
180 param_len: 0,
181 input_buf: Vec::with_capacity(INPUT_BUF_START),
182 input_end: InputEndType::St,
183 param_list: ParamList::new(),
184 cell: CellState::default(),
185 saved: SavedState::default(),
186 utf8_buf: [0; 4],
187 utf8_len: 0,
188 utf8_expected: 0,
189 utf8_started: false,
190 last_char: None,
191 since_ground: Vec::new(),
192 ground_timer_active: false,
193 reply_buf: Vec::new(),
194 terminal_passthrough_dropped_count: 0,
195 }
196 }
197
198 #[must_use]
200 pub fn state(&self) -> InputState {
201 self.state
202 }
203
204 pub fn take_replies(&mut self) -> Vec<u8> {
206 std::mem::take(&mut self.reply_buf)
207 }
208
209 pub(crate) fn take_terminal_passthrough_dropped_count(&mut self) -> u64 {
211 let dropped = self.terminal_passthrough_dropped_count;
212 self.terminal_passthrough_dropped_count = 0;
213 dropped
214 }
215
216 pub fn take_since_ground(&mut self) -> Vec<u8> {
218 std::mem::take(&mut self.since_ground)
219 }
220
221 #[must_use]
223 pub fn pending_bytes(&self) -> Vec<u8> {
224 if self.state != InputState::Ground {
225 return self.since_ground.clone();
226 }
227 if self.utf8_started {
228 return self.utf8_buf[..usize::from(self.utf8_len)].to_vec();
229 }
230 Vec::new()
231 }
232
233 #[must_use]
235 pub fn ground_timer_active(&self) -> bool {
236 self.ground_timer_active
237 }
238
239 pub fn ground_timer_expired(&mut self) {
241 self.reset_to_ground();
242 }
243
244 pub fn reset_to_ground(&mut self) {
246 self.clear();
247 self.state = InputState::Ground;
248 self.flags = 0;
249 self.enter_ground();
250 }
251
252 #[must_use]
254 pub fn cell_state(&self) -> &CellState {
255 &self.cell
256 }
257
258 pub fn parse(&mut self, buf: &[u8], writer: &mut dyn ScreenWriter) {
260 for &byte in buf {
261 self.ch = byte;
262 let transition = self.find_transition();
263 self.execute_transition(transition, writer);
264 }
265 }
266
267 fn find_transition(&self) -> Transition {
268 let table = self.state.transition_table();
269 for entry in table {
270 if self.ch >= entry.first && self.ch <= entry.last {
271 return Transition {
272 handler: entry.handler,
273 next_state: entry.next_state,
274 };
275 }
276 }
277 Transition {
279 handler: states::Handler::None,
280 next_state: None,
281 }
282 }
283
284 fn execute_transition(&mut self, trans: Transition, writer: &mut dyn ScreenWriter) {
285 if !matches!(
287 trans.handler,
288 states::Handler::Print | states::Handler::TopBitSet
289 ) {
290 writer.collect_end();
291 }
292
293 let skip_state = match trans.handler {
295 states::Handler::None => false,
296 states::Handler::Print => self.handle_print(writer),
297 states::Handler::C0Dispatch => self.handle_c0_dispatch(writer),
298 states::Handler::EscDispatch => self.handle_esc_dispatch(writer),
299 states::Handler::CsiDispatch => self.handle_csi_dispatch(writer),
300 states::Handler::DcsDispatch => self.handle_dcs_dispatch(writer),
301 states::Handler::Intermediate => self.handle_intermediate(),
302 states::Handler::Parameter => self.handle_parameter(),
303 states::Handler::Input => self.handle_input(),
304 states::Handler::TopBitSet => self.handle_top_bit_set(writer),
305 states::Handler::EndBel => self.handle_end_bel(),
306 };
307
308 if skip_state {
309 return;
310 }
311
312 if let Some(next) = trans.next_state {
313 self.set_state(next, writer);
314 }
315
316 if self.state != InputState::Ground {
318 self.since_ground.push(self.ch);
319 }
320 }
321
322 fn set_state(&mut self, next: InputState, writer: &mut dyn ScreenWriter) {
323 self.exit_state(writer);
325 self.state = next;
326 self.enter_state(writer);
328 }
329
330 fn enter_state(&mut self, writer: &mut dyn ScreenWriter) {
331 match self.state {
332 InputState::Ground => self.enter_ground(),
333 InputState::EscEnter => self.clear(),
334 InputState::CsiEnter => self.clear(),
335 InputState::DcsEnter => self.enter_dcs(),
336 InputState::OscString => self.enter_osc(),
337 InputState::ApcString => self.enter_apc(),
338 InputState::RenameString => self.enter_rename(),
339 InputState::ConsumeSt => self.enter_rename(), _ => {}
341 }
342 let _ = writer; }
344
345 fn exit_state(&mut self, writer: &mut dyn ScreenWriter) {
346 match self.state {
347 InputState::OscString => self.exit_osc(writer),
348 InputState::ApcString => self.exit_apc(writer),
349 InputState::RenameString => self.exit_rename(writer),
350 _ => {}
351 }
352 }
353
354 fn clear(&mut self) {
355 self.ground_timer_active = false;
356 self.interm_buf = [0; INTERM_BUF_MAX];
357 self.interm_len = 0;
358 self.param_buf = [0; PARAM_BUF_MAX];
359 self.param_len = 0;
360 self.input_buf.clear();
361 self.input_end = InputEndType::St;
362 self.flags &= !INPUT_DISCARD;
363 }
364
365 fn enter_ground(&mut self) {
366 self.ground_timer_active = false;
367 self.since_ground.clear();
368 if self.input_buf.capacity() > INPUT_BUF_START {
370 self.input_buf = Vec::with_capacity(INPUT_BUF_START);
371 }
372 }
373
374 fn enter_dcs(&mut self) {
375 self.clear();
376 self.ground_timer_active = true;
377 self.flags &= !INPUT_LAST;
378 }
379
380 fn enter_osc(&mut self) {
381 self.clear();
382 self.ground_timer_active = true;
383 self.flags &= !INPUT_LAST;
384 }
385
386 fn enter_apc(&mut self) {
387 self.clear();
388 self.ground_timer_active = true;
389 self.flags &= !INPUT_LAST;
390 }
391
392 fn enter_rename(&mut self) {
393 self.clear();
394 self.ground_timer_active = true;
395 self.flags &= !INPUT_LAST;
396 }
397
398 fn exit_osc(&mut self, writer: &mut dyn ScreenWriter) {
399 if self.flags & INPUT_DISCARD != 0 {
400 return;
401 }
402 dispatch::dispatch_osc(self, writer);
403 }
404
405 fn exit_apc(&mut self, writer: &mut dyn ScreenWriter) {
406 if self.flags & INPUT_DISCARD != 0 {
407 return;
408 }
409 if passthrough::is_kitty_graphics_apc(&self.input_buf) {
410 writer.apc_passthrough(&self.input_buf);
411 return;
412 }
413 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
414 writer.set_title(&buf);
415 }
416
417 fn exit_rename(&mut self, writer: &mut dyn ScreenWriter) {
418 if self.flags & INPUT_DISCARD != 0 {
419 return;
420 }
421 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
422 writer.set_window_name(&buf);
423 }
424
425 fn stop_utf8(&mut self, writer: &mut dyn ScreenWriter) {
427 if self.utf8_started {
428 writer.collect_add('\u{FFFD}', &self.cell);
429 self.utf8_started = false;
430 self.utf8_len = 0;
431 self.utf8_expected = 0;
432 }
433 }
434
435 fn handle_print(&mut self, writer: &mut dyn ScreenWriter) -> bool {
436 self.stop_utf8(writer);
437
438 let ch = self.ch as char;
439 let set = if self.cell.set == 0 {
440 self.cell.g0set
441 } else {
442 self.cell.g1set
443 };
444
445 writer.collect_add_with_charset(ch, &self.cell, set != 0);
446
447 self.last_char = Some(ch);
448 self.flags |= INPUT_LAST;
449
450 false
451 }
452
453 fn handle_intermediate(&mut self) -> bool {
454 if self.interm_len >= INTERM_BUF_MAX - 1 {
455 self.flags |= INPUT_DISCARD;
456 } else {
457 self.interm_buf[self.interm_len] = self.ch;
458 self.interm_len += 1;
459 }
460 false
461 }
462
463 fn handle_parameter(&mut self) -> bool {
464 if self.param_len >= PARAM_BUF_MAX - 1 {
465 self.flags |= INPUT_DISCARD;
466 } else {
467 self.param_buf[self.param_len] = self.ch;
468 self.param_len += 1;
469 }
470 false
471 }
472
473 fn handle_input(&mut self) -> bool {
474 let input_limit = self.input_buffer_limit();
475 if self.input_buf.len() + 1 >= input_limit {
476 if self.flags & INPUT_DISCARD == 0 && self.is_terminal_passthrough_string() {
477 self.terminal_passthrough_dropped_count =
478 self.terminal_passthrough_dropped_count.saturating_add(1);
479 }
480 self.flags |= INPUT_DISCARD;
481 } else {
482 self.input_buf.push(self.ch);
483 }
484 false
485 }
486
487 fn input_buffer_limit(&self) -> usize {
488 if self.is_terminal_passthrough_string() {
489 return MAX_TERMINAL_PASSTHROUGH_PAYLOAD_BYTES;
490 }
491 INPUT_BUF_MAX
492 }
493
494 fn is_terminal_passthrough_string(&self) -> bool {
495 (self.state == InputState::ApcString && passthrough::is_kitty_graphics_apc(&self.input_buf))
496 || (self.state == InputState::DcsHandler
497 && self.interm_len == 0
498 && self.input_buf.first() == Some(&b'q'))
499 }
500
501 fn handle_end_bel(&mut self) -> bool {
502 self.input_end = InputEndType::Bel;
503 false
504 }
505
506 fn handle_c0_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
507 self.stop_utf8(writer);
508 dispatch::dispatch_c0(self, writer);
509 self.flags &= !INPUT_LAST;
510 false
511 }
512
513 fn handle_esc_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
514 if self.flags & INPUT_DISCARD != 0 {
515 return false;
516 }
517 dispatch::dispatch_esc(self, writer);
518 self.flags &= !INPUT_LAST;
519 false
520 }
521
522 fn handle_csi_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
523 if self.flags & INPUT_DISCARD != 0 {
524 return false;
525 }
526 dispatch::dispatch_csi(self, writer);
527 self.flags &= !INPUT_LAST;
528 false
529 }
530
531 fn handle_dcs_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
532 if self.flags & INPUT_DISCARD != 0 {
533 return false;
534 }
535 dispatch::dispatch_dcs(self, writer);
536 false
537 }
538
539 fn handle_top_bit_set(&mut self, writer: &mut dyn ScreenWriter) -> bool {
540 self.flags &= !INPUT_LAST;
541
542 if !self.utf8_started {
543 self.utf8_started = true;
544 self.utf8_len = 0;
545 let expected = if self.ch & 0xE0 == 0xC0 {
547 2
548 } else if self.ch & 0xF0 == 0xE0 {
549 3
550 } else if self.ch & 0xF8 == 0xF0 {
551 4
552 } else {
553 self.stop_utf8(writer);
555 return false;
556 };
557 self.utf8_expected = expected;
558 self.utf8_buf[0] = self.ch;
559 self.utf8_len = 1;
560 return false;
561 }
562
563 if self.ch & 0xC0 != 0x80 {
565 self.stop_utf8(writer);
567 if self.ch >= 0x80 {
569 return self.handle_top_bit_set(writer);
570 }
571 return false;
572 }
573
574 self.utf8_buf[self.utf8_len as usize] = self.ch;
575 self.utf8_len += 1;
576
577 if self.utf8_len < self.utf8_expected {
578 return false; }
580
581 self.utf8_started = false;
583 let bytes = &self.utf8_buf[..self.utf8_len as usize];
584 let s = match std::str::from_utf8(bytes) {
585 Ok(s) => s,
586 Err(_) => {
587 writer.collect_add('\u{FFFD}', &self.cell);
588 return false;
589 }
590 };
591 let c = match s.chars().next() {
592 Some(c) => c,
593 None => {
594 writer.collect_add('\u{FFFD}', &self.cell);
595 return false;
596 }
597 };
598
599 writer.collect_add(c, &self.cell);
600
601 self.last_char = Some(c);
602 self.flags |= INPUT_LAST;
603
604 false
605 }
606
607 fn reply(&mut self, s: &str) {
609 self.reply_buf.extend_from_slice(s.as_bytes());
610 }
611
612 fn interm_str(&self) -> &[u8] {
614 &self.interm_buf[..self.interm_len]
615 }
616}
617
618impl Default for InputParser {
619 fn default() -> Self {
620 Self::new()
621 }
622}