Получить Терминал шириной Haskell

Как узнать ширину терминала в Хаскеле?

Вещи, которые я пытался

System.Posix.IOCtl (could not figure out how to get it to work) 

Это должно работать только Unix.

Спасибо

Ответы на вопрос(3)

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.

Уф!

 09 окт. 2012 г., 21:48
Разве это не слишком излишне? Нет ничего проще?
 09 окт. 2012 г., 23:36
Хорошая точка зрения. Я обновил свой ответ, но ваш ответ гораздо надежнее. Хотел бы я дать вам более одного голоса; Ваш ответ переполнен полезной информацией!
 09 окт. 2012 г., 23:28
Обратите внимание0 вioctl' Вызов относится к STDIN, поэтому происходит сбой, если STDIN перенаправлен. Предполагая, что целью получения ширины терминала является форматирование вывода, может быть лучше вместо этого запросить STDOUT.

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

А потом немного разбираем вывод. Это не может быть на 100% портативным, но я верю, что вы можете предоставитьresize с аргументами (проверить-u в частности), так что вы получите довольно последовательный вывод.

Решение Вопроса

ioctl() запрос с использованием FFI, основанный на принятом ответеПолучение ширины терминала в 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 выяснить правильные константы и смещения на основе заголовков Си, а не жестко их кодировать. Я думаю, что он поставляется либо с GHC, либо с платформой Haskell, так что, скорее всего, он у вас уже есть.

Если вы используете Cabal, вы можете добавитьTermSize.hs на ваш.cabal файл, и он автоматически узнает, как его сгенерироватьTermSize.hsc, В противном случае вы можете запуститьhsc2hs TermSize.hsc вручную, чтобы создать.hs файл, который вы можете затем скомпилировать с GHC.

 09 окт. 2012 г., 22:51
Это круто, мне нужно посмотреть на hsc2hs!
 Bilal Syed Hussain10 окт. 2012 г., 00:05
Очень мило спасибо

Ваш ответ на вопрос