Skip to content

Commit

Permalink
add timeout for getBlockOfChars
Browse files Browse the repository at this point in the history
- fixes issue haskell#160 and maybe issue haskell#77.
- 20ms delay worked well for 9600bps serial port, 10ms did not.
  • Loading branch information
goertzenator committed Feb 27, 2025
1 parent 1873dd4 commit 11e433f
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 13 deletions.
39 changes: 27 additions & 12 deletions System/Console/Haskeline/Backend/Posix.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import Control.Monad
import Control.Monad.Catch (MonadMask, handle, finally)
import Control.Concurrent.STM
import Control.Concurrent hiding (throwTo)
import Control.Concurrent.Async (runConcurrently, Concurrently(..))
import Control.Applicative ((<|>))
import Data.Functor (($>))
import Data.Maybe (catMaybes)
import System.Posix.Signals.Exts
import System.Posix.Types(Fd(..))
Expand Down Expand Up @@ -244,21 +247,33 @@ getEvent h baseMap = keyEventLoop $ do
return [KeyInput $ lexKeys baseMap cs]

-- Read at least one character of input, and more if immediately
-- available. In particular the characters making up a control sequence
-- will all be available at once, so they can be processed together
-- (with Posix.lexKeys).
-- available. If an ESC character is seen, timeout mode is
-- activated to prevent control sequences from being broken across
-- getBlockofChars calls. A timeout of 20ms was shown to work
-- well with a 9600bps serial port.
getBlockOfChars :: Handle -> IO String
getBlockOfChars h = do
c <- hGetChar h
loop [c]
where
loop cs = do
isReady <- hReady h
if not isReady
then return $ reverse cs
else do
c <- hGetChar h
loop (c:cs)
loop c [] False
where
loop :: Char -> String -> Bool -> IO String
loop c' cs' timeout' = do
let
timeout = timeout' || (c' == '\ESC')
cs = (c':cs')
maybeC <- do
isReady <- hReady h
case (timeout, isReady) of
(_, True) -> do -- fast new character case
Just <$> hGetChar h
(True, False) -> -- wait up to 20ms for next char
runConcurrently $ Concurrently (Just <$> hGetChar h)
<|> Concurrently (threadDelay 20000 $> Nothing)
(False, False) -> -- no new char and timeout mode has not been triggered
pure Nothing
case maybeC of
Just c -> loop c cs timeout
Nothing -> pure $ reverse cs

stdinTTYHandles, ttyHandles :: MaybeT IO Handles
stdinTTYHandles = do
Expand Down
2 changes: 1 addition & 1 deletion haskeline.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Library
directory>=1.1 && < 1.4, bytestring>=0.9 && < 0.12,
filepath >= 1.2 && < 1.5, transformers >= 0.2 && < 0.7,
process >= 1.0 && < 1.7, stm >= 2.4 && < 2.6,
exceptions == 0.10.*
exceptions == 0.10.*, async == 2.2.*
Default-Language: Haskell98
Default-Extensions:
ForeignFunctionInterface, Rank2Types, FlexibleInstances,
Expand Down

0 comments on commit 11e433f

Please sign in to comment.