Enlico
Enlico

Reputation: 28490

Keep reading user keyboard input if available, while echoing back the latest available

The following code keeps waiting for user input, and echoes it back to screen when it gets it. Then it goes back in waiting-for-input mode. When it's in this state, it keeps writing a constant message on screen.

import Control.Concurrent
import Data.Maybe
import System.IO

main = do
  hSetBuffering stdin NoBuffering
  future_input <- newEmptyMVar
  forkIO $ (>>) <$> putMVar future_input
                <*> (putStrLn . ("pressed key: " ++) . return)
                =<< getChar
  wait future_input
    where wait future_input = do
          input <- tryTakeMVar future_input
          if isJust input
            then main
            else putStrLn "keep waiting" >> threadDelay 1000000 >> wait future_input

What I would like to obtain, is that the message keep waiting be accompained by the lates available user input.

The only idea I had so far, is that I should

  1. declare another MVar, latest_input together with future_input
  2. have the forked thread fill it in at the same time as future_input
  3. in the else of if isJust input, I should tryTakeMVar from latest_input and, if there is something in it (which is, by virtue of point 2, always the case except the first time), I could use it in the output.

However, in this smokey idea of mine, I think I should also have wait take both MVars, because I don't have to lose track of any of them when waiting. Similarly, even in the then branch of if isJust input, I should probably pass latest_input, which means I have to use a function other than main, which would be called by main.

For now, I've got here:

import Control.Concurrent
import Data.Maybe
import System.IO

main = do
  hSetBuffering stdin NoBuffering
  future_input <- newEmptyMVar
  latest_input <- newEmptyMVar
  forkIO $ ((>>) .) . (>>)
                <$> putMVar future_input
                <*> putMVar latest_input
                <*> (putStrLn . ("pressed key: " ++) . return)
                =<< getChar
  wait future_input
    where wait future_input = do
          input <- tryTakeMVar future_input
          if isJust input
            then main
            else putStrLn "keep moving" >> threadDelay 1000000 >> wait future_input

Upvotes: 0

Views: 90

Answers (1)

Enlico
Enlico

Reputation: 28490

Uh, I think I got it :D (and I've put it in Code Review).

import Control.Concurrent
import Data.Maybe
import System.IO

main = do
  hSetBuffering stdin NoBuffering
  future_input <- newEmptyMVar
  latest_input <- newEmptyMVar
  putMVar latest_input 'A' -- assuming a previous input the first time
  work future_input latest_input

work :: MVar Char -> MVar Char -> IO ()
work future_input latest_input = do
  forkIO $ ((>>) .) . (>>)
                <$> putMVar future_input
                <*> tryPutMVar latest_input
                <*> (putStrLn . ("pressed key: " ++) . return)
                =<< getChar
  wait future_input latest_input
    where wait future_input latest_input = do
          input <- tryTakeMVar future_input
          old_input <- takeMVar latest_input
          if isJust input
            then do
              putMVar latest_input (fromJust input)
              work future_input latest_input
            else do
              putMVar latest_input old_input
              putStrLn ("latest input was " ++ (return old_input))
                >> threadDelay 1000000
                >> wait future_input latest_input

Upvotes: 0

Related Questions