{-# 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 }