如何在 Haskell 中获取终端的宽度?
我尝试过的事情
System.Posix.IOCtl (could not figure out how to get it to work)
这只适用于unix。
谢谢
如何在 Haskell 中获取终端的宽度?
我尝试过的事情
System.Posix.IOCtl (could not figure out how to get it to work)
这只适用于unix。
谢谢
如果您不想依赖 ncurses,这里是ioctl()
使用 FFI 的适当请求的包装器,基于在 C 中获取终端宽度的接受答案?
术语大小.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)
这使用hsc2hs
预处理器根据 C 标头计算出正确的常量和偏移量,而不是对它们进行硬编码。我认为它与 GHC 或 Haskell 平台一起打包,所以你很可能已经拥有它。
如果您使用的是 Cabal,您可以添加TermSize.hs
到您的.cabal
文件中,它会自动知道如何从TermSize.hsc
. 否则,您可以hsc2hs TermSize.hsc
手动运行以生成一个.hs
文件,然后您可以使用 GHC 编译该文件。
你可以使用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 */
};
您需要定义一个 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
}
现在,您需要创建一个虚拟数据类型来表示您的请求:
data TIOCGWINSZ = TIOCGWINSZ
最后,您需要将您的请求类型设为 的实例IOControl
,并将其与Winsize
数据类型相关联。
instance IOControl TIOCGWINSZ Winsize where
ioctlReq _ = ??
您需要用头文件(在我的系统上)中??
表示的常量替换 。TIOCGWINSZ
0x5413
现在,您已准备好发布ioctl
. 此命令不关心输入数据,因此您要使用以下ioctl'
形式:
main = do { ws <- ioctl' 1 TIOCGWINSZ
; putStrLn $ "My terminal is " ++ show (ws_col ws) ++ " columns wide"
}
请注意,1 指的是 STDOUT。
呸!
由于您仅在 Unix 上需要它,我建议:
resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""
然后对输出进行一点解析。这可能不是 100% 可移植的,但我相信您可以提供resize
参数(-u
特别是检查),因此您将获得相当一致的输出。