@@ -206,6 +206,7 @@ func (c *Client) RunAttach(name string, command []string, dk DetachKey, forwardE
206206 case * protocol.Output :
207207 os .Stdout .Write (m .Data )
208208 case * protocol.Exited :
209+ restoreHostTerminal (fd , oldState , "[hauntty] session exited\n " )
209210 return & ExitError {Code : int (m .ExitCode )}
210211 case * protocol.Error :
211212 term .Restore (fd , oldState )
@@ -222,36 +223,40 @@ func (c *Client) RunAttach(name string, command []string, dk DetachKey, forwardE
222223 if err != nil {
223224 close (done )
224225 if err == io .EOF || isConnClosed (err ) {
225- // Use 1047 (not 1049) to exit alt screen: 1047
226- // just switches the buffer without restoring the
227- // saved cursor, so session content on the primary
228- // screen stays intact. No-op when already on primary.
229- //
230- // Reset modes, show cursor, pop kitty keyboard,
231- // reset SGR, erase from cursor to end of screen.
232- // Session content above the cursor is preserved.
233- os .Stdout .Write ([]byte (
234- "\x1b [?1047;1;1000;1002;1003;1006;1004;2004;2048;2026l" +
235- "\x1b [?25h" +
236- "\x1b [<u" +
237- "\x1b [0m" +
238- "\x1b [J" ))
239- drainStdin (fd , 20 * time .Millisecond )
240- term .Restore (fd , oldState )
241- fmt .Fprintf (os .Stderr , "[hauntty] detached\n " )
226+ restoreHostTerminal (fd , oldState , "[hauntty] detached\n " )
242227 return nil
243228 }
244229 term .Restore (fd , oldState )
245230 return fmt .Errorf ("read message: %w" , err )
246231 }
247232 if err := handleMsg (msg ); err != nil {
248233 close (done )
249- term .Restore (fd , oldState )
250234 return err
251235 }
252236 }
253237}
254238
239+ func restoreHostTerminal (fd int , oldState * term.State , message string ) {
240+ // Use 1047 (not 1049) to exit alt screen: 1047 just switches the
241+ // buffer without restoring the saved cursor, so session content on
242+ // the primary screen stays intact. No-op when already on primary.
243+ //
244+ // Reset modes, show cursor, pop kitty keyboard, reset SGR, erase
245+ // from cursor to end of screen. Session content above the cursor is
246+ // preserved.
247+ os .Stdout .Write ([]byte (
248+ "\x1b [?1047;1;1000;1002;1003;1006;1004;2004;2048;2026l" +
249+ "\x1b [?25h" +
250+ "\x1b [<u" +
251+ "\x1b [0m" +
252+ "\x1b [J" ))
253+ drainStdin (fd , 20 * time .Millisecond )
254+ term .Restore (fd , oldState )
255+ if message != "" {
256+ fmt .Fprint (os .Stderr , message )
257+ }
258+ }
259+
255260// readCursorRow reads the DSR response (\x1b[{row};{col}R) from fd
256261// and returns the cursor row. Must be called in raw mode after sending
257262// \x1b[6n. Returns fallback if the response cannot be parsed.
0 commit comments