¿Cómo obtener el ancho de la terminal en Haskell?Obtener ancho de terminal Haskell
cosas que intenté
System.Posix.IOCtl (could not figure out how to get it to work)
Esto sólo tiene que trabajar UNIX.
Gracias
¿Cómo obtener el ancho de la terminal en Haskell?Obtener ancho de terminal Haskell
cosas que intenté
System.Posix.IOCtl (could not figure out how to get it to work)
Esto sólo tiene que trabajar UNIX.
Gracias
Si no desea que una dependencia de ncurses, aquí es un envoltorio de la ioctl()
solicitud apropiada utilizando la FFI, basado en la respuesta aceptada de 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)
Utiliza el hsc2hs
preprocessor para descubrir las constantes y compensaciones correctas basadas en los encabezados C en lugar de codificarlos en duro. Creo que está empaquetado con GHC o la plataforma Haskell, así que es probable que ya lo tengas.
Si está utilizando Cabal, puede agregar TermSize.hs
a su archivo .cabal
y automáticamente sabrá cómo generarlo desde TermSize.hsc
. De lo contrario, puede ejecutar hsc2hs TermSize.hsc
manualmente para generar un archivo .hs
que luego puede compilar con GHC.
usted podría utilizar hcurses. Una vez que haya inicializado la biblioteca, puede usar scrSize
para obtener el número de filas y columnas en la pantalla.
Para utilizar System.Posix.IOCtl
, hay que definir un tipo de datos para representar la solicitud TIOCGWINSZ
, que rellena la siguiente estructura:
struct winsize {
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel; /* unused */
unsigned short ws_ypixel; /* unused */
};
Tendrá que definir un tipo de datos Haskell para mantener esta información, y que sea una instancia de 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
}
Ahora, es necesario crear un tipo de datos ficticios para representar su solicitud:
data TIOCGWINSZ = TIOCGWINSZ
Finalmente, debe hacer que su solicitud escriba una instancia de IOControl
y asociarlo con el tipo de datos Winsize
.
instance IOControl TIOCGWINSZ Winsize where
ioctlReq _ = ??
Usted tendrá que reemplazar el ??
con la constante representada por TIOCGWINSZ
en sus archivos de cabecera (0x5413
en mi sistema).
Ahora, está listo para emitir el ioctl
. Este comando no se preocupa por los datos de entrada, por lo que desea utilizar el ioctl'
formulario:
main = do { ws <- ioctl' 1 TIOCGWINSZ
; putStrLn $ "My terminal is " ++ show (ws_col ws) ++ " columns wide"
}
Tenga en cuenta que el 1 se refiere a la salida estándar.
¡Uf!
¿No es esto un poco exagerado? ¿No hay nada más simple? –
Nota: El '0' en la llamada' ioctl'' se refiere a STDIN, por lo que esto falla si se redirige STDIN. Suponiendo que el objetivo para obtener el ancho del terminal es dar formato a la salida, puede ser mejor consultar STDOUT en su lugar. – hammar
Buen punto. He actualizado mi respuesta, pero tu respuesta es mucho más sólida. Desearía poder darte más de 1 voto favorable; ¡Su respuesta si está lleno de información útil! – pat
Ya que se necesita esto sólo en Unix, me gustaría recomendar:
resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""
Y luego hacer un poco de bits de análisis sintáctico de la salida. Puede que no sea 100% portátil, pero creo que puedes proporcionar resize
con argumentos (echa un vistazo a -u
en particular), por lo que obtendrás resultados bastante consistentes.
¡Es genial, necesito ver hsc2hs! – pat
Muy bien, gracias –