packages feed

terminal-size-0.3.4: src/System/Console/Terminal/Windows.hs

module System.Console.Terminal.Windows(size, hSize) where

import System.Console.Terminal.Common

import System.Exit
import System.IO
import System.IO.Error (catchIOError)
import System.Process
import System.Win32.Console
  ( CONSOLE_SCREEN_BUFFER_INFO(srWindow)
  , SMALL_RECT(..)
  , getConsoleScreenBufferInfo
  )
import System.Win32.Types (HANDLE, withHandleToHANDLE)

size :: Integral n => IO (Maybe (Window n))
size = hSize stdout

hSize :: Integral n => Handle -> IO (Maybe (Window n))
hSize hdl =
    withHandleToHANDLE hdl nativeSize
    `catchIOError` \_ -> do
        -- Fallback to use for Cygwin or MSYS
        let stty = (shell "stty size") {
              std_in  = UseHandle hdl
            , std_out = CreatePipe
            , std_err = CreatePipe
            }
        (_, mbStdout, _, rStty) <- createProcess stty
        exStty <- waitForProcess rStty
        case exStty of
            ExitFailure _ -> return Nothing
            ExitSuccess ->
                maybe (return Nothing)
                      (\out -> do
                          sizeStr <- hGetContents out
                          let [r, c] = map read $ words sizeStr :: [Int]
                          return $ Just $ Window (fromIntegral r) (fromIntegral c)
                      )
                      mbStdout

nativeSize :: Integral n => HANDLE -> IO (Maybe (Window n))
nativeSize hdl = do
    rect <- srWindow <$> getConsoleScreenBufferInfo hdl
    return $ Just $ Window
        { height = fromIntegral (1 + bottomPos rect - topPos rect)
        , width = fromIntegral (1 + rightPos rect - leftPos rect)
        }