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 に依存したくない場合は、 Getting terminal width in C?ioctl()
の受け入れられた回答に基づいて、FFI を使用した適切なリクエストのラッパーを次に示します。
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)
これは、hsc2hs
プリプロセッサを使用して、ハードコーディングするのではなく、C ヘッダーに基づいて正しい定数とオフセットを見つけます。GHC か Haskell Platform のどちらかでパッケージ化されていると思いますので、すでにお持ちの可能性があります。
Cabal を使用している場合は、ファイルに を追加TermSize.hs
すると、 .cabal.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