Làm thế nào để có được chiều rộng của thiết bị đầu cuối trong Haskell?Lấy chiều rộng đầu cuối Haskell
Những điều tôi đã cố gắng
System.Posix.IOCtl (could not figure out how to get it to work)
này chỉ phải làm việc unix.
Cảm ơn
Làm thế nào để có được chiều rộng của thiết bị đầu cuối trong Haskell?Lấy chiều rộng đầu cuối Haskell
Những điều tôi đã cố gắng
System.Posix.IOCtl (could not figure out how to get it to work)
này chỉ phải làm việc unix.
Cảm ơn
Nếu bạn không muốn có một sự phụ thuộc vào kernel, đây là một wrapper của ioctl()
yêu cầu thích hợp bằng cách sử dụng FFI, dựa trên câu trả lời chấp nhận của 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)
Điều này sử dụng các hsc2hs
preprocessor để tìm ra các hằng số chính xác và bù đắp dựa trên các tiêu đề C hơn là mã hóa chúng. Tôi nghĩ rằng nó được đóng gói với GHC hoặc Haskell Platform, vì vậy rất có thể bạn sẽ có nó.
Nếu bạn đang sử dụng Cabal, bạn có thể thêm TermSize.hs
vào tệp .cabal
của mình và nó sẽ tự động biết cách tạo từ TermSize.hsc
. Nếu không, bạn có thể chạy hsc2hs TermSize.hsc
theo cách thủ công để tạo tệp .hs
mà sau đó bạn có thể biên dịch với GHC.
Bạn có thể sử dụng hcurses. Khi bạn đã khởi tạo thư viện, bạn có thể sử dụng scrSize
để nhận số hàng và cột trên màn hình.
Để sử dụng System.Posix.IOCtl
, bạn phải xác định một kiểu dữ liệu để đại diện cho TIOCGWINSZ
yêu cầu, trong đó điền vào cấu trúc sau:
struct winsize {
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel; /* unused */
unsigned short ws_ypixel; /* unused */
};
Bạn sẽ cần phải định nghĩa một kiểu dữ liệu Haskell để giữ thông tin này, và làm cho nó một thể hiện của 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
}
Bây giờ, bạn cần phải tạo ra một kiểu dữ liệu giả để đại diện cho yêu cầu của bạn:
data TIOCGWINSZ = TIOCGWINSZ
Cuối cùng, bạn cần yêu cầu nhập một phiên bản IOControl
và liên kết nó với loại dữ liệu Winsize
.
instance IOControl TIOCGWINSZ Winsize where
ioctlReq _ = ??
Bạn sẽ cần phải thay thế ??
với hằng số đại diện bởi TIOCGWINSZ
trong các tập tin tiêu đề của bạn (0x5413
trên hệ thống của tôi).
Bây giờ, bạn đã sẵn sàng phát hành ioctl
. Lệnh này không quan tâm đến dữ liệu đầu vào, do đó bạn muốn sử dụng biểu mẫu ioctl'
:
main = do { ws <- ioctl' 1 TIOCGWINSZ
; putStrLn $ "My terminal is " ++ show (ws_col ws) ++ " columns wide"
}
Lưu ý rằng 1 đề cập đến STDOUT.
Phew!
Đây có phải là quá mức cần thiết không? Không có gì đơn giản hơn? –
Lưu ý: '0' trong cuộc gọi' ioctl'' đề cập đến STDIN, vì vậy điều này không thành công nếu STDIN được chuyển hướng. Giả sử mục tiêu để có được chiều rộng đầu cuối là định dạng đầu ra, có thể tốt hơn để truy vấn STDOUT thay thế. – hammar
Điểm tốt. Tôi đã cập nhật câu trả lời của tôi, nhưng câu trả lời của bạn là mạnh mẽ hơn nhiều. Tôi ước tôi có thể cho bạn hơn 1 phiếu bầu; câu trả lời của bạn nếu chứa đầy những thông tin hữu ích! – pat
Vì bạn cần điều này chỉ trên Unix, tôi khuyên bạn nên:
resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""
Và sau đó làm một chút-bit của phân tích của đầu ra. Điều này có thể không được 100% di động, nhưng tôi tin rằng bạn có thể cung cấp resize
với các đối số (hãy kiểm tra -u
nói riêng), vì vậy bạn sẽ nhận được kết quả khá nhất quán.
Thật tuyệt, tôi cần phải xem hsc2hs! – pat
Rất đẹp, Cảm ơn –