webapp-0.2.0: Web/App/Internal/TerminalSize.hsc
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Web.App.TerminalSize
License : MIT
Maintainer : nate@symer.io
Stability : experimental
Portability : POSIX
Get the size of the terminal. Adapted from http://stackoverflow.com/questions/12806053/get-terminal-width-haskell.
-}
module Web.App.Internal.TerminalSize
(
-- * Terminal Size
getTermSize
)
where
import Foreign
import Foreign.C.Error
import Foreign.C.Types
import Control.Monad as M (void)
#include <sys/ioctl.h>
#include <unistd.h>
-- Trick for calculating alignment of a type, taken from
-- http://www.haskell.org/haskellwiki/FFICookBook#Working_with_structs
#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
-- @xpixel@ and @ypixel@ fields are unused,
-- so they've been omitted.
data WinSize = WinSize CUShort CUShort -- row, col
instance Storable WinSize where
sizeOf _ = (#size struct winsize)
alignment _ = (#alignment struct winsize)
peek ptr = WinSize
<$> (#peek struct winsize, ws_row) ptr
<*> (#peek struct winsize, ws_col) ptr
poke ptr (WinSize row col) = do
(#poke struct winsize, ws_row) ptr row
(#poke struct winsize, ws_col) ptr col
foreign import ccall "sys/ioctl.h ioctl"
ioctl :: CInt -> CInt -> Ptr WinSize -> IO CInt
-- | Get the size of the terminal.
getTermSize :: IO (Maybe (Int, Int)) -- ^ @(rows, columns)@
getTermSize = with (WinSize 0 0) $ \ws -> do
resetErrno
M.void $ ioctl (#const STDOUT_FILENO) (#const TIOCGWINSZ) ws
err <- getErrno
if isValidErrno err
then do
WinSize row col <- peek ws
return $ Just (fromIntegral row, fromIntegral col)
else return Nothing