From ea725456ee1463cbc7278a81f0ea43046c7b3cea Mon Sep 17 00:00:00 2001 From: Neil Mitchell Date: Fri, 7 Nov 2014 14:50:17 +0000 Subject: [PATCH] #3, make it work on Windows --- src/System/Console/Terminal/Size.hs | 42 ++++++++++++++++---------- src/System/Console/Terminal/Windows.hs | 41 +++++++++++++++++++++++++ terminal-size.cabal | 11 ++++--- 3 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 src/System/Console/Terminal/Windows.hs diff --git a/src/System/Console/Terminal/Size.hs b/src/System/Console/Terminal/Size.hs index df40349..aa7173a 100644 --- a/src/System/Console/Terminal/Size.hs +++ b/src/System/Console/Terminal/Size.hs @@ -1,21 +1,37 @@ +{-# LANGUAGE CPP #-} {- | Get terminal window height and width without ncurses dependency -Only tested to work on GNU/Linux systems - Based on answer by Andreas Hammar at -} module System.Console.Terminal.Size - ( Window(..), size, fdSize, hSize + ( Window(..), size +#if !defined(mingw32_HOST_OS) + ,fdSize, hSize +#endif ) where import System.Console.Terminal.Common -import qualified System.Console.Terminal.Posix as Posix +#if defined(mingw32_HOST_OS) +import qualified System.Console.Terminal.Windows as Host +#else +import qualified System.Console.Terminal.Posix as Host import System.Posix.Types(Fd) import System.IO(Handle) +#endif + +-- | Get terminal window width and height for @stdout@. +-- +-- >>> import System.Console.Terminal.Size +-- >>> size +-- Just (Window {height = 60, width = 112}) +size :: Integral n => IO (Maybe (Window n)) +size = Host.size --- | Get terminal window width and height for a specified file descriptor. If +#if !defined(mingw32_HOST_OS) +-- | /Not available on Windows:/ +-- Get terminal window width and height for a specified file descriptor. If -- it's not attached to a terminal then 'Nothing' is returned. -- -- >>> import System.Console.Terminal.Size @@ -26,21 +42,15 @@ import System.IO(Handle) -- >>> fdSize fd -- Nothing fdSize :: Integral n => Fd -> IO (Maybe (Window n)) -fdSize = Posix.fdSize - --- | Get terminal window width and height for @stdout@. --- --- >>> import System.Console.Terminal.Size --- >>> size --- Just (Window {height = 60, width = 112}) -size :: Integral n => IO (Maybe (Window n)) -size = Posix.size +fdSize = Host.fdSize --- | Same as 'fdSize', but takes 'Handle' instead of 'Fd' (file descriptor). +-- | /Not available on Windows:/ +-- Same as 'fdSize', but takes 'Handle' instead of 'Fd' (file descriptor). -- -- >>> import System.Console.Terminal.Size -- >>> import System.IO -- >>> hSize stdout -- Just (Window {height = 56, width = 85}) hSize :: Integral n => Handle -> IO (Maybe (Window n)) -hSize = Posix.hSize +hSize = Host.hSize +#endif diff --git a/src/System/Console/Terminal/Windows.hs b/src/System/Console/Terminal/Windows.hs new file mode 100644 index 0000000..595cc8b --- /dev/null +++ b/src/System/Console/Terminal/Windows.hs @@ -0,0 +1,41 @@ + +module System.Console.Terminal.Windows(size) where + +import System.Console.Terminal.Common + +import Control.Monad +import Data.Word +import Foreign.Ptr +import Foreign.Storable +import Foreign.Marshal.Alloc + +type HANDLE = Ptr () + +data CONSOLE_SCREEN_BUFFER_INFO + +sizeCONSOLE_SCREEN_BUFFER_INFO :: Int +sizeCONSOLE_SCREEN_BUFFER_INFO = 22 + +posCONSOLE_SCREEN_BUFFER_INFO_srWindow :: Int +posCONSOLE_SCREEN_BUFFER_INFO_srWindow = 10 -- 4 x Word16 Left,Top,Right,Bottom + +c_STD_OUTPUT_HANDLE :: Word32 +c_STD_OUTPUT_HANDLE = -11 + +foreign import stdcall unsafe "windows.h GetConsoleScreenBufferInfo" + c_GetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO Bool + +foreign import stdcall unsafe "windows.h GetStdHandle" + c_GetStdHandle :: Word32 -> IO HANDLE + + +size :: Integral n => IO (Maybe (Window n)) +size = do + hdl <- c_GetStdHandle c_STD_OUTPUT_HANDLE + allocaBytes sizeCONSOLE_SCREEN_BUFFER_INFO $ \p -> do + b <- c_GetConsoleScreenBufferInfo hdl p + if not b then return Nothing else do + [left,top,right,bottom] <- forM [0..3] $ \i -> do + v <- peekByteOff p ((i*2) + posCONSOLE_SCREEN_BUFFER_INFO_srWindow) + return $ fromIntegral (v :: Word16) + return $ Just $ Window (1+bottom-top) (1+right-left) diff --git a/terminal-size.cabal b/terminal-size.cabal index fc06b28..a44b17a 100644 --- a/terminal-size.cabal +++ b/terminal-size.cabal @@ -2,9 +2,7 @@ name: terminal-size version: 0.2.1.0 synopsis: Get terminal window height and width description: - Get terminal window height and width without ncurses dependency - . - Only tested to work on GNU/Linux systems + Get terminal window height and width without ncurses dependency. license: BSD3 license-file: LICENSE author: Andreas Hammar, Matvey Aksenov @@ -19,8 +17,11 @@ library build-tools: hsc2hs hs-source-dirs: src exposed-modules: System.Console.Terminal.Size - other-modules: System.Console.Terminal.Posix - System.Console.Terminal.Common + other-modules: System.Console.Terminal.Common + if os(Windows) + other-modules: System.Console.Terminal.Windows + else + other-modules: System.Console.Terminal.Posix ghc-options: -Wall -fno-warn-unused-do-bind