1use std::{
5 io::{self, Write as _},
6 time::Duration,
7};
8
9use termina::{
10 escape::csi::{self, KittyKeyboardFlags},
11 event::{KeyCode, KeyEvent},
12 Event, PlatformTerminal, Terminal, WindowSize,
13};
14
15const HELP: &str = r#"Blocking read()
16 - Keyboard, mouse, focus and terminal resize events enabled
17 - Hit "c" to print current cursor position
18 - Use Esc to quit
19"#;
20
21macro_rules! decset {
22 ($mode:ident) => {
23 csi::Csi::Mode(csi::Mode::SetDecPrivateMode(csi::DecPrivateMode::Code(
24 csi::DecPrivateModeCode::$mode,
25 )))
26 };
27}
28macro_rules! decreset {
29 ($mode:ident) => {
30 csi::Csi::Mode(csi::Mode::ResetDecPrivateMode(csi::DecPrivateMode::Code(
31 csi::DecPrivateModeCode::$mode,
32 )))
33 };
34}
35
36fn main() -> io::Result<()> {
37 println!("{HELP}");
38
39 let mut terminal = PlatformTerminal::new()?;
40 terminal.enter_raw_mode()?;
41
42 write!(
43 terminal,
44 "{}{}{}{}{}{}{}{}",
45 csi::Csi::Keyboard(csi::Keyboard::PushFlags(
46 KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
47 | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
48 )),
49 decset!(FocusTracking),
50 decset!(BracketedPaste),
51 decset!(MouseTracking),
52 decset!(ButtonEventMouse),
53 decset!(AnyEventMouse),
54 decset!(RXVTMouse),
55 decset!(SGRMouse),
56 )?;
57 terminal.flush()?;
58
59 let mut size = terminal.get_dimensions()?;
60 loop {
61 let event = terminal.read(|event| !event.is_escape())?;
62
63 println!("Event: {event:?}\r");
64
65 match event {
66 Event::Key(KeyEvent {
67 code: KeyCode::Escape,
68 ..
69 }) => break,
70 Event::Key(KeyEvent {
71 code: KeyCode::Char('c'),
72 ..
73 }) => {
74 write!(
75 terminal,
76 "{}",
77 csi::Csi::Cursor(csi::Cursor::RequestActivePositionReport),
78 )?;
79 terminal.flush()?;
80 let filter = |event: &Event| {
81 matches!(
82 event,
83 Event::Csi(csi::Csi::Cursor(csi::Cursor::ActivePositionReport { .. }))
84 )
85 };
86 if terminal.poll(filter, Some(Duration::from_millis(50)))? {
87 let Event::Csi(csi::Csi::Cursor(csi::Cursor::ActivePositionReport {
88 line,
89 col,
90 })) = terminal.read(filter)?
91 else {
92 unreachable!()
93 };
94 println!(
95 "Cursor position: {:?}\r",
96 (line.get_zero_based(), col.get_zero_based())
97 );
98 } else {
99 eprintln!("Failed to read the cursor position within 50msec\r");
100 }
101 }
102 Event::WindowResized(dimensions) => {
103 let new_size = flush_resize_events(&terminal, dimensions);
104 println!("Resize from {size:?} to {new_size:?}\r");
105 size = new_size;
106 }
107 _ => (),
108 }
109 }
110
111 write!(
112 terminal,
113 "{}{}{}{}{}{}{}{}",
114 csi::Csi::Keyboard(csi::Keyboard::PopFlags(1)),
115 decreset!(FocusTracking),
116 decreset!(BracketedPaste),
117 decreset!(MouseTracking),
118 decreset!(ButtonEventMouse),
119 decreset!(AnyEventMouse),
120 decreset!(RXVTMouse),
121 decreset!(SGRMouse),
122 )?;
123
124 Ok(())
125}
126
127fn flush_resize_events(terminal: &PlatformTerminal, original_size: WindowSize) -> WindowSize {
128 let mut size = original_size;
129 let filter = |event: &Event| matches!(event, Event::WindowResized { .. });
130 while let Ok(true) = terminal.poll(filter, Some(Duration::from_millis(50))) {
131 if let Ok(Event::WindowResized(dimensions)) = terminal.read(filter) {
132 size = dimensions;
133 }
134 }
135 size
136}