Skip to content

Commit 350b1d8

Browse files
committed
add timeout for getBlockOfChars
- fixes issue haskell#160 and maybe issue haskell#77. - 20ms delay worked well for 9600bps serial port, 10ms did not.
1 parent 81336ac commit 350b1d8

File tree

2 files changed

+28
-13
lines changed

2 files changed

+28
-13
lines changed

System/Console/Haskeline/Backend/Posix.hsc

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import Control.Monad
2424
import Control.Monad.Catch (MonadMask, handle, finally)
2525
import Control.Concurrent.STM
2626
import Control.Concurrent hiding (throwTo)
27+
import Control.Concurrent.Async (runConcurrently, Concurrently(..))
28+
import Control.Applicative ((<|>))
29+
import Data.Functor (($>))
2730
import Data.Maybe (catMaybes)
2831
import System.Posix.Signals.Exts
2932
import System.Posix.Types(Fd(..))
@@ -243,21 +246,33 @@ getEvent h baseMap = keyEventLoop $ do
243246
return [KeyInput $ lexKeys baseMap cs]
244247

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

262277
stdinTTYHandles, ttyHandles :: MaybeT IO Handles
263278
stdinTTYHandles = do

haskeline.cabal

Lines changed: 1 addition & 1 deletion
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.7,
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)