@@ -13,7 +13,9 @@ use std::time::Duration;
1313use anyhow:: Result ;
1414use app:: { App , AppAction , StatusKind , StatusLine } ;
1515use config:: ConfigStore ;
16- use crossterm:: event;
16+ use crossterm:: event:: {
17+ self , KeyboardEnhancementFlags , PopKeyboardEnhancementFlags , PushKeyboardEnhancementFlags ,
18+ } ;
1719use crossterm:: execute;
1820use crossterm:: terminal:: {
1921 disable_raw_mode, enable_raw_mode, EnterAlternateScreen , LeaveAlternateScreen ,
@@ -29,16 +31,21 @@ fn main() {
2931}
3032
3133fn start ( ) -> Result < ( ) > {
32- let mut terminal = setup_terminal ( ) ?;
33- let res = run_loop ( & mut terminal) ;
34- restore_terminal ( & mut terminal ) ?;
34+ let mut guard = TerminalGuard :: new ( ) ?;
35+ let res = run_loop ( guard . terminal ( ) ) ;
36+ guard . restore ( ) ?;
3537 res
3638}
3739
3840fn setup_terminal ( ) -> Result < Terminal < CrosstermBackend < io:: Stdout > > > {
3941 enable_raw_mode ( ) ?;
4042 let mut stdout = io:: stdout ( ) ;
41- execute ! ( stdout, EnterAlternateScreen ) ?;
43+ execute ! (
44+ stdout,
45+ EnterAlternateScreen ,
46+ // Keep kitty keyboard protocol scoped to the TUI session.
47+ PushKeyboardEnhancementFlags ( KeyboardEnhancementFlags :: DISAMBIGUATE_ESCAPE_CODES )
48+ ) ?;
4249 let backend = CrosstermBackend :: new ( stdout) ;
4350 let mut terminal = Terminal :: new ( backend) ?;
4451 terminal. clear ( ) ?;
@@ -47,11 +54,48 @@ fn setup_terminal() -> Result<Terminal<CrosstermBackend<io::Stdout>>> {
4754
4855fn restore_terminal ( terminal : & mut Terminal < CrosstermBackend < io:: Stdout > > ) -> Result < ( ) > {
4956 disable_raw_mode ( ) ?;
50- execute ! ( terminal. backend_mut( ) , LeaveAlternateScreen ) ?;
57+ execute ! (
58+ terminal. backend_mut( ) ,
59+ // Pop before leaving the alternate screen to avoid leaking CSI u sequences.
60+ PopKeyboardEnhancementFlags ,
61+ LeaveAlternateScreen
62+ ) ?;
5163 terminal. show_cursor ( ) ?;
5264 Ok ( ( ) )
5365}
5466
67+ struct TerminalGuard {
68+ terminal : Terminal < CrosstermBackend < io:: Stdout > > ,
69+ restored : bool ,
70+ }
71+
72+ impl TerminalGuard {
73+ fn new ( ) -> Result < Self > {
74+ Ok ( Self {
75+ terminal : setup_terminal ( ) ?,
76+ restored : false ,
77+ } )
78+ }
79+
80+ fn terminal ( & mut self ) -> & mut Terminal < CrosstermBackend < io:: Stdout > > {
81+ & mut self . terminal
82+ }
83+
84+ fn restore ( & mut self ) -> Result < ( ) > {
85+ if !self . restored {
86+ restore_terminal ( & mut self . terminal ) ?;
87+ self . restored = true ;
88+ }
89+ Ok ( ( ) )
90+ }
91+ }
92+
93+ impl Drop for TerminalGuard {
94+ fn drop ( & mut self ) {
95+ let _ = self . restore ( ) ;
96+ }
97+ }
98+
5599fn run_loop ( terminal : & mut Terminal < CrosstermBackend < io:: Stdout > > ) -> Result < ( ) > {
56100 let mut app = App :: new ( ConfigStore :: new ( ) ?) ?;
57101 loop {
0 commit comments