|
1 |
| - |
2 |
| -module System.Console.Terminal.Windows(size) where |
| 1 | +module System.Console.Terminal.Windows(size, hSize) where |
3 | 2 |
|
4 | 3 | import System.Console.Terminal.Common
|
5 | 4 |
|
6 |
| -import Control.Monad |
7 |
| -import Data.Word |
8 |
| -import Foreign.Ptr |
9 |
| -import Foreign.Storable |
10 |
| -import Foreign.Marshal.Alloc |
11 | 5 | import System.Exit
|
12 | 6 | import System.IO
|
| 7 | +import System.IO.Error (catchIOError) |
13 | 8 | import System.Process
|
14 |
| - |
15 |
| -type HANDLE = Ptr () |
16 |
| - |
17 |
| -data CONSOLE_SCREEN_BUFFER_INFO |
18 |
| - |
19 |
| -sizeCONSOLE_SCREEN_BUFFER_INFO :: Int |
20 |
| -sizeCONSOLE_SCREEN_BUFFER_INFO = 22 |
21 |
| - |
22 |
| -posCONSOLE_SCREEN_BUFFER_INFO_srWindow :: Int |
23 |
| -posCONSOLE_SCREEN_BUFFER_INFO_srWindow = 10 -- 4 x Word16 Left,Top,Right,Bottom |
24 |
| - |
25 |
| -c_STD_OUTPUT_HANDLE :: Word32 |
26 |
| -c_STD_OUTPUT_HANDLE = -11 |
27 |
| - |
28 |
| -foreign import stdcall unsafe "windows.h GetConsoleScreenBufferInfo" |
29 |
| - c_GetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO Bool |
30 |
| - |
31 |
| -foreign import stdcall unsafe "windows.h GetStdHandle" |
32 |
| - c_GetStdHandle :: Word32 -> IO HANDLE |
33 |
| - |
| 9 | +import System.Win32.Console |
| 10 | + ( CONSOLE_SCREEN_BUFFER_INFO(srWindow) |
| 11 | + , SMALL_RECT(..) |
| 12 | + , getConsoleScreenBufferInfo |
| 13 | + ) |
| 14 | +import System.Win32.Types (HANDLE, withHandleToHANDLE) |
34 | 15 |
|
35 | 16 | size :: Integral n => IO (Maybe (Window n))
|
36 |
| -size = do |
37 |
| - hdl <- c_GetStdHandle c_STD_OUTPUT_HANDLE |
38 |
| - allocaBytes sizeCONSOLE_SCREEN_BUFFER_INFO $ \p -> do |
39 |
| - b <- c_GetConsoleScreenBufferInfo hdl p |
40 |
| - if not b |
41 |
| - then do -- This could happen on Cygwin or MSYS |
42 |
| - let stty = (shell "stty size") { |
43 |
| - std_in = UseHandle stdin |
44 |
| - , std_out = CreatePipe |
45 |
| - , std_err = CreatePipe |
46 |
| - } |
47 |
| - (_, mbStdout, _, rStty) <- createProcess stty |
48 |
| - exStty <- waitForProcess rStty |
49 |
| - case exStty of |
50 |
| - ExitFailure _ -> return Nothing |
51 |
| - ExitSuccess -> |
52 |
| - maybe (return Nothing) |
53 |
| - (\hSize -> do |
54 |
| - sizeStr <- hGetContents hSize |
55 |
| - let [r, c] = map read $ words sizeStr :: [Int] |
56 |
| - return $ Just $ Window (fromIntegral r) (fromIntegral c) |
57 |
| - ) |
58 |
| - mbStdout |
59 |
| - else do |
60 |
| - [left,top,right,bottom] <- forM [0..3] $ \i -> do |
61 |
| - v <- peekByteOff p ((i*2) + posCONSOLE_SCREEN_BUFFER_INFO_srWindow) |
62 |
| - return $ fromIntegral (v :: Word16) |
63 |
| - return $ Just $ Window (1+bottom-top) (1+right-left) |
| 17 | +size = hSize stdout |
| 18 | + |
| 19 | +hSize :: Integral n => Handle -> IO (Maybe (Window n)) |
| 20 | +hSize hdl = |
| 21 | + withHandleToHANDLE hdl nativeSize |
| 22 | + `catchIOError` \_ -> do |
| 23 | + -- This could happen on Cygwin or MSYS |
| 24 | + let stty = (shell "stty size") { |
| 25 | + std_in = UseHandle hdl |
| 26 | + , std_out = CreatePipe |
| 27 | + , std_err = CreatePipe |
| 28 | + } |
| 29 | + (_, mbStdout, _, rStty) <- createProcess stty |
| 30 | + exStty <- waitForProcess rStty |
| 31 | + case exStty of |
| 32 | + ExitFailure _ -> return Nothing |
| 33 | + ExitSuccess -> |
| 34 | + maybe (return Nothing) |
| 35 | + (\out -> do |
| 36 | + sizeStr <- hGetContents out |
| 37 | + let [r, c] = map read $ words sizeStr :: [Int] |
| 38 | + return $ Just $ Window (fromIntegral r) (fromIntegral c) |
| 39 | + ) |
| 40 | + mbStdout |
| 41 | + |
| 42 | +nativeSize :: Integral n => HANDLE -> IO (Maybe (Window n)) |
| 43 | +nativeSize hdl = do |
| 44 | + rect <- srWindow <$> getConsoleScreenBufferInfo hdl |
| 45 | + return $ Just $ Window |
| 46 | + { height = fromIntegral (1 + bottomPos rect - topPos rect) |
| 47 | + , width = fromIntegral (1 + rightPos rect - leftPos rect) |
| 48 | + } |
0 commit comments