2012-10-09 34 views

cevap

12

, burada 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) 

kabul Yanıta göre FFI kullanarak uygun ioctl() isteğin bir sarıcı, var Bu, sabit kodlama yapmak yerine C başlıklarına göre doğru sabitleri ve ofsetleri bulmak için hsc2hs preprocessor kullanır. Sanırım GHC ya da Haskell Platformu ile paketlenmiş, bu yüzden şimdiden şansınız olacak. Eğer Cabal kullanıyorsanız

, kendi .cabal dosyaya TermSize.hs ekleyebilir ve otomatik olarak TermSize.hsc onu üretmek için nasıl bileceksiniz. Aksi halde, daha sonra GHC ile derleyebildiğiniz bir .hs dosyası oluşturmak için hsc2hs TermSize.hsc dosyasını çalıştırabilirsiniz.Yalnızca Unix bu gerektiğinden

+0

Bu harika, hsc2hs'e bakmam lazım! – pat

+0

Çok güzel, Teşekkürler –

9

Sen hcurses kullanabilirsiniz. Kitaplığı başlattıktan sonra, ekranda satır ve sütun sayısını almak için scrSize'u kullanabilirsiniz. ,

struct winsize { 
    unsigned short ws_row; 
    unsigned short ws_col; 
    unsigned short ws_xpixel; /* unused */ 
    unsigned short ws_ypixel; /* unused */ 
}; 

Bu verileri saklayacak bir Haskell veri türünü tanımlamak gerekir: System.Posix.IOCtl kullanmak için

, aşağıdaki yapıda doldurur TIOCGWINSZ isteği temsil edecek bir veri türü tanımlamak zorunda

: isteğinizi temsil edecek bir kukla veri türü oluşturmak için gereken, Şimdi

{-# 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 
          } 

: ve ona Storable bir örneğini yapmak

data TIOCGWINSZ = TIOCGWINSZ 

Son olarak, isteğinizi bir IOControl örneğini oluşturmanız ve Winsize veri türüyle ilişkilendirmeniz gerekir.

instance IOControl TIOCGWINSZ Winsize where 
    ioctlReq _ = ?? 

Sen (sistemimde 0x5413) sizin başlık dosyalarında TIOCGWINSZ ile temsil sabiti ile ?? değiştirmeniz gerekir.

Şimdi, ioctl numarasını vermeye hazırsınız. Bu komut veri girişi umursamıyor, yani ioctl' formunu kullanmak istiyorum: 1 STDOUT başvurduğu

main = do { ws <- ioctl' 1 TIOCGWINSZ 
      ; putStrLn $ "My terminal is " ++ show (ws_col ws) ++ " columns wide" 
      } 

Not.

Phew! Eğer ncurses'da bir bağımlılık istemiyorsanız

+1

Bu biraz aşırı değil mi? Daha basit bir şey yok mu? –

+0

Not: 'ioctl'' çağrısındaki' '' STDIN’i ifade eder, bu nedenle STDIN yeniden yönlendirilirse bu başarısız olur. Terminal genişliğini elde etmek için hedefin çıktıyı biçimlendirmek olduğunu varsayarak, bunun yerine STDOUT'u sorgulamak daha iyi olabilir. – hammar

+0

İyi nokta. Cevabımı güncelledim, ama cevabınız çok daha sağlam. Keşke size 1 oydan fazlasını verebilseydim; Cevabınız yararlı bilgilerle dolu olduğunda – pat

3

, ben tavsiye ederim:

resizeOutput <- readProcess "/usr/X11/bin/resize" [] ""

Sonra çıktı ayrıştırma biraz bit yapıyor. Bu,% 100 taşınabilir olmayabilir, ancak ben resize argümanları ile sağlayabileceğine inanıyorum (özellikle -u'a bakın), böylece oldukça tutarlı bir çıktı elde edersiniz.