获取终端宽度Haskell

Bil*_*ain 8 terminal console haskell ioctl functional-programming

如何在Haskell中获得终端的宽度?

我试过的事情

System.Posix.IOCtl (could not figure out how to get it to work) 
Run Code Online (Sandbox Code Playgroud)

这只需要工作unix.

谢谢

ham*_*mar 15

如果你不想依赖于ncurses,那么这里是ioctl()使用FFI 的相应请求的包装器,基于在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)
Run Code Online (Sandbox Code Playgroud)

这使用hsc2hs预处理器根据C头找出正确的常量和偏移量,而不是硬编码它们.我认为它与GHC或Haskell平台打包在一起,所以你很可能已经拥有它.

如果你正在使用Cabal,你可以添加TermSize.hs到你的.cabal文件中,它会自动知道如何生成它TermSize.hsc.否则,您可以hsc2hs TermSize.hsc手动运行以生成一个.hs文件,然后可以使用GHC进行编译.


pat*_*pat 9

你可以使用hcurses.初始化库后,可以使用scrSize获取屏幕上的行数和列数.

要使用System.Posix.IOCtl,您必须定义表示TIOCGWINSZ请求的数据类型,该类型填充以下结构:

struct winsize {
    unsigned short ws_row;
    unsigned short ws_col;
    unsigned short ws_xpixel;   /* unused */
    unsigned short ws_ypixel;   /* unused */
};
Run Code Online (Sandbox Code Playgroud)

您需要定义一个Haskell数据类型来保存此信息,并使其成为以下实例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
                           }
Run Code Online (Sandbox Code Playgroud)

现在,您需要创建一个虚拟数据类型来表示您的请求:

data TIOCGWINSZ = TIOCGWINSZ
Run Code Online (Sandbox Code Playgroud)

最后,您需要让您的请求键入一个实例IOControl,并将其与Winsize数据类型相关联.

instance IOControl TIOCGWINSZ Winsize where
  ioctlReq _ = ??
Run Code Online (Sandbox Code Playgroud)

您需要将头文件(在我的系统上)中??表示的常量替换为.TIOCGWINSZ0x5413

现在,您已准备好发布ioctl.此命令不关心输入数据,因此您要使用以下ioctl'格式:

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

请注意,1指的是STDOUT.

唷!

  • 这不是有点矫枉过正吗?没有更简单的吗? (2认同)