8

Haskellで端末の幅を取得するには?

私が試したこと

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

これは unix で動作する必要があります。

ありがとう

4

3 に答える 3

16

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 でコンパイルできるファイルを生成できます。

于 2012-10-09T19:58:28.627 に答える
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 */
};

この情報を保持する 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 _ = ??

を、ヘッダー ファイル (私のシステムでは)で??表される定数に置き換える必要があります。TIOCGWINSZ0x5413

これで、発行する準備が整いましたioctl。このコマンドは入力データを気にしないため、次のioctl'形式を使用します。

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

1 は STDOUT を指すことに注意してください。

ふぅ!

于 2012-10-09T18:35:14.557 に答える
3

これは Unix でのみ必要なので、次のことをお勧めします。

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

そして、出力の解析を少し行います。これは 100% 移植可能ではないかもしれませんが、引数を指定できると思います(特にresizeチェックしてください)。かなり一貫した出力が得られます。-u

于 2012-10-10T04:04:30.093 に答える