module Graphics.UI.WXCore.Process
(
OnReceive, OnEndProcess
, processExecAsyncTimed, processExecAsync
, StreamStatus(..)
, streamBaseStatus
, inputStreamGetContents
, inputStreamGetContentsN
, inputStreamGetLine
, inputStreamGetString
, inputStreamGetChar
, outputStreamPutString
, inputStreamGetLineNoWait
, inputStreamGetStringNoWait
, inputStreamGetCharNoWait
, outputStreamPutStringNoWait
) where
import System.IO.Unsafe( unsafeInterleaveIO )
import Graphics.UI.WXCore.WxcDefs
import Graphics.UI.WXCore.WxcClasses
import Graphics.UI.WXCore.Types
import Graphics.UI.WXCore.Events
import Foreign
import Foreign.C.String
import Foreign.C.Types
outputStreamPutString :: OutputStream a -> String -> IO ()
outputStreamPutString outputStream s
= withCString s $ \cstr -> write cstr (length s)
where
write cstr n
= do outputStreamWrite outputStream cstr n
m <- outputStreamLastWrite outputStream
if (m < n && m > 0 )
then write (advancePtr cstr m) (n m)
else return ()
outputStreamPutStringNoWait :: OutputStream a -> String -> IO Int
outputStreamPutStringNoWait outputStream s
= withCString s $ \cstr ->
do outputStreamWrite outputStream cstr (length s)
outputStreamLastWrite outputStream
inputStreamGetLineNoWait :: InputStream a -> Int -> IO String
inputStreamGetLineNoWait inputStream maxChars
= readInputStream "" 0
where
readInputStream acc n
= if n >= maxChars
then return (reverse acc)
else do mbc <- inputStreamGetCharNoWait inputStream
case mbc of
Nothing -> return (reverse acc)
Just '\n' -> return (reverse ('\n':acc))
Just '\r' -> do mbc2 <- inputStreamGetCharNoWait inputStream
case mbc2 of
Just c2 | c2 /= '\n' -> inputStreamUngetch inputStream c2 >>
return ()
_ -> return ()
return (reverse ('\n':acc))
Just c -> readInputStream (c:acc) (n+1)
inputStreamGetStringNoWait :: InputStream a -> Int -> IO String
inputStreamGetStringNoWait input maxChars
= readInputStream "" 0
where
readInputStream acc n
= if ( n >= maxChars )
then return (reverse acc)
else do mbc <- inputStreamGetCharNoWait input
case mbc of
Nothing -> return (reverse acc)
Just c -> readInputStream (c:acc) (n+1)
inputStreamGetCharNoWait :: InputStream a -> IO (Maybe Char)
inputStreamGetCharNoWait input
= do canRead <- inputStreamCanRead input
if canRead
then do c <- inputStreamGetC input
return (Just c)
else return Nothing
inputStreamGetLine :: InputStream a -> Int -> IO String
inputStreamGetLine inputStream maxChars
= readInputStream "" 0
where
readInputStream acc n
= if n >= maxChars
then return (reverse acc)
else do c <- inputStreamGetChar inputStream
case c of
'\n' -> return (reverse ('\n':acc))
'\r' -> do mbc2 <- inputStreamGetCharNoWait inputStream
case mbc2 of
Just c2 | c2 /= '\n' -> inputStreamUngetch inputStream c2 >>
return ()
_ -> return ()
return (reverse ('\n':acc))
_ -> readInputStream (c:acc) (n+1)
inputStreamGetChar :: InputStream a -> IO Char
inputStreamGetChar input
= inputStreamGetC input
inputStreamGetString :: InputStream a -> Int -> IO String
inputStreamGetString inputStream n
= allocaBytes (n+1) $ \buffer ->
do inputStreamRead inputStream buffer n
nread <- inputStreamLastRead inputStream
mapM (peekChar buffer) [0..nread1]
where
peekChar :: Ptr CChar -> Int -> IO Char
peekChar p ofs
= do cchar <- peekElemOff p ofs
return (castCCharToChar cchar)
inputStreamGetContents :: InputStream a -> IO String
inputStreamGetContents inputStream
= inputStreamGetContentsN inputStream 1
inputStreamGetContentsN :: InputStream a -> Int -> IO String
inputStreamGetContentsN inputStream n
= do status <- streamBaseGetLastError inputStream
if (status == wxSTREAM_NO_ERROR)
then do x <- inputStreamGetString inputStream n
xs <- unsafeInterleaveIO (inputStreamGetContentsN inputStream n)
return (x ++ xs)
else return ""
streamBaseStatus :: StreamBase a -> IO StreamStatus
streamBaseStatus stream
= do code <- streamBaseGetLastError stream
return (streamStatusFromInt code)
type OnReceive = String -> StreamStatus -> IO ()
type OnEndProcess = Int -> IO ()
processExecAsyncTimed :: Window a -> String -> Bool -> OnEndProcess -> OnReceive -> OnReceive
-> IO (String -> IO StreamStatus, Process (), Int)
processExecAsyncTimed parent cmd readInputOnEnd onEndProcess onOutput onErrOutput
= do process <- processCreateDefault parent idAny
processRedirect process
pid <- wxcAppExecuteProcess cmd wxEXEC_ASYNC process
if (pid == 0)
then return (\_s -> return StreamEof, objectNull, pid)
else do v <- varCreate (Just process)
windowOnIdle parent (handleAnyInput v)
unregister <- appRegisterIdle 100
evtHandlerOnEndProcess parent (handleTerminate v unregister)
let send txt = handleSend v txt
return (send, process, pid)
where
maxLine :: Int
maxLine = 160
handleSend :: Var (Maybe (Process a)) -> String -> IO StreamStatus
handleSend v txt
= withProcess v StreamEof $ \process ->
do outputPipe <- processGetOutputStream process
outputStreamPutString outputPipe txt
streamBaseStatus outputPipe
handleAnyInput :: Var (Maybe (Process a)) -> IO Bool
handleAnyInput v
= withProcess v False $ \process ->
do inputPipe <- processGetInputStream process
available <- handleInput inputPipe onOutput
errorPipe <- processGetErrorStream process
handleAllInput errorPipe onErrOutput
return available
handleAllInput :: InputStream a -> OnReceive -> IO ()
handleAllInput input onOutput'
= do available <- handleInput input onOutput'
if (available)
then handleAllInput input onOutput'
else return ()
handleInput :: InputStream a -> OnReceive -> IO Bool
handleInput input onOutput'
= do txt <- inputStreamGetLineNoWait input maxLine
status <- streamBaseStatus input
if null txt
then case status of
StreamOk -> return False
_ -> do onOutput' "" status
return False
else do onOutput' txt status
return True
handleTerminate :: Var (Maybe (Process a)) -> IO () -> Int -> Int -> IO ()
handleTerminate v unregister _pid exitCode
= do unregister
withProcess v () $ \process ->
do varSet v Nothing
if (readInputOnEnd)
then do inputPipe <- processGetInputStream process
handleAllInput inputPipe onOutput
else return ()
onEndProcess exitCode
processDelete process
return ()
withProcess v x f
= do mb <- varGet v
case mb of
Nothing -> return x
Just p -> f p
processExecAsync :: Window a -> String -> Int -> OnEndProcess -> OnReceive -> OnReceive
-> IO (String -> IO (), Process (), Int)
processExecAsync parent command bufferSize onEndProcess onOutput onErrOutput
= do process <- processCreateDefault parent idAny
processRedirect process
pid <- wxcAppExecuteProcess command wxEXEC_ASYNC process
if (pid == 0)
then return (\_s -> return (), objectNull, pid)
else do inputPipe <- processGetInputStream process
outputPipe <- processGetOutputStream process
errorPipe <- processGetErrorStream process
evtHandlerOnEndProcess parent (handleOnEndProcess pid process inputPipe outputPipe errorPipe)
evtHandlerOnInput parent onOutput inputPipe bufferSize
evtHandlerOnInput parent onErrOutput errorPipe bufferSize
let send txt = outputStreamPutString outputPipe txt
return (send, process, pid)
where
handleOnEndProcess ourPid process _inputPipe _outputPipe _errorPipe pid exitcode
| ourPid == pid = do onEndProcess exitcode
processDelete process
| otherwise = return ()