{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
module Reflex.Host.Headless where
import Control.Concurrent.Chan (newChan, readChan)
import Control.Monad (unless)
import Control.Monad.Fix (MonadFix, fix)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Primitive (PrimMonad)
import Control.Monad.Ref (MonadRef, Ref, readRef)
import Data.Dependent.Sum (DSum (..), (==>))
import Data.Foldable (for_)
import Data.Functor.Identity (Identity(..))
import Data.IORef (IORef, readIORef)
import Data.Maybe (catMaybes, fromMaybe)
import Data.Traversable (for)
import Reflex
import Reflex.Host.Class
type MonadHeadlessApp t m =
( Adjustable t m
, MonadFix m
, MonadHold t m
, MonadIO (HostFrame t)
, MonadIO (Performable m)
, MonadIO m
, MonadRef (HostFrame t)
, NotReady t m
, PerformEvent t m
, PostBuild t m
, PrimMonad (HostFrame t)
, Ref (HostFrame t) ~ IORef
, Ref m ~ IORef
, Reflex t
, ReflexHost t
, TriggerEvent t m
)
runHeadlessApp
:: (forall t m. MonadHeadlessApp t m => m (Event t ()))
-> IO ()
runHeadlessApp guest =
withSpiderTimeline $ runSpiderHostForTimeline $ do
(postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
events <- liftIO newChan
(result, fc@(FireCommand fire)) <- do
hostPerformEventT $
flip runPostBuildT postBuild $
flip runTriggerEventT events $
guest
mPostBuildTrigger <- readRef postBuildTriggerRef
shutdown <- subscribeEvent result
soa <- for mPostBuildTrigger $ \postBuildTrigger ->
fire [postBuildTrigger :=> Identity ()] $ isFiring shutdown
unless (or (fromMaybe [] soa)) $ fix $ \loop -> do
ers <- liftIO $ readChan events
stop <- do
fireEventTriggerRefs fc ers $
isFiring shutdown
if or stop
then pure ()
else loop
where
isFiring ev = readEvent ev >>= \case
Nothing -> pure False
Just _ -> pure True
fireEventTriggerRefs
:: MonadIO m
=> FireCommand t m
-> [DSum (EventTriggerRef t) TriggerInvocation]
-> ReadPhase m a
-> m [a]
fireEventTriggerRefs (FireCommand fire) ers rcb = do
mes <- liftIO $
for ers $ \(EventTriggerRef er :=> TriggerInvocation a _) -> do
me <- readIORef er
pure $ fmap (==> a) me
a <- fire (catMaybes mes) rcb
liftIO $ for_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
pure a