@@ -2,11 +2,19 @@ module Hix.Terminal.State where
22
33import Control.Monad.Catch (bracket )
44import Data.IORef (IORef , newIORef , readIORef )
5- import System.Console.ANSI (hHideCursor , hSaveCursor , hSetCursorColumn , hSetCursorPosition , hShowCursor )
5+ import System.Console.ANSI (
6+ hClearLine ,
7+ hCursorDown ,
8+ hHideCursor ,
9+ hSaveCursor ,
10+ hSetCursorColumn ,
11+ hSetCursorPosition ,
12+ hShowCursor ,
13+ )
614import System.IO (BufferMode (.. ), Handle , hFlush , hGetBuffering , hPutStr , hSetBuffering , hSetEcho , stdin )
715import System.Posix (Fd )
816
9- import Hix.Data.Monad (M )
17+ import Hix.Data.Monad (AppResources ( .. ), M , appRes )
1018import Hix.Error (ignoringIOErrors )
1119import Hix.Monad (appContextVerboseIO )
1220import Hix.Terminal.Geometry (runRender , terminalGeometry )
@@ -58,37 +66,56 @@ hInitializeTerminal tuiState = do
5866 stateRef <- newOutputState tuiState
5967 pure (bufMode, stateRef)
6068
61- -- | After a Brick app exits, move the cursor below the rendered area so subsequent output
62- -- (log messages, next UI) doesn't overwrite the previous content.
63- repositionCursor :: Handle -> IORef OutputState -> IO ()
64- repositionCursor hOut stateRef = do
69+ -- | After a Brick app exits, reposition the cursor.
70+ --
71+ -- When @persistent@ is 'True', move below the rendered area so subsequent output doesn't
72+ -- overwrite the previous content.
73+ --
74+ -- When @persistent@ is 'False', move back to the first line of the rendered area and clear
75+ -- the lines so the next output starts where the UI was.
76+ repositionCursor :: Bool -> Handle -> IORef OutputState -> IO ()
77+ repositionCursor persistent hOut stateRef = do
6578 OutputState {firstLine, lastRenderHeight} <- readIORef stateRef
6679 when (lastRenderHeight > 0 ) do
67- -- Move to the last rendered line and emit a newline.
68- -- This works even when the cursor is at the bottom of the terminal,
69- -- where hSetCursorPosition would be clamped and fail to advance.
70- let lastLine = firstLine + lastRenderHeight - 1
71- hSetCursorPosition hOut lastLine 0
72- hPutStr hOut " \n "
73- hFlush hOut
80+ if persistent
81+ then do
82+ -- Move to the last rendered line and emit a newline.
83+ -- This works even when the cursor is at the bottom of the terminal,
84+ -- where hSetCursorPosition would be clamped and fail to advance.
85+ let lastLine = firstLine + lastRenderHeight - 1
86+ hSetCursorPosition hOut lastLine 0
87+ hPutStr hOut " \n "
88+ hFlush hOut
89+ else do
90+ -- Move back to the first line and clear the rendered area.
91+ hSetCursorPosition hOut firstLine 0
92+ replicateM_ lastRenderHeight do
93+ hClearLine hOut
94+ hCursorDown hOut 1
95+ hSetCursorPosition hOut firstLine 0
96+ hFlush hOut
7497
7598hResetTerminal ::
99+ Bool ->
76100 IORef VtyTuiResources ->
77101 (BufferMode , IORef OutputState ) ->
78102 IO ()
79- hResetTerminal tuiState (hInMode, stateRef) = do
103+ hResetTerminal persistent tuiState (hInMode, stateRef) = do
80104 VtyTuiResources {hIn, hOut} <- readIORef tuiState
81- ignoringIOErrors $ repositionCursor hOut stateRef
82- ignoringIOErrors $ hShowCursor hOut
83- ignoringIOErrors $ hFlush hOut
84- ignoringIOErrors $ hSetEcho hIn True
85- ignoringIOErrors $ hSetBuffering hIn hInMode
105+ traverse_ @ [] ignoringIOErrors [
106+ repositionCursor persistent hOut stateRef,
107+ hShowCursor hOut,
108+ hFlush hOut,
109+ hSetEcho hIn True ,
110+ hSetBuffering hIn hInMode
111+ ]
86112
87113bracketTerminal ::
88114 IORef VtyTuiResources ->
89115 (IORef OutputState -> IO a ) ->
90116 M a
91- bracketTerminal tuiState use =
117+ bracketTerminal tuiState use = do
118+ persistent <- appRes. persistentUi
92119 appContextVerboseIO " running brick app" do
93- bracket (hInitializeTerminal tuiState) (hResetTerminal tuiState) \ (_, stateRef) ->
120+ bracket (hInitializeTerminal tuiState) (hResetTerminal persistent tuiState) \ (_, stateRef) ->
94121 use stateRef
0 commit comments