packages feed

hermit-0.6.0.0: src/HERMIT/Win32/Console.hsc

{-# LANGUAGE ForeignFunctionInterface #-}
-- | Taken from <https://github.com/batterseapower/ansi-terminal/blob/master/System/Console/ANSI/Windows/Foreign.hs> in the ansi-terminal package by batterseapower, licensed under BSD3
module HERMIT.Win32.Console
    (
      getConsoleWindowSize
    , isCygwinConsole
    ) where

import Control.Applicative
import Control.Exception (try)

import Foreign.C.Types
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable

import System.Environment
import System.Win32.Types

#include <windows.h>
#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)

type SHORT = CShort

isCygwinConsole :: IO Bool
isCygwinConsole = do
    result <- try $ getEnv "_" -- Cygwin defines this, Windows Cmd does not
    return $ case (result :: Either IOError String) of
                  Left _ -> False
                  Right _ -> True

getConsoleWindowSize :: IO (Maybe (Int, Int))
getConsoleWindowSize = do
    hStdout <- getStdHandle sTD_OUTPUT_HANDLE
    csbiInfo <- getConsoleScreenBufferInfo hStdout
    return $ fmap coords csbiInfo
        where coords :: CONSOLE_SCREEN_BUFFER_INFO -> (Int, Int)
              coords info = case csbi_maximum_window_size info of
                                 (COORD x y) -> (fromIntegral x, fromIntegral y)

sTD_OUTPUT_HANDLE :: DWORD
sTD_OUTPUT_HANDLE = #{const STD_OUTPUT_HANDLE}

foreign import ccall unsafe "windows.h GetStdHandle"
    getStdHandle :: DWORD -> IO HANDLE
foreign import ccall unsafe "windows.h GetConsoleScreenBufferInfo"
    cGetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO BOOL

getConsoleScreenBufferInfo :: HANDLE -> IO (Maybe CONSOLE_SCREEN_BUFFER_INFO)
getConsoleScreenBufferInfo handle = alloca $ \ptr_console_screen_buffer_info -> do
    ret <- cGetConsoleScreenBufferInfo handle ptr_console_screen_buffer_info
    if ret then peek ptr_console_screen_buffer_info >>= return . Just
           else return Nothing

data CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO {
        _csbi_size :: COORD,
        _csbi_cursor_position :: COORD,
        _csbi_attributes :: WORD,
        _csbi_window :: SMALL_RECT,
        csbi_maximum_window_size :: COORD
    }

instance Storable CONSOLE_SCREEN_BUFFER_INFO where
    sizeOf _ = #{size CONSOLE_SCREEN_BUFFER_INFO}
    alignment _ = #{alignment CONSOLE_SCREEN_BUFFER_INFO}
    peek ptr = CONSOLE_SCREEN_BUFFER_INFO                        <$>
        #{peek CONSOLE_SCREEN_BUFFER_INFO, dwSize} ptr           <*>
        #{peek CONSOLE_SCREEN_BUFFER_INFO, dwCursorPosition} ptr <*>
        #{peek CONSOLE_SCREEN_BUFFER_INFO, wAttributes} ptr      <*>
        #{peek CONSOLE_SCREEN_BUFFER_INFO, srWindow} ptr         <*>
        #{peek CONSOLE_SCREEN_BUFFER_INFO, dwMaximumWindowSize} ptr
    poke ptr (CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size) = do
        #{poke CONSOLE_SCREEN_BUFFER_INFO, dwSize} ptr size
        #{poke CONSOLE_SCREEN_BUFFER_INFO, dwCursorPosition} ptr cursor_position
        #{poke CONSOLE_SCREEN_BUFFER_INFO, wAttributes} ptr attributes
        #{poke CONSOLE_SCREEN_BUFFER_INFO, srWindow} ptr window
        #{poke CONSOLE_SCREEN_BUFFER_INFO, dwMaximumWindowSize} ptr maximum_window_size

data COORD = COORD SHORT SHORT

instance Storable COORD where
    sizeOf _ = #{size COORD}
    alignment _ = #{alignment COORD}
    peek ptr =
        COORD <$> #{peek COORD, X} ptr
              <*> #{peek COORD, Y} ptr
    poke ptr (COORD x y) = do
        #{poke COORD, X} ptr x
        #{poke COORD, Y} ptr y

data SMALL_RECT = SMALL_RECT SHORT SHORT SHORT SHORT

instance Storable SMALL_RECT where
    sizeOf _ = #{size SMALL_RECT}
    alignment _ = #{alignment SMALL_RECT}
    peek ptr =
        SMALL_RECT <$> #{peek SMALL_RECT, Left} ptr
                   <*> #{peek SMALL_RECT, Top} ptr
                   <*> #{peek SMALL_RECT, Right} ptr
                   <*> #{peek SMALL_RECT, Bottom} ptr
    poke ptr (SMALL_RECT l t r b) = do
        #{poke SMALL_RECT, Left} ptr l
        #{poke SMALL_RECT, Top} ptr t
        #{poke SMALL_RECT, Right} ptr r
        #{poke SMALL_RECT, Bottom} ptr b