{-# LANGUAGE CPP #-}
module System.Console.Haskeline.Backend.Win32.Echo (hWithoutInputEcho) where
import Control.Exception (throw)
import Control.Monad (void)
import Control.Monad.Catch (MonadMask, bracket)
import Control.Monad.IO.Class (MonadIO(..))
import System.Exit (ExitCode(..))
import System.IO (Handle, hGetContents, hGetEcho, hSetEcho)
import System.Process (StdStream(..), createProcess, shell,
std_in, std_out, waitForProcess)
#if MIN_VERSION_Win32(2,5,0)
import Control.Concurrent.MVar (readMVar)
import Data.Typeable (cast)
import Foreign.C.Types
import Foreign.StablePtr (StablePtr, freeStablePtr, newStablePtr)
import GHC.IO.FD (FD(..))
#if defined(__IO_MANAGER_WINIO__)
import GHC.IO.Handle.Windows (handleToHANDLE)
import GHC.IO.SubSystem ((<!>))
#endif
import GHC.IO.Handle.Types (Handle(..), Handle__(..))
import System.Win32.Types (HANDLE)
import System.Win32.MinTTY (isMinTTYHandle)
#endif
hGetInputEchoState :: Handle -> IO EchoState
hGetInputEchoState :: Handle -> IO EchoState
hGetInputEchoState Handle
input = do
min_tty <- Handle -> IO Bool
minTTY Handle
input
if min_tty
then fmap MinTTY (hGetInputEchoSTTY input)
else fmap DefaultTTY $ hGetEcho input
hGetInputEchoSTTY :: Handle -> IO STTYSettings
hGetInputEchoSTTY :: Handle -> IO STTYSettings
hGetInputEchoSTTY Handle
input = Handle -> STTYSettings -> IO STTYSettings
hSttyRaw Handle
input STTYSettings
"-g"
hSetInputEchoState :: Handle -> EchoState -> IO ()
hSetInputEchoState :: Handle -> EchoState -> IO ()
hSetInputEchoState Handle
input (MinTTY STTYSettings
settings) = Handle -> STTYSettings -> IO ()
hSetInputEchoSTTY Handle
input STTYSettings
settings
hSetInputEchoState Handle
input (DefaultTTY Bool
echo) = Handle -> Bool -> IO ()
hSetEcho Handle
input Bool
echo
hSetInputEchoSTTY :: Handle -> STTYSettings -> IO ()
hSetInputEchoSTTY :: Handle -> STTYSettings -> IO ()
hSetInputEchoSTTY Handle
input = IO STTYSettings -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO STTYSettings -> IO ())
-> (STTYSettings -> IO STTYSettings) -> STTYSettings -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> STTYSettings -> IO STTYSettings
hSttyRaw Handle
input
hBracketInputEcho :: (MonadIO m, MonadMask m) => Handle -> m a -> m a
hBracketInputEcho :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
Handle -> m a -> m a
hBracketInputEcho Handle
input m a
action =
m EchoState -> (EchoState -> m ()) -> (EchoState -> m a) -> m a
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket (IO EchoState -> m EchoState
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO EchoState -> m EchoState) -> IO EchoState -> m EchoState
forall a b. (a -> b) -> a -> b
$ Handle -> IO EchoState
hGetInputEchoState Handle
input)
(IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> (EchoState -> IO ()) -> EchoState -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> EchoState -> IO ()
hSetInputEchoState Handle
input)
(m a -> EchoState -> m a
forall a b. a -> b -> a
const m a
action)
hWithoutInputEcho :: (MonadIO m, MonadMask m) => Handle -> m a -> m a
hWithoutInputEcho :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
Handle -> m a -> m a
hWithoutInputEcho Handle
input m a
action = do
echo_off <- IO EchoState -> m EchoState
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO EchoState -> m EchoState) -> IO EchoState -> m EchoState
forall a b. (a -> b) -> a -> b
$ Handle -> IO EchoState
hEchoOff Handle
input
hBracketInputEcho input
(liftIO (hSetInputEchoState input echo_off) >> action)
hSttyRaw :: Handle -> String -> IO STTYSettings
hSttyRaw :: Handle -> STTYSettings -> IO STTYSettings
hSttyRaw Handle
input STTYSettings
arg = do
let stty :: CreateProcess
stty = (STTYSettings -> CreateProcess
shell (STTYSettings -> CreateProcess) -> STTYSettings -> CreateProcess
forall a b. (a -> b) -> a -> b
$ STTYSettings
"stty " STTYSettings -> STTYSettings -> STTYSettings
forall a. [a] -> [a] -> [a]
++ STTYSettings
arg) {
std_in = UseHandle input
, std_out = CreatePipe
}
(_, mbStdout, _, rStty) <- CreateProcess
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
createProcess CreateProcess
stty
exStty <- waitForProcess rStty
case exStty of
e :: ExitCode
e@ExitFailure{} -> ExitCode -> IO STTYSettings
forall a e. (HasCallStack, Exception e) => e -> a
throw ExitCode
e
ExitCode
ExitSuccess -> IO STTYSettings
-> (Handle -> IO STTYSettings) -> Maybe Handle -> IO STTYSettings
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (STTYSettings -> IO STTYSettings
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return STTYSettings
"") Handle -> IO STTYSettings
hGetContents Maybe Handle
mbStdout
data EchoState
= MinTTY STTYSettings
| DefaultTTY Bool
deriving (EchoState -> EchoState -> Bool
(EchoState -> EchoState -> Bool)
-> (EchoState -> EchoState -> Bool) -> Eq EchoState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: EchoState -> EchoState -> Bool
== :: EchoState -> EchoState -> Bool
$c/= :: EchoState -> EchoState -> Bool
/= :: EchoState -> EchoState -> Bool
Eq, Eq EchoState
Eq EchoState =>
(EchoState -> EchoState -> Ordering)
-> (EchoState -> EchoState -> Bool)
-> (EchoState -> EchoState -> Bool)
-> (EchoState -> EchoState -> Bool)
-> (EchoState -> EchoState -> Bool)
-> (EchoState -> EchoState -> EchoState)
-> (EchoState -> EchoState -> EchoState)
-> Ord EchoState
EchoState -> EchoState -> Bool
EchoState -> EchoState -> Ordering
EchoState -> EchoState -> EchoState
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: EchoState -> EchoState -> Ordering
compare :: EchoState -> EchoState -> Ordering
$c< :: EchoState -> EchoState -> Bool
< :: EchoState -> EchoState -> Bool
$c<= :: EchoState -> EchoState -> Bool
<= :: EchoState -> EchoState -> Bool
$c> :: EchoState -> EchoState -> Bool
> :: EchoState -> EchoState -> Bool
$c>= :: EchoState -> EchoState -> Bool
>= :: EchoState -> EchoState -> Bool
$cmax :: EchoState -> EchoState -> EchoState
max :: EchoState -> EchoState -> EchoState
$cmin :: EchoState -> EchoState -> EchoState
min :: EchoState -> EchoState -> EchoState
Ord, Int -> EchoState -> STTYSettings -> STTYSettings
[EchoState] -> STTYSettings -> STTYSettings
EchoState -> STTYSettings
(Int -> EchoState -> STTYSettings -> STTYSettings)
-> (EchoState -> STTYSettings)
-> ([EchoState] -> STTYSettings -> STTYSettings)
-> Show EchoState
forall a.
(Int -> a -> STTYSettings -> STTYSettings)
-> (a -> STTYSettings)
-> ([a] -> STTYSettings -> STTYSettings)
-> Show a
$cshowsPrec :: Int -> EchoState -> STTYSettings -> STTYSettings
showsPrec :: Int -> EchoState -> STTYSettings -> STTYSettings
$cshow :: EchoState -> STTYSettings
show :: EchoState -> STTYSettings
$cshowList :: [EchoState] -> STTYSettings -> STTYSettings
showList :: [EchoState] -> STTYSettings -> STTYSettings
Show)
hEchoOff :: Handle -> IO EchoState
hEchoOff :: Handle -> IO EchoState
hEchoOff Handle
input = do
min_tty <- Handle -> IO Bool
minTTY Handle
input
return $ if min_tty
then MinTTY "-echo"
else DefaultTTY False
type STTYSettings = String
minTTY :: Handle -> IO Bool
#if MIN_VERSION_Win32(2,5,0)
minTTY :: Handle -> IO Bool
minTTY Handle
input = Handle -> (HANDLE -> IO Bool) -> IO Bool
forall a. Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLE Handle
input HANDLE -> IO Bool
isMinTTYHandle
#else
minTTY _ = return False
#endif
#if MIN_VERSION_Win32(2,5,0)
foreign import ccall unsafe "_get_osfhandle"
c_get_osfhandle :: CInt -> IO HANDLE
withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a
#if defined(__IO_MANAGER_WINIO__)
withHandleToHANDLE :: forall a. Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLE = Handle -> (HANDLE -> IO a) -> IO a
forall a. Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLEPosix (Handle -> (HANDLE -> IO a) -> IO a)
-> (Handle -> (HANDLE -> IO a) -> IO a)
-> Handle
-> (HANDLE -> IO a)
-> IO a
forall a. a -> a -> a
<!> Handle -> (HANDLE -> IO a) -> IO a
forall a. Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLENative
#else
withHandleToHANDLE = withHandleToHANDLEPosix
#endif
#if defined(__IO_MANAGER_WINIO__)
withHandleToHANDLENative :: Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLENative :: forall a. Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLENative Handle
haskell_handle HANDLE -> IO a
action =
Handle -> (StablePtr Handle -> IO a) -> IO a
forall a b. a -> (StablePtr a -> IO b) -> IO b
withStablePtr Handle
haskell_handle ((StablePtr Handle -> IO a) -> IO a)
-> (StablePtr Handle -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ IO a -> StablePtr Handle -> IO a
forall a b. a -> b -> a
const (IO a -> StablePtr Handle -> IO a)
-> IO a -> StablePtr Handle -> IO a
forall a b. (a -> b) -> a -> b
$ do
windows_handle <- Handle -> IO HANDLE
handleToHANDLE Handle
haskell_handle
action windows_handle
#endif
withHandleToHANDLEPosix :: Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLEPosix :: forall a. Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLEPosix Handle
haskell_handle HANDLE -> IO a
action =
Handle -> (StablePtr Handle -> IO a) -> IO a
forall a b. a -> (StablePtr a -> IO b) -> IO b
withStablePtr Handle
haskell_handle ((StablePtr Handle -> IO a) -> IO a)
-> (StablePtr Handle -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ IO a -> StablePtr Handle -> IO a
forall a b. a -> b -> a
const (IO a -> StablePtr Handle -> IO a)
-> IO a -> StablePtr Handle -> IO a
forall a b. (a -> b) -> a -> b
$ do
let write_handle_mvar :: MVar Handle__
write_handle_mvar = case Handle
haskell_handle of
FileHandle STTYSettings
_ MVar Handle__
handle_mvar -> MVar Handle__
handle_mvar
DuplexHandle STTYSettings
_ MVar Handle__
_ MVar Handle__
handle_mvar -> MVar Handle__
handle_mvar
Just fd <- (Handle__ -> Maybe CInt) -> IO Handle__ -> IO (Maybe CInt)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(Handle__ { haDevice :: ()
haDevice = dev
dev }) -> (FD -> CInt) -> Maybe FD -> Maybe CInt
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FD -> CInt
fdFD (dev -> Maybe FD
forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast dev
dev))
(IO Handle__ -> IO (Maybe CInt)) -> IO Handle__ -> IO (Maybe CInt)
forall a b. (a -> b) -> a -> b
$ MVar Handle__ -> IO Handle__
forall a. MVar a -> IO a
readMVar MVar Handle__
write_handle_mvar
windows_handle <- c_get_osfhandle fd
action windows_handle
withStablePtr :: a -> (StablePtr a -> IO b) -> IO b
withStablePtr :: forall a b. a -> (StablePtr a -> IO b) -> IO b
withStablePtr a
value = IO (StablePtr a)
-> (StablePtr a -> IO ()) -> (StablePtr a -> IO b) -> IO b
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket (a -> IO (StablePtr a)
forall a. a -> IO (StablePtr a)
newStablePtr a
value) StablePtr a -> IO ()
forall a. StablePtr a -> IO ()
freeStablePtr
#endif