@@ -315,13 +315,30 @@ enum KeyboardRestore {
315315 ResetAfterExit ,
316316}
317317
318+ #[ derive( Clone , Copy ) ]
319+ pub ( crate ) enum TerminalHandoff {
320+ Restore ,
321+ KeepScreen ,
322+ }
323+
318324fn restore_common (
319325 raw_mode_restore : RawModeRestore ,
320326 keyboard_restore : KeyboardRestore ,
327+ handoff : TerminalHandoff ,
321328) -> Result < ( ) > {
322329 let mut first_error = ensure_virtual_terminal_processing ( ) . err ( ) ;
323330
324- if let Err ( err) = ALTERNATE_SCREEN . restore ( & mut stdout ( ) , keyboard_restore) {
331+ let screen_result = match handoff {
332+ TerminalHandoff :: KeepScreen if ALTERNATE_SCREEN . is_active ( ) => {
333+ let result = ALTERNATE_SCREEN . release_input ( & mut stdout ( ) ) ;
334+ let _ = execute ! ( stdout( ) , keyboard_modes:: DisableModifyOtherKeys ) ;
335+ result
336+ }
337+ TerminalHandoff :: Restore | TerminalHandoff :: KeepScreen => {
338+ ALTERNATE_SCREEN . restore ( & mut stdout ( ) , keyboard_restore)
339+ }
340+ } ;
341+ if let Err ( err) = screen_result {
325342 first_error. get_or_insert ( err) ;
326343 }
327344
@@ -355,7 +372,11 @@ fn restore_common(
355372/// Inverse of `set_modes`.
356373#[ cfg( unix) ]
357374pub fn restore ( ) -> Result < ( ) > {
358- restore_common ( RawModeRestore :: Disable , KeyboardRestore :: PopStack )
375+ restore_common (
376+ RawModeRestore :: Disable ,
377+ KeyboardRestore :: PopStack ,
378+ TerminalHandoff :: Restore ,
379+ )
359380}
360381
361382/// Force crossterm's cached raw-mode state back in sync with the terminal after `fg`.
@@ -375,8 +396,12 @@ pub(super) fn reapply_raw_mode_after_resume() -> Result<()> {
375396/// Uses a stronger keyboard reset than `restore` so the parent shell recovers even if a
376397/// terminal missed the stack pop that normally pairs with [`set_modes`].
377398pub fn restore_after_exit ( ) -> Result < ( ) > {
378- let mut first_error =
379- restore_common ( RawModeRestore :: Disable , KeyboardRestore :: ResetAfterExit ) . err ( ) ;
399+ let mut first_error = restore_common (
400+ RawModeRestore :: Disable ,
401+ KeyboardRestore :: ResetAfterExit ,
402+ TerminalHandoff :: Restore ,
403+ )
404+ . err ( ) ;
380405 if let Err ( err) = terminal_stderr:: finish ( ) {
381406 first_error. get_or_insert ( err) ;
382407 }
@@ -388,8 +413,8 @@ pub fn restore_after_exit() -> Result<()> {
388413}
389414
390415/// Restore the terminal to its original state, but keep raw mode enabled.
391- pub fn restore_keep_raw ( ) -> Result < ( ) > {
392- restore_common ( RawModeRestore :: Keep , KeyboardRestore :: PopStack )
416+ fn restore_keep_raw ( handoff : TerminalHandoff ) -> Result < ( ) > {
417+ restore_common ( RawModeRestore :: Keep , KeyboardRestore :: PopStack , handoff )
393418}
394419
395420/// Flush the underlying stdin buffer to clear any input that may be buffered at the terminal level.
@@ -887,21 +912,55 @@ impl Tui {
887912 /// This pauses crossterm's stdin polling by dropping the underlying event stream, restores
888913 /// terminal modes and stderr while keeping raw mode enabled, then re-applies Codex TUI modes
889914 /// and stderr suppression before resuming events.
890- pub async fn with_restored < R , F , Fut > ( & mut self , f : F ) -> R
915+ pub ( crate ) async fn with_restored < R , F , Fut > ( & mut self , handoff : TerminalHandoff , f : F ) -> R
891916 where
892917 F : FnOnce ( ) -> Fut ,
893918 Fut : Future < Output = R > ,
894919 {
895920 // Pause crossterm events to avoid stdin conflicts with external program `f`.
896921 self . pause_events ( ) ;
897922
898- // Leave alt screen if active to avoid conflicts with external program `f`.
923+ // Restore both screens' input modes before giving the terminal to an editor. Repaint the
924+ // last frame on the alternate screen so separate-window editors leave Codex visible.
899925 let was_alt_screen = self . is_alt_screen_active ( ) ;
900- if was_alt_screen {
901- let _ = self . leave_alt_screen_for_handoff ( ) ;
902- }
903-
904- if let Err ( err) = restore_keep_raw ( ) {
926+ let visible_frame = ( was_alt_screen && matches ! ( handoff, TerminalHandoff :: KeepScreen ) )
927+ . then ( || self . terminal . previous_buffer ( ) . clone ( ) ) ;
928+ let restore_result = if let Some ( visible_frame) = visible_frame {
929+ stdout ( )
930+ . sync_update ( |_| {
931+ let leave_result = self . leave_alt_screen_for_handoff ( ) ;
932+ let main_result = restore_keep_raw ( TerminalHandoff :: Restore ) ;
933+ let enter_result = self . enter_alt_screen ( ) ;
934+ if !self . is_alt_screen_active ( ) {
935+ return leave_result. and ( main_result) . and ( enter_result) ;
936+ }
937+ let draw_result = if enter_result. is_ok ( ) {
938+ self . terminal . draw ( |frame| {
939+ let area = frame. area ( ) . intersection ( visible_frame. area ) ;
940+ for y in area. top ( ) ..area. bottom ( ) {
941+ for x in area. left ( ) ..area. right ( ) {
942+ frame. buffer_mut ( ) [ ( x, y) ] = visible_frame[ ( x, y) ] . clone ( ) ;
943+ }
944+ }
945+ } )
946+ } else {
947+ Ok ( ( ) )
948+ } ;
949+ let input_result = restore_keep_raw ( TerminalHandoff :: KeepScreen ) ;
950+ leave_result
951+ . and ( main_result)
952+ . and ( enter_result)
953+ . and ( draw_result)
954+ . and ( input_result)
955+ } )
956+ . and_then ( std:: convert:: identity)
957+ } else {
958+ if was_alt_screen {
959+ let _ = self . leave_alt_screen_for_handoff ( ) ;
960+ }
961+ restore_keep_raw ( TerminalHandoff :: Restore )
962+ } ;
963+ if let Err ( err) = restore_result {
905964 tracing:: warn!( "failed to restore terminal modes before external program: {err}" ) ;
906965 }
907966 if let Err ( err) = terminal_stderr:: pause ( ) {
@@ -910,6 +969,12 @@ impl Tui {
910969
911970 let output = f ( ) . await ;
912971
972+ if was_alt_screen && matches ! ( handoff, TerminalHandoff :: KeepScreen ) {
973+ // The editor may already have left the alternate screen. Return to the main screen
974+ // before reapplying its keyboard mode and entering the alternate screen again.
975+ let _ = self . leave_alt_screen_for_handoff ( ) ;
976+ }
977+
913978 if let Err ( err) = terminal_stderr:: resume ( ) {
914979 tracing:: warn!( "failed to suppress terminal stderr after external program: {err}" ) ;
915980 }
0 commit comments