haskelliogloss

Haskell Gloss, reading from console within animate function does not update drawing


I'm working in a simple implementation of LOGO in haskell.

For the graphic part I'm using Gloss, in particular the function simulateIO from the module Graphics.Gloss.Interface.IO.Simulate.

I use it to make a window and save the state of my program. The problem is with the updates, it uses a function of type ViewPort -> Float -> model -> IO model where model is the type of the state. I don't use the Viweport and Float arguments. I read a line from console, parse it and evaluate it to get a new state that I return at the end of the function.

After the first execution and update of the display, it doesn't update anymore. When I print the state I can see it is updating, but the display does not. Putting a constant value instead of getting the input fixes the problem, but that is nor very useful :).

Here is a brief part of my code:

runProgram :: Display -> IO ()
runProgram d = simulateIO d white 10 defaultEnv env2Pic step

env2Pic :: Env -> IO Picture
env2Pic e =
   ...
   in return $ pictures piccc

step :: ViewPort -> Float -> Env -> IO Env
step v f e = do
  minput <- getLine
  case minput of
    "" -> step v f e
    _ -> case parserComm minput of
      Nothing -> print "no parse" >> step v f e
      Just cms -> evalPrint e cms

evalPrint returns IO Env

Is there a way to force the redrawing?

EDIT It seems that my function is working well and the state is indeed being modified. I think the problem is that the function env2Pic doesn't get called by the simulateIO function, only a few times at the start.

I still can't figure out why.


Solution

  • Apparently the step function is not supposed to block, so you should not run blocking operations like getLine in that function. You can work around it by running getLine in a background thread. Here is a minimal working example of that:

    import Control.Concurrent (forkIO)
    import Control.Monad (forever)
    import Data.IORef (IORef, atomicWriteIORef, newIORef, readIORef)
    import Graphics.Gloss (Display (InWindow), Picture (Text), white)
    import Graphics.Gloss.Interface.IO.Simulate
      ( ViewPort,
        simulateIO,
      )
    
    runProgram :: IORef String -> Display -> IO ()
    runProgram r d = simulateIO d white 10 "" env2Pic (step r)
    
    type Env = String
    
    env2Pic :: Applicative f => Env -> f Picture
    env2Pic e = pure (Text e)
    
    step :: IORef String -> ViewPort -> Float -> Env -> IO Env
    step r _ _ _ = readIORef r
    
    background :: IORef String -> IO b
    background r = forever $ do
      x <- getLine
      atomicWriteIORef r x
    
    main :: IO ()
    main = do
      r <- newIORef ""
      forkIO $ background r
      runProgram r (InWindow "test" (500, 500) (100, 100))