module ParkBench.Internal.Terminal ( withTerminal, Terminal, renderToTerminal, blue, green, red, ) where import Control.Exception (bracket_) import qualified Data.ByteString as ByteString import Data.IORef import qualified Data.Text.Encoding as Text (encodeUtf8) import ParkBench.Internal.Builder (Builder) import qualified ParkBench.Internal.Builder as Builder import ParkBench.Internal.Prelude import System.IO (hSetEcho, stdin) withTerminal :: (Terminal -> IO a) -> IO a withTerminal :: forall a. (Terminal -> IO a) -> IO a withTerminal Terminal -> IO a action = do Terminal terminal <- IO Terminal newTerminal forall a b c. IO a -> IO b -> IO c -> IO c bracket_ IO () hideCursor IO () showCursor do forall a b c. IO a -> IO b -> IO c -> IO c bracket_ (Handle -> Bool -> IO () hSetEcho Handle stdin Bool False) (Handle -> Bool -> IO () hSetEcho Handle stdin Bool True) do ByteString -> IO () ByteString.putStr (Word8 -> ByteString ByteString.singleton Word8 newline) Terminal -> IO a action Terminal terminal newtype Terminal = Terminal (IORef Int) newTerminal :: IO Terminal newTerminal :: IO Terminal newTerminal = coerce :: forall a b. Coercible a b => a -> b coerce (forall a. a -> IO (IORef a) newIORef (Int 0 :: Int)) renderToTerminal :: Terminal -> Builder -> IO () renderToTerminal :: Terminal -> Builder -> IO () renderToTerminal (Terminal IORef Int lastLineCountRef) Builder bytes0 = do Int lastLineCount <- forall a. IORef a -> IO a readIORef IORef Int lastLineCountRef let bytes :: ByteString bytes = Text -> ByteString Text.encodeUtf8 (Builder -> Text Builder.build (Int -> Builder cursorUp Int lastLineCount forall a. Semigroup a => a -> a -> a <> Builder clearFromCursor forall a. Semigroup a => a -> a -> a <> Builder bytes0)) ByteString -> IO () ByteString.putStr ByteString bytes forall a. IORef a -> a -> IO () writeIORef IORef Int lastLineCountRef forall a b. (a -> b) -> a -> b $! Word8 -> ByteString -> Int ByteString.count Word8 newline ByteString bytes newline :: Word8 newline :: Word8 newline = Word8 10 hideCursor :: IO () hideCursor :: IO () hideCursor = ByteString -> IO () ByteString.putStr ByteString "\ESC[?25l" showCursor :: IO () showCursor :: IO () showCursor = ByteString -> IO () ByteString.putStr ByteString "\ESC[?25h" clearFromCursor :: Builder clearFromCursor :: Builder clearFromCursor = Builder "\ESC[0J" cursorUp :: Int -> Builder cursorUp :: Int -> Builder cursorUp Int n = Builder "\ESC[" forall a. Semigroup a => a -> a -> a <> forall a. Integral a => a -> Builder Builder.decimal Int n forall a. Semigroup a => a -> a -> a <> Builder "F" blue :: Builder -> Builder blue :: Builder -> Builder blue Builder s = Builder "\ESC[34m" forall a. Semigroup a => a -> a -> a <> Builder s forall a. Semigroup a => a -> a -> a <> Builder "\ESC[39m" green :: Builder -> Builder green :: Builder -> Builder green Builder s = Builder "\ESC[32m" forall a. Semigroup a => a -> a -> a <> Builder s forall a. Semigroup a => a -> a -> a <> Builder "\ESC[39m" red :: Builder -> Builder red :: Builder -> Builder red Builder s = Builder "\ESC[31m" forall a. Semigroup a => a -> a -> a <> Builder s forall a. Semigroup a => a -> a -> a <> Builder "\ESC[39m"