packages feed

reflex-gi-gtk-0.1.0.0: src/Reflex/GI/Gtk/Host.hs

-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.

{-# LANGUAGE RankNTypes, FlexibleContexts, PatternSynonyms, OverloadedLabels, RecursiveDo #-}

module Reflex.GI.Gtk.Host
  ( MonadGtk
  , runReflexGtk
  ) where

import Control.Concurrent (myThreadId)
import Control.Concurrent.Async ( async
                                , waitCatchSTM
                                )
import Control.Concurrent.Chan ( newChan
                               , readChan
                               )
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Ref (readRef)
import Data.Dependent.Sum ( DSum((:=>))
                          , (==>)
                          )
import Data.Function (fix)
import Data.Int (Int32)
import Data.Maybe (catMaybes)
import Data.Void (absurd)
import GI.Gtk ( Application
              , on
              )
import Reflex ( FireCommand(FireCommand)
              , TriggerInvocation(TriggerInvocation)
              , hostPerformEventT
              , runPostBuildT
              , runSpiderHostForTimeline
              , runTriggerEventT
              , unEventTriggerRef
              , withSpiderTimeline
              )
import Reflex.GI.Gtk.Class (MonadGtk)
import Reflex.GI.Gtk.Run.Base (runGtkT)
import Reflex.Host.Class (newEventWithTriggerRef)

runReflexGtk :: Application
             -> Maybe [String]
             -> (forall t m. (MonadGtk t m) => m ())
             -> IO Int32
runReflexGtk app argv a = do
  _ <- app `on` #startup $
    withSpiderTimeline
    $ \tl -> flip runSpiderHostForTimeline tl $ do
        eventChan <- liftIO newChan
        rec
          let waitForEventThreadException =
                either id absurd <$> waitCatchSTM eventThread

          (postBuildE, postBuildTriggerRef) <- newEventWithTriggerRef
          ((), FireCommand fireCommand) <-
            hostPerformEventT $
            liftIO myThreadId
            >>= runGtkT (
            runTriggerEventT (  runPostBuildT a postBuildE
                             ) eventChan
            ) waitForEventThreadException

          readRef postBuildTriggerRef
            >>= mapM_ (\trigger -> fireCommand [trigger ==> ()] $ pure ())

          eventThread <- liftIO $ async $ flip runSpiderHostForTimeline tl $ fix $ \loop -> do
            invocations <- liftIO $ readChan eventChan
            triggers <-
              catMaybes <$>
              traverse (\(triggerRef :=> TriggerInvocation x _) ->
                           fmap (==> x) <$> readRef (unEventTriggerRef triggerRef)
                       ) invocations
            _ <- fireCommand triggers $ pure ()
            liftIO $ mapM_ (\(_ :=> (TriggerInvocation _ done)) -> done) invocations
            loop
        pure ()

  #run app argv