Skip to content

Commit 3d13eba

Browse files
committed
add timeout for getBlockOfChars
- fixes issue #160 and maybe issue #77. - 20ms delay worked well for 9600bps serial port, 10ms did not.
1 parent 7d8d1ab commit 3d13eba

File tree

2 files changed

+28
-13
lines changed

2 files changed

+28
-13
lines changed

System/Console/Haskeline/Backend/Posix.hsc

+27-12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import Control.Monad
2222
import Control.Monad.Catch (MonadMask, handle, finally)
2323
import Control.Concurrent.STM
2424
import Control.Concurrent hiding (throwTo)
25+
import Control.Concurrent.Async (runConcurrently, Concurrently(..))
26+
import Control.Applicative ((<|>))
27+
import Data.Functor (($>))
2528
import Data.Maybe (catMaybes)
2629
import System.Posix.Signals.Exts
2730
import System.Posix.Types(Fd(..))
@@ -233,21 +236,33 @@ getEvent h baseMap = keyEventLoop $ do
233236
return [KeyInput $ lexKeys baseMap cs]
234237

235238
-- Read at least one character of input, and more if immediately
236-
-- available. In particular the characters making up a control sequence
237-
-- will all be available at once, so they can be processed together
238-
-- (with Posix.lexKeys).
239+
-- available. If an ESC character is seen, timeout mode is
240+
-- activated to prevent control sequences from being broken across
241+
-- getBlockofChars calls. A timeout of 20ms was shown to work
242+
-- well with a 9600bps serial port.
239243
getBlockOfChars :: Handle -> IO String
240244
getBlockOfChars h = do
241245
c <- hGetChar h
242-
loop [c]
243-
where
244-
loop cs = do
245-
isReady <- hReady h
246-
if not isReady
247-
then return $ reverse cs
248-
else do
249-
c <- hGetChar h
250-
loop (c:cs)
246+
loop c [] False
247+
where
248+
loop :: Char -> String -> Bool -> IO String
249+
loop c' cs' timeout' = do
250+
let
251+
timeout = timeout' || (c' == '\ESC')
252+
cs = (c':cs')
253+
maybeC <- do
254+
isReady <- hReady h
255+
case (timeout, isReady) of
256+
(_, True) -> do -- fast new character case
257+
Just <$> hGetChar h
258+
(True, False) -> -- wait up to 20ms for next char
259+
runConcurrently $ Concurrently (Just <$> hGetChar h)
260+
<|> Concurrently (threadDelay 20000 $> Nothing)
261+
(False, False) -> -- no new char and timeout mode has not been triggered
262+
pure Nothing
263+
case maybeC of
264+
Just c -> loop c cs timeout
265+
Nothing -> pure $ reverse cs
251266

252267
stdinTTYHandles, ttyHandles :: MaybeT IO Handles
253268
stdinTTYHandles = do

haskeline.cabal

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Library
5353
directory>=1.1 && < 1.4, bytestring>=0.9 && < 0.12,
5454
filepath >= 1.2 && < 1.5, transformers >= 0.2 && < 0.6,
5555
process >= 1.0 && < 1.7, stm >= 2.4 && < 2.6,
56-
exceptions == 0.10.*
56+
exceptions == 0.10.*, async == 2.2.*
5757
Default-Language: Haskell98
5858
Default-Extensions:
5959
ForeignFunctionInterface, Rank2Types, FlexibleInstances,

0 commit comments

Comments
 (0)