{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Reflex.Host.Basic
(
basicHostWithQuit
, basicHostForever
, BasicGuest
, BasicGuestConstraints
, repeatUntilQuit
, repeatUntilQuit_
) where
import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (newChan, readChan)
import Control.Concurrent.STM.TVar (newTVarIO, writeTVar, readTVarIO)
import Control.Lens ((<&>))
import Control.Monad (void, when, unless)
import Control.Monad.Fix (MonadFix)
import Control.Monad.Primitive (PrimMonad)
import Control.Monad.Ref (MonadRef(..))
import Control.Monad.STM (atomically)
import Control.Monad.Trans (MonadIO(..), MonadTrans(..))
import Data.Dependent.Sum (DSum(..), (==>))
import Data.Foldable (for_, traverse_)
import Data.Functor.Identity (Identity)
import Data.Maybe (catMaybes, isJust)
import Data.Traversable (for)
import Reflex
import Reflex.Host.Class
type BasicGuestConstraints t (m :: * -> *) =
( MonadReflexHost t m
, MonadHold t m
, MonadSample t m
, Ref m ~ Ref IO
, MonadRef (HostFrame t)
, Ref (HostFrame t) ~ Ref IO
, MonadIO (HostFrame t)
, PrimMonad (HostFrame t)
, MonadIO m
, MonadFix m
)
newtype BasicGuest t (m :: * -> *) a =
BasicGuest {
unBasicGuest :: PostBuildT t (TriggerEventT t (PerformEventT t m)) a
} deriving (Functor, Applicative, Monad, MonadFix)
instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (BasicGuest t m) where
{-# INLINEABLE liftIO #-}
liftIO = BasicGuest . liftIO
instance ReflexHost t => MonadSample t (BasicGuest t m) where
{-# INLINABLE sample #-}
sample = BasicGuest . lift . sample
instance (ReflexHost t, MonadHold t m) => MonadHold t (BasicGuest t m) where
{-# INLINABLE hold #-}
hold v0 = BasicGuest . lift . hold v0
{-# INLINABLE holdDyn #-}
holdDyn v0 = BasicGuest . lift . holdDyn v0
{-# INLINABLE holdIncremental #-}
holdIncremental v0 = BasicGuest . lift . holdIncremental v0
{-# INLINABLE buildDynamic #-}
buildDynamic a0 = BasicGuest . lift . buildDynamic a0
{-# INLINABLE headE #-}
headE = BasicGuest . lift . headE
instance ReflexHost t => PostBuild t (BasicGuest t m) where
{-# INLINABLE getPostBuild #-}
getPostBuild = BasicGuest getPostBuild
instance
( ReflexHost t
, MonadRef (HostFrame t)
, Ref (HostFrame t) ~ Ref IO
) => TriggerEvent t (BasicGuest t m) where
{-# INLINABLE newTriggerEvent #-}
newTriggerEvent = BasicGuest $ lift newTriggerEvent
{-# INLINABLE newTriggerEventWithOnComplete #-}
newTriggerEventWithOnComplete =
BasicGuest $ lift newTriggerEventWithOnComplete
{-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
newEventWithLazyTriggerWithOnComplete =
BasicGuest . lift . newEventWithLazyTriggerWithOnComplete
instance
( ReflexHost t
, Ref m ~ Ref IO
, MonadRef (HostFrame t)
, Ref (HostFrame t) ~ Ref IO
, MonadIO (HostFrame t)
, PrimMonad (HostFrame t)
, MonadIO m
) => PerformEvent t (BasicGuest t m) where
type Performable (BasicGuest t m) = HostFrame t
{-# INLINABLE performEvent_ #-}
performEvent_ = BasicGuest . lift . lift . performEvent_
{-# INLINABLE performEvent #-}
performEvent = BasicGuest . lift . lift . performEvent
instance
( ReflexHost t
, Ref m ~ Ref IO
, MonadHold t m
, PrimMonad (HostFrame t)
) => Adjustable t (BasicGuest t m) where
{-# INLINABLE runWithReplace #-}
runWithReplace a0 a' = BasicGuest $
runWithReplace (unBasicGuest a0) (fmap unBasicGuest a')
{-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
traverseIntMapWithKeyWithAdjust f dm0 dm' = BasicGuest $
traverseIntMapWithKeyWithAdjust (\k v -> unBasicGuest (f k v)) dm0 dm'
{-# INLINABLE traverseDMapWithKeyWithAdjust #-}
traverseDMapWithKeyWithAdjust f dm0 dm' = BasicGuest $
traverseDMapWithKeyWithAdjust (\k v -> unBasicGuest (f k v)) dm0 dm'
{-# INLINABLE traverseDMapWithKeyWithAdjustWithMove #-}
traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = BasicGuest $
traverseDMapWithKeyWithAdjustWithMove (\k v -> unBasicGuest (f k v)) dm0 dm'
instance ReflexHost t => NotReady t (BasicGuest t m) where
{-# INLINABLE notReadyUntil #-}
notReadyUntil _ = pure ()
{-# INLINABLE notReady #-}
notReady = pure ()
basicHostForever
:: (forall t m. BasicGuestConstraints t m => BasicGuest t m ())
-> IO ()
basicHostForever guest = basicHostWithQuit $ never <$ guest
basicHostWithQuit
:: (forall t m. BasicGuestConstraints t m => BasicGuest t m (Event t ()))
-> IO ()
basicHostWithQuit guest =
withSpiderTimeline $ runSpiderHostForTimeline $ do
(postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
triggerEventChan <- liftIO newChan
rHasQuit <- newRef False
(eQuit, FireCommand fire) <- hostPerformEventT
. flip runTriggerEventT triggerEventChan
. flip runPostBuildT postBuild
$ unBasicGuest guest
hQuit <- subscribeEvent eQuit
let
runFrameAndCheckQuit firings = do
lmQuit <- fire firings $ readEvent hQuit >>= sequenceA
when (any isJust lmQuit) $ writeRef rHasQuit True
readRef postBuildTriggerRef
>>= traverse_ (\t -> runFrameAndCheckQuit [t ==> ()])
let
loop = do
hasQuit <- readRef rHasQuit
unless hasQuit $ do
eventsAndTriggers <- liftIO $ readChan triggerEventChan
let
prepareFiring
:: (MonadRef m, Ref m ~ Ref IO)
=> DSum (EventTriggerRef t) TriggerInvocation
-> m (Maybe (DSum (EventTrigger t) Identity))
prepareFiring (EventTriggerRef er :=> TriggerInvocation x _)
= readRef er <&> fmap (==> x)
catMaybes <$> for eventsAndTriggers prepareFiring
>>= runFrameAndCheckQuit
liftIO . for_ eventsAndTriggers $
\(_ :=> TriggerInvocation _ cb) -> cb
loop
loop
repeatUntilQuit
:: BasicGuestConstraints t m
=> IO a
-> Event t ()
-> BasicGuest t m (Event t a)
repeatUntilQuit act eQuit = do
ePostBuild <- getPostBuild
tHasQuit <- liftIO $ newTVarIO False
let
go fire = loop where
loop = do
hasQuit <- readTVarIO tHasQuit
unless hasQuit $ (act >>= fire) *> loop
performEvent_ $ liftIO (atomically $ writeTVar tHasQuit True) <$ eQuit
performEventAsync $ liftIO . void . forkIO . go <$ ePostBuild
repeatUntilQuit_
:: BasicGuestConstraints t m
=> IO a
-> Event t ()
-> BasicGuest t m ()
repeatUntilQuit_ act eQuit = do
ePostBuild <- getPostBuild
tHasQuit <- liftIO $ newTVarIO False
let
loop = do
hasQuit <- readTVarIO tHasQuit
unless hasQuit $ act *> loop
performEvent_ $ liftIO (atomically $ writeTVar tHasQuit True) <$ eQuit
performEvent_ $ liftIO (void $ forkIO loop) <$ ePostBuild