summaryrefslogtreecommitdiff
path: root/src/Main.hs
blob: 8bd2729c09844c038f7083588eaa176cc92ca30a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
-- Copyright © 2015 Benjamin Barenblat
--
-- Licensed under the Apache License, Version 2.0 (the "License"); you may not
-- use this file except in compliance with the License.  You may obtain a copy
-- of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-- License for the specific language governing permissions and limitations under
-- the License.

module Main (main) where

import Prelude hiding (lines, log)

import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.Chan
import Control.Monad (forever, void)
import Data.Default.Class (def)
import Data.IORef
import Graphics.Vty (Vty)
import qualified Graphics.Vty as Vty

import Flag (Flag)
import qualified Flag

main :: IO ()
main = do
  terminal <- Vty.mkVty def
  initialize terminal
  jarvis terminal
  Vty.shutdown terminal

initialize :: Vty -> IO ()
initialize _ = return ()

jarvis :: Vty -> IO ()
jarvis terminal = do
  log <- newIORef Vty.emptyImage
  prompt <- newIORef Vty.emptyImage
  promptInput <- newChan
  ready <- Flag.new
  void $ forkIO $ redrawer ready log prompt terminal
  void $ forkIO $ prompter promptInput prompt ready
  void $ forkIO $ logger log ready
  processEvents terminal promptInput

processEvents :: Vty -> Chan Vty.Event -> IO ()
processEvents terminal promptInput = do
  event <- Vty.nextEvent terminal
  case event of
    Vty.EvKey (Vty.KChar 'd') [Vty.MCtrl] -> return ()
    Vty.EvKey (Vty.KChar _) _ -> writeChan promptInput event >> continue
    _ -> continue
  where continue = processEvents terminal promptInput

prompter :: Chan Vty.Event -> IORef Vty.Image -> Flag -> IO ()
prompter input output redraw = do
  let promptString = "> "
  writeIORef output $ Vty.string def promptString
  Flag.wave redraw
  readCommand ""
  where readCommand command = do
          event <- readChan input
          case event of
            Vty.EvKey (Vty.KChar c) _ -> do
              let command' = command ++ [c]
              writeIORef output $ Vty.string def $ "> " ++ command'
              Flag.wave redraw
              readCommand command'
            _ -> readCommand command

logger :: IORef Vty.Image -> Flag -> IO ()
logger output redraw = log 0 []
  where log :: Integer -> [String] -> IO ()
        log n lines = do
          let lines' = lines ++ ["<" ++ show n ++ ">"]
          writeIORef output $ Vty.vertCat $ map (Vty.string def) lines'
          Flag.wave redraw
          threadDelay 200000
          log (n + 1) lines'

redrawer :: Flag -> IORef Vty.Image -> IORef Vty.Image -> Vty -> IO ()
redrawer ready log prompt terminal = forever $ do
  Flag.wait ready
  (_maxX, maxY) <- Vty.displayBounds $ Vty.outputIface terminal
  images <- sequence $ map readIORef [log, prompt]
  let stack = resizeHeight' maxY $ Vty.vertCat images
  Vty.update terminal $ Vty.picForImage stack
  Vty.refresh terminal

resizeHeight' :: Int -> Vty.Image -> Vty.Image
resizeHeight' h i = case h `compare` Vty.imageHeight i of
    LT -> Vty.cropTop h i
    EQ -> i
    GT -> Vty.translateY (h - Vty.imageHeight i) i