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
34const PARAM_LIST_MAX: usize = 24;
36
37const INTERM_BUF_MAX: usize = 4;
39
40const INPUT_BUF_START: usize = 32;
42
43const INPUT_BUF_MAX: usize = 1_048_576;
45
46const PARAM_BUF_MAX: usize = 64;
48
49const INPUT_DISCARD: u32 = 0x1;
51const INPUT_LAST: u32 = 0x2;
53
54pub mod mode {
56 pub const MODE_CURSOR: u32 = 0x1;
58 pub const MODE_INSERT: u32 = 0x2;
60 pub const MODE_KCURSOR: u32 = 0x4;
62 pub const MODE_KKEYPAD: u32 = 0x8;
64 pub const MODE_WRAP: u32 = 0x10;
66 pub const MODE_MOUSE_STANDARD: u32 = 0x20;
68 pub const MODE_MOUSE_BUTTON: u32 = 0x40;
70 pub const MODE_CURSOR_BLINKING: u32 = 0x80;
72 pub const MODE_MOUSE_UTF8: u32 = 0x100;
74 pub const MODE_MOUSE_SGR: u32 = 0x200;
76 pub const MODE_BRACKETPASTE: u32 = 0x400;
78 pub const MODE_FOCUSON: u32 = 0x800;
80 pub const MODE_MOUSE_ALL: u32 = 0x1000;
82 pub const MODE_ORIGIN: u32 = 0x2000;
84 pub const MODE_CRLF: u32 = 0x4000;
86 pub const MODE_KEYS_EXTENDED: u32 = 0x8000;
88 pub const MODE_CURSOR_VERY_VISIBLE: u32 = 0x1_0000;
90 pub const MODE_CURSOR_BLINKING_SET: u32 = 0x2_0000;
92 pub const MODE_KEYS_EXTENDED_2: u32 = 0x4_0000;
94 pub const MODE_THEME_UPDATES: u32 = 0x8_0000;
96 pub const MODE_SYNC: u32 = 0x10_0000;
98
99 pub const ALL_MOUSE_MODES: u32 = MODE_MOUSE_STANDARD | MODE_MOUSE_BUTTON | MODE_MOUSE_ALL;
101 pub const EXTENDED_KEY_MODES: u32 = MODE_KEYS_EXTENDED | MODE_KEYS_EXTENDED_2;
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum InputEndType {
108 St,
110 Bel,
112}
113
114pub struct InputParser {
116 state: InputState,
118 flags: u32,
120
121 ch: u8,
123
124 interm_buf: [u8; INTERM_BUF_MAX],
126 interm_len: usize,
127
128 param_buf: [u8; PARAM_BUF_MAX],
130 param_len: usize,
131
132 input_buf: Vec<u8>,
134 input_end: InputEndType,
136
137 param_list: ParamList,
139
140 cell: CellState,
142 saved: SavedState,
144
145 utf8_buf: [u8; 4],
147 utf8_len: u8,
148 utf8_expected: u8,
149 utf8_started: bool,
150
151 last_char: Option<char>,
153
154 since_ground: Vec<u8>,
156
157 ground_timer_active: bool,
160
161 reply_buf: Vec<u8>,
163 terminal_passthrough_dropped_count: u64,
165}
166
167impl InputParser {
168 #[must_use]
170 pub fn new() -> Self {
171 Self {
172 state: InputState::Ground,
173 flags: 0,
174 ch: 0,
175 interm_buf: [0; INTERM_BUF_MAX],
176 interm_len: 0,
177 param_buf: [0; PARAM_BUF_MAX],
178 param_len: 0,
179 input_buf: Vec::with_capacity(INPUT_BUF_START),
180 input_end: InputEndType::St,
181 param_list: ParamList::new(),
182 cell: CellState::default(),
183 saved: SavedState::default(),
184 utf8_buf: [0; 4],
185 utf8_len: 0,
186 utf8_expected: 0,
187 utf8_started: false,
188 last_char: None,
189 since_ground: Vec::new(),
190 ground_timer_active: false,
191 reply_buf: Vec::new(),
192 terminal_passthrough_dropped_count: 0,
193 }
194 }
195
196 #[must_use]
198 pub fn state(&self) -> InputState {
199 self.state
200 }
201
202 pub fn take_replies(&mut self) -> Vec<u8> {
204 std::mem::take(&mut self.reply_buf)
205 }
206
207 pub(crate) fn take_terminal_passthrough_dropped_count(&mut self) -> u64 {
209 let dropped = self.terminal_passthrough_dropped_count;
210 self.terminal_passthrough_dropped_count = 0;
211 dropped
212 }
213
214 pub fn take_since_ground(&mut self) -> Vec<u8> {
216 std::mem::take(&mut self.since_ground)
217 }
218
219 #[must_use]
221 pub fn pending_bytes(&self) -> Vec<u8> {
222 if self.state != InputState::Ground {
223 return self.since_ground.clone();
224 }
225 if self.utf8_started {
226 return self.utf8_buf[..usize::from(self.utf8_len)].to_vec();
227 }
228 Vec::new()
229 }
230
231 #[must_use]
233 pub fn ground_timer_active(&self) -> bool {
234 self.ground_timer_active
235 }
236
237 pub fn ground_timer_expired(&mut self) {
239 self.reset_to_ground();
240 }
241
242 pub fn reset_to_ground(&mut self) {
244 self.clear();
245 self.state = InputState::Ground;
246 self.flags = 0;
247 self.enter_ground();
248 }
249
250 #[must_use]
252 pub fn cell_state(&self) -> &CellState {
253 &self.cell
254 }
255
256 pub fn parse(&mut self, buf: &[u8], writer: &mut dyn ScreenWriter) {
258 for &byte in buf {
259 self.ch = byte;
260 let transition = self.find_transition();
261 self.execute_transition(transition, writer);
262 }
263 }
264
265 fn find_transition(&self) -> Transition {
266 let table = self.state.transition_table();
267 for entry in table {
268 if self.ch >= entry.first && self.ch <= entry.last {
269 return Transition {
270 handler: entry.handler,
271 next_state: entry.next_state,
272 };
273 }
274 }
275 Transition {
277 handler: states::Handler::None,
278 next_state: None,
279 }
280 }
281
282 fn execute_transition(&mut self, trans: Transition, writer: &mut dyn ScreenWriter) {
283 if !matches!(
285 trans.handler,
286 states::Handler::Print | states::Handler::TopBitSet
287 ) {
288 writer.collect_end();
289 }
290
291 let skip_state = match trans.handler {
293 states::Handler::None => false,
294 states::Handler::Print => self.handle_print(writer),
295 states::Handler::C0Dispatch => self.handle_c0_dispatch(writer),
296 states::Handler::EscDispatch => self.handle_esc_dispatch(writer),
297 states::Handler::CsiDispatch => self.handle_csi_dispatch(writer),
298 states::Handler::DcsDispatch => self.handle_dcs_dispatch(writer),
299 states::Handler::Intermediate => self.handle_intermediate(),
300 states::Handler::Parameter => self.handle_parameter(),
301 states::Handler::Input => self.handle_input(),
302 states::Handler::TopBitSet => self.handle_top_bit_set(writer),
303 states::Handler::EndBel => self.handle_end_bel(),
304 };
305
306 if skip_state {
307 return;
308 }
309
310 if let Some(next) = trans.next_state {
311 self.set_state(next, writer);
312 }
313
314 if self.state != InputState::Ground {
316 self.since_ground.push(self.ch);
317 }
318 }
319
320 fn set_state(&mut self, next: InputState, writer: &mut dyn ScreenWriter) {
321 self.exit_state(writer);
323 self.state = next;
324 self.enter_state(writer);
326 }
327
328 fn enter_state(&mut self, writer: &mut dyn ScreenWriter) {
329 match self.state {
330 InputState::Ground => self.enter_ground(),
331 InputState::EscEnter => self.clear(),
332 InputState::CsiEnter => self.clear(),
333 InputState::DcsEnter => self.enter_dcs(),
334 InputState::OscString => self.enter_osc(),
335 InputState::ApcString => self.enter_apc(),
336 InputState::RenameString => self.enter_rename(),
337 InputState::ConsumeSt => self.enter_rename(), _ => {}
339 }
340 let _ = writer; }
342
343 fn exit_state(&mut self, writer: &mut dyn ScreenWriter) {
344 match self.state {
345 InputState::OscString => self.exit_osc(writer),
346 InputState::ApcString => self.exit_apc(writer),
347 InputState::RenameString => self.exit_rename(writer),
348 _ => {}
349 }
350 }
351
352 fn clear(&mut self) {
353 self.ground_timer_active = false;
354 self.interm_buf = [0; INTERM_BUF_MAX];
355 self.interm_len = 0;
356 self.param_buf = [0; PARAM_BUF_MAX];
357 self.param_len = 0;
358 self.input_buf.clear();
359 self.input_end = InputEndType::St;
360 self.flags &= !INPUT_DISCARD;
361 }
362
363 fn enter_ground(&mut self) {
364 self.ground_timer_active = false;
365 self.since_ground.clear();
366 if self.input_buf.capacity() > INPUT_BUF_START {
368 self.input_buf = Vec::with_capacity(INPUT_BUF_START);
369 }
370 }
371
372 fn enter_dcs(&mut self) {
373 self.clear();
374 self.ground_timer_active = true;
375 self.flags &= !INPUT_LAST;
376 }
377
378 fn enter_osc(&mut self) {
379 self.clear();
380 self.ground_timer_active = true;
381 self.flags &= !INPUT_LAST;
382 }
383
384 fn enter_apc(&mut self) {
385 self.clear();
386 self.ground_timer_active = true;
387 self.flags &= !INPUT_LAST;
388 }
389
390 fn enter_rename(&mut self) {
391 self.clear();
392 self.ground_timer_active = true;
393 self.flags &= !INPUT_LAST;
394 }
395
396 fn exit_osc(&mut self, writer: &mut dyn ScreenWriter) {
397 if self.flags & INPUT_DISCARD != 0 {
398 return;
399 }
400 dispatch::dispatch_osc(self, writer);
401 }
402
403 fn exit_apc(&mut self, writer: &mut dyn ScreenWriter) {
404 if self.flags & INPUT_DISCARD != 0 {
405 return;
406 }
407 if passthrough::is_kitty_graphics_apc(&self.input_buf) {
408 writer.apc_passthrough(&self.input_buf);
409 return;
410 }
411 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
412 writer.set_title(&buf);
413 }
414
415 fn exit_rename(&mut self, writer: &mut dyn ScreenWriter) {
416 if self.flags & INPUT_DISCARD != 0 {
417 return;
418 }
419 let buf = String::from_utf8_lossy(&self.input_buf).into_owned();
420 writer.set_window_name(&buf);
421 }
422
423 fn stop_utf8(&mut self, writer: &mut dyn ScreenWriter) {
425 if self.utf8_started {
426 writer.collect_add('\u{FFFD}', &self.cell);
427 self.utf8_started = false;
428 self.utf8_len = 0;
429 self.utf8_expected = 0;
430 }
431 }
432
433 fn handle_print(&mut self, writer: &mut dyn ScreenWriter) -> bool {
434 self.stop_utf8(writer);
435
436 let ch = self.ch as char;
437 let set = if self.cell.set == 0 {
438 self.cell.g0set
439 } else {
440 self.cell.g1set
441 };
442
443 writer.collect_add_with_charset(ch, &self.cell, set != 0);
444
445 self.last_char = Some(ch);
446 self.flags |= INPUT_LAST;
447
448 false
449 }
450
451 fn handle_intermediate(&mut self) -> bool {
452 if self.interm_len >= INTERM_BUF_MAX - 1 {
453 self.flags |= INPUT_DISCARD;
454 } else {
455 self.interm_buf[self.interm_len] = self.ch;
456 self.interm_len += 1;
457 }
458 false
459 }
460
461 fn handle_parameter(&mut self) -> bool {
462 if self.param_len >= PARAM_BUF_MAX - 1 {
463 self.flags |= INPUT_DISCARD;
464 } else {
465 self.param_buf[self.param_len] = self.ch;
466 self.param_len += 1;
467 }
468 false
469 }
470
471 fn handle_input(&mut self) -> bool {
472 if self.input_buf.len() + 1 >= INPUT_BUF_MAX {
473 if self.flags & INPUT_DISCARD == 0
474 && self.state == InputState::ApcString
475 && passthrough::is_kitty_graphics_apc(&self.input_buf)
476 {
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 handle_end_bel(&mut self) -> bool {
488 self.input_end = InputEndType::Bel;
489 false
490 }
491
492 fn handle_c0_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
493 self.stop_utf8(writer);
494 dispatch::dispatch_c0(self, writer);
495 self.flags &= !INPUT_LAST;
496 false
497 }
498
499 fn handle_esc_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
500 if self.flags & INPUT_DISCARD != 0 {
501 return false;
502 }
503 dispatch::dispatch_esc(self, writer);
504 self.flags &= !INPUT_LAST;
505 false
506 }
507
508 fn handle_csi_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
509 if self.flags & INPUT_DISCARD != 0 {
510 return false;
511 }
512 dispatch::dispatch_csi(self, writer);
513 self.flags &= !INPUT_LAST;
514 false
515 }
516
517 fn handle_dcs_dispatch(&mut self, writer: &mut dyn ScreenWriter) -> bool {
518 if self.flags & INPUT_DISCARD != 0 {
519 return false;
520 }
521 dispatch::dispatch_dcs(self, writer);
522 false
523 }
524
525 fn handle_top_bit_set(&mut self, writer: &mut dyn ScreenWriter) -> bool {
526 self.flags &= !INPUT_LAST;
527
528 if !self.utf8_started {
529 self.utf8_started = true;
530 self.utf8_len = 0;
531 let expected = if self.ch & 0xE0 == 0xC0 {
533 2
534 } else if self.ch & 0xF0 == 0xE0 {
535 3
536 } else if self.ch & 0xF8 == 0xF0 {
537 4
538 } else {
539 self.stop_utf8(writer);
541 return false;
542 };
543 self.utf8_expected = expected;
544 self.utf8_buf[0] = self.ch;
545 self.utf8_len = 1;
546 return false;
547 }
548
549 if self.ch & 0xC0 != 0x80 {
551 self.stop_utf8(writer);
553 if self.ch >= 0x80 {
555 return self.handle_top_bit_set(writer);
556 }
557 return false;
558 }
559
560 self.utf8_buf[self.utf8_len as usize] = self.ch;
561 self.utf8_len += 1;
562
563 if self.utf8_len < self.utf8_expected {
564 return false; }
566
567 self.utf8_started = false;
569 let bytes = &self.utf8_buf[..self.utf8_len as usize];
570 let s = match std::str::from_utf8(bytes) {
571 Ok(s) => s,
572 Err(_) => {
573 writer.collect_add('\u{FFFD}', &self.cell);
574 return false;
575 }
576 };
577 let c = match s.chars().next() {
578 Some(c) => c,
579 None => {
580 writer.collect_add('\u{FFFD}', &self.cell);
581 return false;
582 }
583 };
584
585 writer.collect_add(c, &self.cell);
586
587 self.last_char = Some(c);
588 self.flags |= INPUT_LAST;
589
590 false
591 }
592
593 fn reply(&mut self, s: &str) {
595 self.reply_buf.extend_from_slice(s.as_bytes());
596 }
597
598 fn interm_str(&self) -> &[u8] {
600 &self.interm_buf[..self.interm_len]
601 }
602}
603
604impl Default for InputParser {
605 fn default() -> Self {
606 Self::new()
607 }
608}