packages feed

brillo-1.13.3: Brillo/Internals/Interface/Animate/Timing.hs

{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_HADDOCK hide #-}

{-| Handles timing of animation.
     The main point is that we want to restrict the framerate to something
     sensible, instead of just displaying at the machines maximum possible
     rate and soaking up 100% cpu.

     We also keep track of the elapsed time since the start of the program,
     so we can pass this to the user's animation function.
-}
module Brillo.Internals.Interface.Animate.Timing (
  animateBegin,
  animateEnd,
)
where

import Brillo.Internals.Interface.Animate.State
import Brillo.Internals.Interface.Backend
import Control.Monad
import Data.IORef


{-| Handles animation timing details.
     Call this function at the start of each frame.
-}
animateBegin :: IORef State -> DisplayCallback
animateBegin stateRef backendRef =
  do
    -- write the current time into the display state
    displayTime <- elapsedTime backendRef
    displayTimeLast <- stateRef `getsIORef` stateDisplayTime
    let displayTimeElapsed = displayTime - displayTimeLast

    modifyIORef' stateRef $ \s ->
      s
        { stateDisplayTime = displayTime
        , stateDisplayTimeLast = displayTimeLast
        }

    -- increment the animation time
    animate <- stateRef `getsIORef` stateAnimate
    animateCount <- stateRef `getsIORef` stateAnimateCount
    animateTime <- stateRef `getsIORef` stateAnimateTime
    animateStart <- stateRef `getsIORef` stateAnimateStart

    {-      when (animateCount `mod` 5 == 0)
             $  putStr  $  "  displayTime        = " ++ show displayTime                ++ "\n"
                        ++ "  displayTimeLast    = " ++ show displayTimeLast            ++ "\n"
                        ++ "  displayTimeElapsed = " ++ show displayTimeElapsed         ++ "\n"
                        ++ "  fps                = " ++ show (truncate $ 1 / displayTimeElapsed)   ++ "\n"
    -}
    when (animate && not animateStart) $
      modifyIORef' stateRef $ \s ->
        s
          { stateAnimateTime = animateTime + displayTimeElapsed
          }

    when animate $
      modifyIORef' stateRef $ \s ->
        s
          { stateAnimateCount = animateCount + 1
          , stateAnimateStart = False
          }


{-| Handles animation timing details.
     Call this function at the end of each frame.
-}
animateEnd :: IORef State -> DisplayCallback
animateEnd stateRef backendRef =
  do
    -- timing gate, limits the maximum frame frequency (FPS)
    timeClamp <- stateRef `getsIORef` stateDisplayTimeClamp

    -- the start of this gate
    gateTimeStart <- elapsedTime backendRef

    -- end of the previous gate
    gateTimeEnd <- stateRef `getsIORef` stateGateTimeEnd
    let gateTimeElapsed = gateTimeStart - gateTimeEnd

    when (gateTimeElapsed < timeClamp) $
      do sleep backendRef (timeClamp - gateTimeElapsed)

    gateTimeFinal <- elapsedTime backendRef

    modifyIORef' stateRef $ \s ->
      s
        { stateGateTimeEnd = gateTimeFinal
        , stateGateTimeElapsed = gateTimeElapsed
        }


getsIORef :: IORef a -> (a -> r) -> IO r
getsIORef ref fun =
  liftM fun $ readIORef ref