@@ -8,20 +8,25 @@ module System.Console.Haskeline.Backend.Win32(
8
8
9
9
10
10
import System.IO
11
+ import System.IO.Unsafe (unsafePerformIO )
11
12
import Foreign
12
13
import Foreign.C
13
14
#if MIN_VERSION_Win32(2,9,0)
14
- import System.Win32 hiding (multiByteToWideChar , setConsoleMode , getConsoleMode )
15
+ import System.Win32 hiding (getConsoleMode , isMinTTY , multiByteToWideChar ,
16
+ setConsoleMode , try )
17
+ #elif MIN_VERSION_Win32(2,5,0)
18
+ import System.Win32 hiding (isMintty , multiByteToWideChar , try )
15
19
#else
16
- import System.Win32 hiding (multiByteToWideChar )
20
+ import System.Win32 hiding (multiByteToWideChar , try )
21
+ import System.Console.Mintty (isMinTTYHandle )
17
22
#endif
18
23
import Graphics.Win32.Misc (getStdHandle , sTD_OUTPUT_HANDLE )
19
24
import Data.List (intercalate )
20
25
import Control.Concurrent.STM
21
26
import Control.Concurrent hiding (throwTo )
22
27
import Data.Char (isPrint , chr , ord )
23
28
import Data.Maybe (mapMaybe )
24
- import Control.Exception (IOException , throwTo )
29
+ import Control.Exception (IOException , SomeException , throwTo , try )
25
30
import Control.Monad
26
31
import Control.Monad.Catch
27
32
( MonadThrow
@@ -261,16 +266,26 @@ foreign import WINDOWS_CCONV "windows.h GetConsoleMode" c_GetConsoleMode
261
266
foreign import WINDOWS_CCONV " windows.h SetConsoleMode" c_SetConsoleMode
262
267
:: HANDLE -> DWORD -> IO Bool
263
268
269
+ #if !MIN_VERSION_Win32(2,8,5)
270
+ eNABLE_VIRTUAL_TERMINAL_PROCESSING :: DWORD
271
+ eNABLE_VIRTUAL_TERMINAL_PROCESSING = 4
272
+ #endif
273
+
264
274
withWindowMode :: (MonadIO m , MonadMask m ) => Handles -> m a -> m a
265
275
withWindowMode hs f = do
266
276
let h = hIn hs
267
277
bracket (getConsoleMode h) (setConsoleMode h)
268
278
$ \ m -> setConsoleMode h (m .|. (# const ENABLE_WINDOW_INPUT )) >> f
269
- where
270
- getConsoleMode h = liftIO $ alloca $ \ p -> do
271
- failIfFalse_ " GetConsoleMode" $ c_GetConsoleMode h p
272
- peek p
273
- setConsoleMode h m = liftIO $ failIfFalse_ " SetConsoleMode" $ c_SetConsoleMode h m
279
+
280
+ getConsoleMode :: (MonadIO m ) => HANDLE -> m DWORD
281
+ getConsoleMode h = liftIO $ alloca $ \ p -> do
282
+ failIfFalse_ " GetConsoleMode" $ c_GetConsoleMode h p
283
+ peek p
284
+
285
+ setConsoleMode :: (MonadIO m ) => HANDLE -> DWORD -> m ()
286
+ setConsoleMode h m =
287
+ liftIO $ failIfFalse_ " SetConsoleMode" $ c_SetConsoleMode h m
288
+
274
289
275
290
----------------------------
276
291
-- Drawing
@@ -369,9 +384,11 @@ crlf :: String
369
384
crlf = " \r\n "
370
385
371
386
instance (MonadMask m , MonadIO m , MonadReader Layout m ) => Term (Draw m ) where
372
- drawLineDiff (xs1,ys1) (xs2,ys2) = let
373
- fixEsc = filter ((/= ' \ESC ' ) . baseChar)
374
- in drawLineDiffWin (fixEsc xs1, fixEsc ys1) (fixEsc xs2, fixEsc ys2)
387
+ drawLineDiff (xs1,ys1) (xs2,ys2) = if aNSISupport
388
+ then drawLineDiffWin (xs1,ys1) (xs2,ys2)
389
+ else
390
+ let fixEsc = filter ((/= ' \ESC ' ) . baseChar)
391
+ in drawLineDiffWin (fixEsc xs1, fixEsc ys1) (fixEsc xs2, fixEsc ys2)
375
392
-- TODO now that we capture resize events.
376
393
-- first, looks like the cursor stays on the same line but jumps
377
394
-- to the beginning if cut off.
@@ -567,3 +584,35 @@ clearScreen = do
567
584
liftIO $ fillConsoleChar h ' ' windowSize origin
568
585
liftIO $ fillConsoleAttribute h attr windowSize origin
569
586
setPos origin
587
+
588
+ -- | This function assumes that once it is first established whether or not the
589
+ -- Windows console is ANSI-capable, that will not change.
590
+ {-# NOINLINE aNSISupport #-}
591
+ aNSISupport :: Bool
592
+ aNSISupport = unsafePerformIO $ withHandleToHANDLE stdout $ withHANDLE
593
+ (return False ) -- Invalid handle or no handle
594
+ $ \ h -> do
595
+ tryMode <- try (getConsoleMode h) :: IO (Either SomeException DWORD )
596
+ case tryMode of
597
+ Left _ -> do -- No ConHost mode
598
+ isMinTTY <- isMinTTYHandle h
599
+ if isMinTTY
600
+ then return True -- 'mintty' terminal emulator
601
+ else return False -- Not sure!
602
+ Right mode -> if mode .&. eNABLE_VIRTUAL_TERMINAL_PROCESSING /= 0
603
+ then return True -- VT processing already enabled
604
+ else do
605
+ let mode' = mode .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING
606
+ trySetMode <- try (setConsoleMode h mode')
607
+ :: IO (Either SomeException () )
608
+ case trySetMode of
609
+ Left _ -> return False -- Can't enable VT processing
610
+ Right () -> return True -- VT processing enabled
611
+ where
612
+ -- | This function applies another to the Windows handle, if the handle is
613
+ -- valid. If it is invalid, the specified default action is returned.
614
+ withHANDLE :: IO a -> (HANDLE -> IO a ) -> HANDLE -> IO a
615
+ withHANDLE invalid action h =
616
+ if h == iNVALID_HANDLE_VALUE || h == nullHANDLE
617
+ then invalid -- Invalid handle or no handle
618
+ else action h
0 commit comments