packages feed

matterhorn-90000.1.1: src/Matterhorn/App.hs

module Matterhorn.App
  ( runMatterhorn
  , closeMatterhorn
  )
where

import           Prelude ()
import           Matterhorn.Prelude

import qualified Brick as B
import           Control.Monad.Trans.Except ( runExceptT )
import qualified Control.Exception as E
import qualified Graphics.Vty as Vty
import qualified Graphics.Vty.CrossPlatform as Vty
import qualified Graphics.Vty.UnicodeWidthTable.Install as Vty
import           Text.Aspell ( stopAspell )
import           GHC.Conc (getNumProcessors, setNumCapabilities)

import           Network.Mattermost

import           Matterhorn.Config
import           Matterhorn.Draw
import qualified Matterhorn.Events as Events
import           Matterhorn.IOUtil
import           Matterhorn.InputHistory
import           Matterhorn.LastRunState
import           Matterhorn.Options
import           Matterhorn.State.Setup
import           Matterhorn.State.Setup.Threads.Logging ( shutdownLogManager )
import           Matterhorn.Types


app :: B.App ChatState MHEvent Name
app =
    B.App { B.appDraw         = draw
          , B.appHandleEvent  = Events.onEvent
          , B.appAttrMap      = (^.csResources.crTheme)
          , B.appChooseCursor = \s cs -> do
              tId <- s^.csCurrentTeamId
              cursorByMode cs s tId (teamMode $ s^.csTeam(tId))
          , B.appStartEvent = do
              vty <- B.getVtyHandle
              (w, h) <- liftIO $ Vty.displayBounds $ Vty.outputIface vty
              runMHEvent $ Events.setWindowSize w h
          }

cursorByMode :: [B.CursorLocation Name] -> ChatState -> TeamId -> Mode -> Maybe (B.CursorLocation Name)
cursorByMode cs s tId mode =
    case mode of
        Main -> case s^.csTeam(tId).tsMessageInterfaceFocus of
            FocusCurrentChannel -> do
                cId <- s^.csCurrentChannelId(tId)
                mi <- s^?maybeChannelMessageInterface(cId)
                cur <- messageInterfaceCursor mi
                B.showCursorNamed cur cs
            FocusThread -> do
                ti <- s^.csTeam(tId).tsThreadInterface
                cur <- messageInterfaceCursor ti
                B.showCursorNamed cur cs
        LeaveChannelConfirm           -> Nothing
        DeleteChannelConfirm          -> Nothing
        MessageSelectDeleteConfirm {} -> Nothing
        PostListWindow {}             -> Nothing
        ViewMessage                   -> Nothing
        ShowHelp {}                   -> Nothing
        EditNotifyPrefs               -> Nothing
        ChannelSelect                 -> B.showFirstCursor s cs
        UserListWindow                -> B.showFirstCursor s cs
        ReactionEmojiListWindow       -> B.showFirstCursor s cs
        ChannelListWindow             -> B.showFirstCursor s cs
        ThemeListWindow               -> B.showFirstCursor s cs
        ChannelTopicWindow            -> B.showCursorNamed (ChannelTopicEditor tId) cs

applicationMaxCPUs :: Int
applicationMaxCPUs = 2

setupCpuUsage :: Config -> IO ()
setupCpuUsage config = do
    actualNumCpus <- getNumProcessors

    let requestedCPUs = case configCpuUsagePolicy config of
            SingleCPU -> 1
            MultipleCPUs -> min applicationMaxCPUs actualNumCpus

    setNumCapabilities requestedCPUs

setupCharWidthMap :: Config -> IO ()
setupCharWidthMap config = do
    case configCharacterWidths config of
        Nothing -> return ()
        Just widths -> do
            let wMap = buildWidthMap widths
            Vty.installUnicodeWidthTable wMap `E.catch`
                (\(_::Vty.TableInstallException) -> return ())

vtyModes :: Config -> [(Vty.Mode, Bool)]
vtyModes config =
    [ (Vty.BracketedPaste, True)
    , (Vty.Hyperlink, configHyperlinkingMode config)
    , (Vty.Mouse, configMouseMode config)
    ]

vtyBuilder :: Config -> IO Vty.Vty
vtyBuilder config = do
    vty <- Vty.mkVty Vty.defaultConfig
    let output = Vty.outputIface vty

    forM_ (vtyModes config) $ \(mode, val) ->
        Vty.setMode output mode val

    return vty

runMatterhorn :: Options -> Config -> IO ChatState
runMatterhorn opts config = do
    setupCpuUsage config
    setupCharWidthMap config

    let builder = vtyBuilder config

    (st, vty) <- setupState builder (optLogLocation opts) config
    finalSt <- B.customMain vty builder (Just $ st^.csResources.crEventQueue) app st

    case st^.csResources.crSpellChecker of
        Nothing -> return ()
        Just s -> stopAspell s

    return finalSt

-- | Cleanup resources and save data for restoring on program restart.
closeMatterhorn :: ChatState -> IO ()
closeMatterhorn finalSt = do
  logIfError (mmCloseSession $ getResourceSession $ finalSt^.csResources)
      "Error in closing session"

  logIfError (writeHistory (finalSt^.csInputHistory))
      "Error in writing history"

  logIfError (writeLastRunStates finalSt)
      "Error in writing last run states"

  shutdownLogManager $ finalSt^.csResources.crLogManager

  where
    logIfError action msg = do
      done <- runExceptT $ convertIOException action
      case done of
        Left err -> putStrLn $ msg <> ": " <> err
        Right _  -> return ()