-- {-# LANGUAGE UndecidableInstances #-}
-- {-# LANGUAGE FlexibleInstances #-}
module Hails.Yampa where
import Control.Monad (when, void)
import Data.CBMVar
import Data.IORef
import Data.Maybe (fromJust)
import Data.ReactiveValue
import Data.Time.Clock
import FRP.Yampa
-- | Define a couple of RVs connected by an SF, so that
-- writing to one makes the SF process the value and make
-- the result available in a readable RV.
yampaReactiveDual :: a -> SF a b
-> IO (ReactiveFieldWrite IO a, ReactiveFieldRead IO b)
yampaReactiveDual initial sf = do
mvar <- newCBMVar Nothing
-- Initial clock
initialClock <- getCurrentTime
lastTimeR <- newIORef initialClock
-- Reactimation Handle
rh <- reactInit
-- Initial value
(return initial)
-- Actuation
(\_ changed output -> do when changed $ writeCBMVar mvar (Just output)
return False
)
-- Processing
sf
-- RV fields
let getter = fmap fromJust $ readCBMVar mvar
setter y = void $ do -- Calculate time delta
newTime <- getCurrentTime
lastTime <- readIORef lastTimeR
let dt = realToFrac $ diffUTCTime newTime lastTime
writeIORef lastTimeR newTime
-- Run FRP system
react rh (dt, Just y)
notifier evH = installCallbackCBMVar mvar evH
let rvRead = ReactiveFieldRead getter notifier
rvWrite = ReactiveFieldWrite setter
return (rvWrite, rvRead)
-- | Create an RV that processes the value
-- with an SF every time it is written.
yampaReactive :: a -> SF a a -> IO (ReactiveFieldReadWrite IO a)
yampaReactive initial sf = do
mvar <- newCBMVar initial
-- Initial clock
initialClock <- getCurrentTime
lastTimeR <- newIORef initialClock
-- Reactimation Handle
rh <- reactInit
-- Initial value
(readCBMVar mvar)
-- Actuation
(\_ changed output -> do when changed $ writeCBMVar mvar output
return False
)
-- Processing
sf
-- RV fields
let getter = readCBMVar mvar
setter y = void $ do -- Calculate time delta
newTime <- getCurrentTime
lastTime <- readIORef lastTimeR
let dt = realToFrac $ diffUTCTime newTime lastTime
writeIORef lastTimeR newTime
-- Run FRP system
react rh (dt, Just y)
notifier = installCallbackCBMVar mvar
return $ ReactiveFieldReadWrite setter getter notifier
-- | Alternative (simpler) definition to 'yampaReactive'
yampaReactive' :: a -> SF a a -> IO (ReactiveFieldReadWrite IO a)
yampaReactive' initial sf = do
(rvW, rvR) <- yampaReactiveDual initial sf
return $ combineRVReadWrite rvR rvW
-- | To be moved to Data.ReactiveValue
combineRVReadWrite :: (ReactiveValueRead r1 a m, ReactiveValueWrite r2 a m)
=> r1 -> r2 -> ReactiveFieldReadWrite m a
combineRVReadWrite rvR rvW = ReactiveFieldReadWrite setter getter notifier
where getter = reactiveValueRead rvR
setter = reactiveValueWrite rvW
notifier = reactiveValueOnCanRead rvR