2012-10-09 25 views

Trả lời

12

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.

+0

Thật tuyệt, tôi cần phải xem hsc2hs! – pat

+0

Rất đẹp, Cảm ơn –

9

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!

+1

Đây có phải là quá mức cần thiết không? Không có gì đơn giản hơn? –

+0

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

+0

Đ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

3

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.

Các vấn đề liên quan