Как получить ширину терминала в 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 на основе принятого ответа Getting ширина терминала в 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)
При этом используется 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.
Фу!
0
в вызове ioctl'
относится к STDIN, поэтому это не удается, если STDIN перенаправляется. Предполагая, что целью получения ширины терминала является форматирование вывода, вместо этого может быть лучше запросить STDOUT.
- person hammar; 10.10.2012
Поскольку вам это нужно только в Unix, я бы рекомендовал:
resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""
А затем сделать небольшой разбор вывода. Это может быть не на 100% переносимым, но я верю, что вы можете предоставить resize
аргументы (в частности, проверьте -u
), так что вы получите довольно последовательный вывод.
/usr/X11/bin/resize
даже не существует в арке ^^
- person Xerus; 20.10.2020