packages feed

hs-sdl-term-emulator-0.1.0.4: src/System/Terminal/Emulator/SDL/Pty.hs

{-# LANGUAGE ScopedTypeVariables #-}

module System.Terminal.Emulator.SDL.Pty where

import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Exception (bracket, try)
import Data.ByteString (ByteString)
import System.Environment (getEnvironment)
import System.Posix.Pty
import System.Process

termEnvVarName :: String
termEnvVarName = "xterm"

launchPty :: (Int, Int) -> (STM ByteString) -> (STM (Int, Int)) -> (ByteString -> IO ()) -> IO ()
launchPty initialSize getInput getResize onOutput = do
  currentEnv <- getEnvironment

  let environ :: [(String, String)]
      environ =
        filter ((/= "TERM") . fst) currentEnv
          <> [("TERM", termEnvVarName)]

  bracket
    (spawnWithPty (Just environ) False "/bin/bash" [] initialSize)
    ( \(pty, processHandle) -> do
        closePty pty
        _ <- waitForProcess processHandle
        pure ()
    )
    ( \(pty, _) -> do
        let inputLoop :: IO ()
            inputLoop = do
              event <- atomically $ (getInput >>= pure . Left) `orElse` (getResize >>= pure . Right)
              case event of
                Left inputBuf -> writePty pty inputBuf
                Right newSize -> resizePty pty newSize
              inputLoop

        let readNext :: IO ()
            readNext = do
              result <- try $ do
                threadWaitReadPty pty
                readPty pty
              case result of
                Right output -> do
                  onOutput output
                  readNext
                Left (_ :: IOError) -> do
                  pure ()

        withAsync inputLoop $ \inputLoopAsync -> do
          link inputLoopAsync
          readNext
    )