Get Haskell Terminal Width

How to get terminal width in Haskell?

Things i tried

System.Posix.IOCtl (could not figure out how to get it to work) 

This should only work unix.

thanks

+7
source share
3 answers

If you don't need a dependency on ncurses, here is the shell of the corresponding ioctl() request using FFI, based on the accepted answer Getting the terminal width in C?

TermSize.hsc

 {-# LANGUAGE ForeignFunctionInterface #-} module TermSize (getTermSize) where import Foreign import Foreign.C.Error import Foreign.C.Types #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__) -- The ws_xpixel and ws_ypixel fields are unused, so I've omitted them here. data WinSize = WinSize { wsRow, wsCol :: CUShort } instance Storable WinSize where sizeOf _ = (#size struct winsize) alignment _ = (#alignment struct winsize) peek ptr = do row <- (#peek struct winsize, ws_row) ptr col <- (#peek struct winsize, ws_col) ptr return $ WinSize row col 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 -- | Return current number of (rows, columns) of the terminal. getTermSize :: IO (Int, Int) getTermSize = with (WinSize 0 0) $ \ws -> do throwErrnoIfMinus1 "ioctl" $ ioctl (#const STDOUT_FILENO) (#const TIOCGWINSZ) ws WinSize row col <- peek ws return (fromIntegral row, fromIntegral col) 

It uses the hsc2hs preprocessor to determine the correct constants and offsets based on the C headers, rather than hard code them. I think it is packaged with either GHC or the Haskell platform, so most likely you will already do it.

If you are using Cabal, you can add TermSize.hs to your .cabal file and it will automatically learn how to generate it from TermSize.hsc . Otherwise, you can run hsc2hs TermSize.hsc manually to generate the .hs file, which can then be compiled with GHC.

+12
source

You can use hcurses . After initializing the library, you can use scrSize to get the number of rows and columns on the screen.

To use System.Posix.IOCtl , you need to define the data type to represent the TIOCGWINSZ request, which populates the following structure:

 struct winsize { unsigned short ws_row; unsigned short ws_col; unsigned short ws_xpixel; /* unused */ unsigned short ws_ypixel; /* unused */ }; 

You will need to define a Haskell data type to store this information and make it an instance of Storable :

 {-# LANGUAGE RecordWildCards #-} import Foreign.Storable import Foreign.Ptr import Foreign.C data Winsize = Winsize { ws_row :: CUShort , ws_col :: CUShort , ws_xpixel :: CUShort , ws_ypixel :: CUShort } instance Storable Winsize where sizeOf _ = 8 alignment _ = 2 peek p = do { ws_row <- peekByteOff p 0 ; ws_col <- peekByteOff p 2 ; ws_xpixel <- peekByteOff p 4 ; ws_ypixel <- peekByteOff p 6 ; return $ Winsize {..} } poke p Winsize {..} = do { pokeByteOff p 0 ws_row ; pokeByteOff p 2 ws_col ; pokeByteOff p 4 ws_xpixel ; pokeByteOff p 6 ws_ypixel } 

Now you need to create a dummy data type to represent your query:

 data TIOCGWINSZ = TIOCGWINSZ 

Finally, you need to make your request an IOControl instance IOControl and bind it to the Winsize data Winsize .

 instance IOControl TIOCGWINSZ Winsize where ioctlReq _ = ?? 

Will you need to replace ?? the constant represented by TIOCGWINSZ in your header files ( 0x5413 on my system).

Now you are ready to issue ioctl . This command does not care about the input, so you want to use the ioctl' form:

 main = do { ws <- ioctl' 1 TIOCGWINSZ ; putStrLn $ "My terminal is " ++ show (ws_col ws) ++ " columns wide" } 

Please note that 1 refers to STDOUT.

Phew!

+9
source

Since you only need this on Unix, I would recommend:

resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""

And then do a little bit of output analysis. It may not be 100% portable, but I suggest that you can provide resize arguments (in particular, -u ), so you get pretty consistent output.

+3
source

All Articles