Jak uzyskać szerokość terminala w Haskell?Uzyskaj szerokość terminala Haskell
Czego próbowałem
System.Posix.IOCtl (could not figure out how to get it to work)
Ma tylko pracować UNIX.
Dzięki
Jak uzyskać szerokość terminala w Haskell?Uzyskaj szerokość terminala Haskell
Czego próbowałem
System.Posix.IOCtl (could not figure out how to get it to work)
Ma tylko pracować UNIX.
Dzięki
Jeśli nie chcesz zależność ncurses, oto owinięcie z odpowiednim ioctl()
żądanie przy użyciu FFI, na podstawie zaakceptowanej odpowiedzi z Getting 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)
Wykorzystuje to hsc2hs
preprocessor do obliczenia poprawnych stałych i przesunięć w oparciu o nagłówki C zamiast ich kodowania na sztywno. Myślę, że jest on zapakowany albo z GHC, albo z platformy Haskell, więc prawdopodobnie już go masz.
Jeśli używasz Cabal można dodać TermSize.hs
do pliku .cabal
i będzie ona automatycznie wie jak wygenerować go z TermSize.hsc
. W przeciwnym razie można ręcznie uruchomić hsc2hs TermSize.hsc
, aby wygenerować plik .hs
, który następnie można skompilować za pomocą GHC.
można użyć hcurses. Po zainicjowaniu biblioteki można użyć numeru scrSize
, aby uzyskać liczbę wierszy i kolumn na ekranie.
Aby korzystać System.Posix.IOCtl
, trzeba określić typ danych do reprezentowania wniosek TIOCGWINSZ
, która wypełnia w następującej strukturze:
struct winsize {
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel; /* unused */
unsigned short ws_ypixel; /* unused */
};
Musisz określić typ danych Haskell trzymać tę informację, i sprawiają, że wystąpienie 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
}
teraz trzeba stworzyć fikcyjny typ danych do reprezentowania żądanie:
data TIOCGWINSZ = TIOCGWINSZ
Wreszcie, należy wprowadzić żądanie typu instancji o numerze IOControl
i powiązać je z typem danych Winsize
.
instance IOControl TIOCGWINSZ Winsize where
ioctlReq _ = ??
Trzeba będzie wymienić ??
ze stałą reprezentowanego przez TIOCGWINSZ
w plikach nagłówkowych (0x5413
w moim systemie).
Teraz jesteś gotowy do wydania ioctl
. Komenda ta nie dba o danych wejściowych, więc chcesz skorzystać z formularza ioctl'
:
main = do { ws <- ioctl' 1 TIOCGWINSZ
; putStrLn $ "My terminal is " ++ show (ws_col ws) ++ " columns wide"
}
Należy pamiętać, że 1 odnosi się do STDOUT.
Uff!
Czy to trochę przesada? Czy nie ma nic prostszego? –
Uwaga: '0' w wywołaniu' ioctl'' odnosi się do STDIN, więc to się nie powiedzie, jeśli STDIN jest przekierowany. Zakładając, że celem uzyskania szerokości terminala jest formatowanie wyjścia, lepiej zamiast tego zapytać STDOUT. – hammar
Dobra uwaga. Zaktualizowałem swoją odpowiedź, ale twoja odpowiedź jest znacznie bardziej niezawodna. Chciałbym móc dać więcej niż 1 w górę; twoja odpowiedź, jeśli zakorkowana pełna przydatnych informacji! – pat
Ponieważ trzeba to tylko na Uniksie, polecam:
resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""
i wtedy robi się trochę-bitowej parsowanie wyjścia. To może nie być w 100% przenośne, ale uważam, że możesz podać resize
z argumentami (szczególnie sprawdź w szczególności -u
), aby uzyskać dość spójne dane wyjściowe.
To jest fajne, muszę spojrzeć na hsc2hs! – pat
Bardzo ładne, dzięki –