2012-10-09 9 views

उत्तर

12

आप ncurses पर निर्भरता नहीं करना चाहते हैं, यहाँ उचित ioctl() FFI का उपयोग कर अनुरोध का एक आवरण, 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) 

के स्वीकार किए जाते हैं उत्तर के आधार पर है यह hsc2hs preprocessor का उपयोग करता है ताकि सी हार्डर्सिंग के बजाय सी हेडर पर आधारित सही स्थिरांक और ऑफसेट का पता लगाया जा सके। मुझे लगता है कि यह या तो जीएचसी या हास्केल प्लेटफ़ॉर्म के साथ पैक किया गया है, इसलिए संभावना है कि आपके पास पहले से ही होगा।

यदि आप कैबल का उपयोग कर रहे हैं, तो आप TermSize.hs को अपने .cabal फ़ाइल में जोड़ सकते हैं और यह स्वचालित रूप से TermSize.hsc से इसे उत्पन्न करने के तरीके के बारे में जानेंगे। अन्यथा, आप hsc2hs TermSize.hsc को .hs फ़ाइल जेनरेट करने के लिए मैन्युअल रूप से चला सकते हैं जिसे आप जीएचसी के साथ संकलित कर सकते हैं।

+0

यह अच्छा है, मुझे hsc2hs को देखने की ज़रूरत है! – pat

+0

बहुत अच्छा, धन्यवाद –

9

आप 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 */ 
}; 

आप इस जानकारी धारण करने के लिए एक हास्केल डेटा के प्रकार को परिभाषित करने की आवश्यकता होगी,

:

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

आप अपने अनुरोध का प्रतिनिधित्व करने के एक डमी डेटा प्रकार बनाने की जरूरत अब,: और यह Storable का एक उदाहरण बनाने के

data TIOCGWINSZ = TIOCGWINSZ 

अंत में, आपको अपना अनुरोध IOControl का उदाहरण बनाने की आवश्यकता है, और इसे Winsize डेटा प्रकार से संबद्ध करें।

instance IOControl TIOCGWINSZ Winsize where 
    ioctlReq _ = ?? 

आप निरंतर (अपने सिस्टम पर 0x5413) अपने हेडर फाइल में TIOCGWINSZ द्वारा प्रतिनिधित्व के साथ ?? को बदलने के लिए की आवश्यकता होगी।

अब, आप ioctl जारी करने के लिए तैयार हैं। यह आदेश इनपुट डेटा के बारे में परवाह नहीं करता है, तो आप ioctl' फार्म का उपयोग करना चाहते हैं:

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

ध्यान दें कि 1 STDOUT को दर्शाता है।

पुhew!

+1

क्या यह थोड़ा अधिक नहीं है? क्या कुछ आसान नहीं है? –

+0

नोट: 'ioctl' कॉल में '0' एसटीडीआईएन को संदर्भित करता है, इसलिए अगर एसटीडीआईएन रीडायरेक्ट किया जाता है तो यह विफल हो जाता है। टर्मिनल चौड़ाई प्राप्त करने के लक्ष्य को मानना ​​प्रारूप को प्रारूपित करना है, इसके बजाय STDOUT से पूछना बेहतर हो सकता है। – hammar

+0

अच्छा बिंदु। मैंने अपना जवाब अपडेट कर लिया है, लेकिन आपका जवाब कहीं अधिक मजबूत है। काश मैं आपको 1 से अधिक वोट दे सकता हूं; उपयोगी जानकारी से भरा हुआ अगर आपका जवाब! – pat

3

जब से तुम केवल यूनिक्स पर इस की जरूरत है, मैं सलाह देते हैं:

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

और फिर उत्पादन को पार्स का एक छोटा-बिट कर रही। यह 100% पोर्टेबल नहीं हो सकता है, लेकिन मुझे विश्वास है कि आप तर्क के साथ resize प्रदान कर सकते हैं (विशेष रूप से -u देखें), इसलिए आपको काफी लगातार आउटपुट मिलेगा।

संबंधित मुद्दे