{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
module Potato.Reflex.Vty.Host
( VtyApp
, VtyResult(..)
, getDefaultVty
, runVtyApp
, runVtyAppWithHandle
, MonadVtyApp
, VtyEvent
) where
import Prelude
import System.IO
import Control.Concurrent (forkIO, killThread, MVar, newMVar, putMVar, readMVar, modifyMVar_)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Exception (onException)
import Control.Monad (forM, forM_, forever)
import Control.Monad.Fix (MonadFix, fix)
import Control.Monad.IO.Class (liftIO, MonadIO)
import Control.Monad.Identity (Identity(..))
import Control.Monad.Primitive (PrimMonad)
import Control.Monad.Ref (MonadRef, Ref, readRef)
import Data.Dependent.Sum (DSum ((:=>)))
import Data.IORef (IORef, readIORef)
import Data.Maybe (catMaybes)
import Reflex
import Reflex.Host.Class
import Reflex.Spider.Orphans ()
import Graphics.Vty (DisplayRegion)
import qualified Graphics.Vty as V
type VtyEvent = V.Event
data VtyResult t = VtyResult
{ forall t. VtyResult t -> Behavior t Picture
_vtyResult_picture :: Behavior t V.Picture
, forall t. VtyResult t -> Event t ()
_vtyResult_shutdown :: Event t ()
}
type MonadVtyApp 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)
, MonadSample t (Performable m)
, Adjustable t m
)
type VtyApp t m = MonadVtyApp t m
=> DisplayRegion
-> Event t V.Event
-> m (VtyResult t)
runVtyAppWithHandle
:: V.Vty
-> (forall t m. VtyApp t m)
-> IO ()
runVtyAppWithHandle :: Vty -> (forall t (m :: * -> *). VtyApp t m) -> IO ()
runVtyAppWithHandle Vty
vty forall t (m :: * -> *). VtyApp t m
vtyGuest = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a b. IO a -> IO b -> IO a
onException (Vty -> IO ()
V.shutdown Vty
vty) forall a b. (a -> b) -> a -> b
$
(forall a. SpiderHost Global a -> IO a
runSpiderHost :: SpiderHost Global a -> IO a) forall a b. (a -> b) -> a -> b
$ do
(Event (SpiderTimeline Global) Event
vtyEvent, IORef (Maybe (EventTrigger (SpiderTimeline Global) Event))
vtyEventTriggerRef) <- forall t (m :: * -> *) a.
(MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) =>
m (Event t a, Ref m (Maybe (EventTrigger t a)))
newEventWithTriggerRef
(Event (SpiderTimeline Global) ()
postBuild, IORef (Maybe (EventTrigger (SpiderTimeline Global) ()))
postBuildTriggerRef) <- forall t (m :: * -> *) a.
(MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) =>
m (Event t a, Ref m (Maybe (EventTrigger t a)))
newEventWithTriggerRef
Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
events <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a. IO (Chan a)
newChan
Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
triggerEvents <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a. IO (Chan a)
newChan
MVar Int
chanSizeVar :: MVar Int <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (MVar a)
newMVar Int
0
DisplayRegion
displayRegion0 <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ Output -> IO DisplayRegion
V.displayBounds forall a b. (a -> b) -> a -> b
$ Vty -> Output
V.outputIface Vty
vty
(VtyResult (SpiderTimeline Global)
vtyResult, fc :: FireCommand (SpiderTimeline Global) (SpiderHost Global)
fc@(FireCommand forall a.
[DSum (EventTrigger (SpiderTimeline Global)) Identity]
-> ReadPhase (SpiderHost Global) a -> SpiderHost Global [a]
fire)) <- do
forall t (m :: * -> *) a.
(Monad m, MonadSubscribeEvent t m, MonadReflexHost t m, MonadRef m,
Ref m ~ Ref IO) =>
PerformEventT t m a -> m (a, FireCommand t m)
hostPerformEventT forall a b. (a -> b) -> a -> b
$
forall a b c. (a -> b -> c) -> b -> a -> c
flip forall t (m :: * -> *) a. PostBuildT t m a -> Event t () -> m a
runPostBuildT Event (SpiderTimeline Global) ()
postBuild forall a b. (a -> b) -> a -> b
$
forall a b c. (a -> b -> c) -> b -> a -> c
flip forall t (m :: * -> *) a.
TriggerEventT t m a
-> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
runTriggerEventT Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
triggerEvents forall a b. (a -> b) -> a -> b
$
forall t (m :: * -> *). VtyApp t m
vtyGuest DisplayRegion
displayRegion0 Event (SpiderTimeline Global) Event
vtyEvent
let updateVty :: SpiderHost Global ()
updateVty =
forall {k} (t :: k) (m :: * -> *) a.
MonadSample t m =>
Behavior t a -> m a
sample (forall t. VtyResult t -> Behavior t Picture
_vtyResult_picture VtyResult (SpiderTimeline Global)
vtyResult) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Picture
x -> do
Int
n <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. MVar a -> IO a
readMVar forall a b. (a -> b) -> a -> b
$ MVar Int
chanSizeVar
if Int
n forall a. Ord a => a -> a -> Bool
< Int
5
then forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vty -> Picture -> IO ()
V.update Vty
vty forall a b. (a -> b) -> a -> b
$ Picture
x
else forall (m :: * -> *) a. Monad m => a -> m a
return ()
Maybe (EventTrigger (SpiderTimeline Global) ())
mPostBuildTrigger <- forall (m :: * -> *) a. MonadRef m => Ref m a -> m a
readRef IORef (Maybe (EventTrigger (SpiderTimeline Global) ()))
postBuildTriggerRef
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Maybe (EventTrigger (SpiderTimeline Global) ())
mPostBuildTrigger forall a b. (a -> b) -> a -> b
$ \EventTrigger (SpiderTimeline Global) ()
postBuildTrigger ->
forall a.
[DSum (EventTrigger (SpiderTimeline Global)) Identity]
-> ReadPhase (SpiderHost Global) a -> SpiderHost Global [a]
fire [EventTrigger (SpiderTimeline Global) ()
postBuildTrigger forall {k} (tag :: k -> *) (f :: k -> *) (a :: k).
tag a -> f a -> DSum tag f
:=> forall a. a -> Identity a
Identity ()] forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ()
SpiderHost Global ()
updateVty
EventHandle (SpiderTimeline Global) ()
shutdown <- forall t (m :: * -> *) a.
MonadSubscribeEvent t m =>
Event t a -> m (EventHandle t a)
subscribeEvent forall a b. (a -> b) -> a -> b
$ forall t. VtyResult t -> Event t ()
_vtyResult_shutdown VtyResult (SpiderTimeline Global)
vtyResult
ThreadId
nextEventThread <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ IO () -> IO ThreadId
forkIO forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Applicative f => f a -> f b
forever forall a b. (a -> b) -> a -> b
$ do
Event
ne <- Vty -> IO Event
V.nextEvent Vty
vty
let
triggerRef :: EventTriggerRef (SpiderTimeline Global) Event
triggerRef = forall t a. IORef (Maybe (EventTrigger t a)) -> EventTriggerRef t a
EventTriggerRef IORef (Maybe (EventTrigger (SpiderTimeline Global) Event))
vtyEventTriggerRef
triggerInvocation :: TriggerInvocation Event
triggerInvocation = forall a. a -> IO () -> TriggerInvocation a
TriggerInvocation Event
ne forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ()
forall a. Chan a -> a -> IO ()
writeChan Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
events [EventTriggerRef (SpiderTimeline Global) Event
triggerRef forall {k} (tag :: k -> *) (f :: k -> *) (a :: k).
tag a -> f a -> DSum tag f
:=> TriggerInvocation Event
triggerInvocation]
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar Int
chanSizeVar (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. Num a => a -> a -> a
+Int
1))
ThreadId
triggerEventThread <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ IO () -> IO ThreadId
forkIO forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Applicative f => f a -> f b
forever forall a b. (a -> b) -> a -> b
$ do
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
ev <- forall a. Chan a -> IO a
readChan Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
triggerEvents
forall a. Chan a -> a -> IO ()
writeChan Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
events [DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
ev
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar Int
chanSizeVar (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. Num a => a -> a -> a
+Int
1))
MVar Int
numFramesVar :: MVar Int <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (MVar a)
newMVar Int
0
forall a. (a -> a) -> a
fix forall a b. (a -> b) -> a -> b
$ \SpiderHost Global ()
loop -> do
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
ers <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Chan a -> IO a
readChan Chan
[DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
events
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar Int
chanSizeVar (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. Num a => a -> a -> a
+ (-Int
1)))
[Bool]
stop <- do
forall (m :: * -> *) t a.
(Monad (ReadPhase m), MonadIO m) =>
FireCommand t m
-> [DSum (EventTriggerRef t) TriggerInvocation]
-> ReadPhase m a
-> m [a]
fireEventTriggerRefs FireCommand (SpiderTimeline Global) (SpiderHost Global)
fc [DSum (EventTriggerRef (SpiderTimeline Global)) TriggerInvocation]
ers forall a b. (a -> b) -> a -> b
$
forall t (m :: * -> *) a.
MonadReadEvent t m =>
EventHandle t a -> m (Maybe (m a))
readEvent EventHandle (SpiderTimeline Global) ()
shutdown forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe (ReadPhase Global ())
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
Just ReadPhase Global ()
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
if forall (t :: * -> *). Foldable t => t Bool -> Bool
or [Bool]
stop
then forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
ThreadId -> IO ()
killThread ThreadId
nextEventThread
ThreadId -> IO ()
killThread ThreadId
triggerEventThread
Vty -> IO ()
V.shutdown Vty
vty
else do
SpiderHost Global ()
updateVty
SpiderHost Global ()
loop
where
fireEventTriggerRefs
:: (Monad (ReadPhase m), MonadIO m)
=> FireCommand t m
-> [DSum (EventTriggerRef t) TriggerInvocation]
-> ReadPhase m a
-> m [a]
fireEventTriggerRefs :: forall (m :: * -> *) t a.
(Monad (ReadPhase m), MonadIO m) =>
FireCommand t m
-> [DSum (EventTriggerRef t) TriggerInvocation]
-> ReadPhase m a
-> m [a]
fireEventTriggerRefs (FireCommand forall a.
[DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a]
fire) [DSum (EventTriggerRef t) TriggerInvocation]
ers ReadPhase m a
rcb = do
[Maybe (DSum (EventTrigger t) Identity)]
mes <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [DSum (EventTriggerRef t) TriggerInvocation]
ers forall a b. (a -> b) -> a -> b
$ \(EventTriggerRef IORef (Maybe (EventTrigger t a))
er :=> TriggerInvocation a
a IO ()
_) -> do
Maybe (EventTrigger t a)
me <- forall a. IORef a -> IO a
readIORef IORef (Maybe (EventTrigger t a))
er
return $ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\EventTrigger t a
e -> EventTrigger t a
e forall {k} (tag :: k -> *) (f :: k -> *) (a :: k).
tag a -> f a -> DSum tag f
:=> forall a. a -> Identity a
Identity a
a) Maybe (EventTrigger t a)
me
[a]
a <- forall a.
[DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a]
fire (forall a. [Maybe a] -> [a]
catMaybes [Maybe (DSum (EventTrigger t) Identity)]
mes) ReadPhase m a
rcb
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [DSum (EventTriggerRef t) TriggerInvocation]
ers forall a b. (a -> b) -> a -> b
$ \(EventTriggerRef t a
_ :=> TriggerInvocation a
_ IO ()
cb) -> IO ()
cb
return [a]
a
runVtyApp
:: (forall t m. VtyApp t m)
-> IO ()
runVtyApp :: (forall t (m :: * -> *). VtyApp t m) -> IO ()
runVtyApp forall t (m :: * -> *). VtyApp t m
app = do
Vty
vty <- IO Vty
getDefaultVty
Vty -> (forall t (m :: * -> *). VtyApp t m) -> IO ()
runVtyAppWithHandle Vty
vty forall t (m :: * -> *). VtyApp t m
app
getDefaultVty :: IO V.Vty
getDefaultVty :: IO Vty
getDefaultVty = do
Config
cfg <- IO Config
V.standardIOConfig
Config -> IO Vty
V.mkVty forall a b. (a -> b) -> a -> b
$ Config
cfg { mouseMode :: Maybe Bool
V.mouseMode = forall a. a -> Maybe a
Just Bool
True }