reflex-ghci-0.1.4.0: tests/HeadlessHost.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
module HeadlessHost where
import Data.Maybe
import Reflex
import Control.Monad.Fix
import Control.Monad.Primitive
import Reflex.Host.Class
import Control.Monad.IO.Class
import Control.Monad.Ref
import Data.IORef
import Data.Dependent.Sum
import Control.Concurrent.Chan (newChan, readChan)
import Control.Monad (forM, forM_)
import Control.Monad.Identity (Identity(..))
type HeadlessResult t = Event t ()
type MonadHeadlessApp t m =
( Reflex t
, MonadHold t m
, MonadFix m
, PrimMonad (HostFrame t)
, ReflexHost t
, MonadIO (HostFrame t)
, Ref m ~ IORef
, Ref (HostFrame t) ~ IORef
, MonadRef (HostFrame t)
, NotReady t m
, TriggerEvent t m
, PostBuild t m
, PerformEvent t m
, MonadIO m
, MonadIO (Performable m)
, Adjustable t m
)
runHeadlessApp
:: (forall t m. MonadHeadlessApp t m => m (HeadlessResult t))
-> IO ()
runHeadlessApp guest =
(runSpiderHost :: SpiderHost Global a -> IO a) $ do
(postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
events <- liftIO newChan
(vtyResult, fc@(FireCommand fire)) <- do
hostPerformEventT $
flip runPostBuildT postBuild $
flip runTriggerEventT events $
guest
mPostBuildTrigger <- readRef postBuildTriggerRef
forM_ mPostBuildTrigger $ \postBuildTrigger ->
fire [postBuildTrigger :=> Identity ()] $ return ()
shutdown <- subscribeEvent vtyResult
fix $ \loop -> do
ers <- liftIO $ readChan events
stop <- fireEventTriggerRefs fc ers $ readEvent shutdown >>= \case
Nothing -> return False
Just _ -> return True
if or stop
then return ()
else loop
where
-- TODO Some part of this is probably general enough to belong in reflex
-- | Use the given 'FireCommand' to fire events that have subscribers
-- and call the callback for the 'TriggerInvocation' of each.
fireEventTriggerRefs
:: (Monad (ReadPhase m), MonadIO m)
=> FireCommand t m
-> [DSum (EventTriggerRef t) TriggerInvocation]
-> ReadPhase m a
-> m [a]
fireEventTriggerRefs (FireCommand fire) ers rcb = do
mes <- liftIO $
forM ers $ \(EventTriggerRef er :=> TriggerInvocation a _) -> do
me <- readIORef er
return $ fmap (\e -> e :=> Identity a) me
a <- fire (catMaybes mes) rcb
liftIO $ forM_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
return a