{-# LANGUAGE PatternSynonyms #-} -- | -- A @termbox@ program is typically constructed as an infinite loop that: -- -- 1. Renders a scene. -- 2. Polls for an event. -- -- For example, this progam simply displays the number of keys pressed, and -- quits on @Esc@: -- -- @ -- {-\# LANGUAGE LambdaCase \#-} -- -- import qualified Termbox -- -- main :: IO () -- main = -- Termbox.'run' (\\_width _height render poll -> loop render poll 0) -- -- loop :: (Termbox.'Cells' -> Termbox.'Cursor' -> IO ()) -> IO Termbox.'Event' -> Int -> IO () -- loop render poll n = do -- render (string (show n)) Termbox.'NoCursor' -- -- poll >>= \\case -- Termbox.'EventKey' Termbox.'KeyEsc' -> pure () -- _ -> loop render poll (n+1) -- -- string :: Int -> Int -> String -> Termbox.'Cells' -- string col row = -- foldMap (\\(i, c) -> Termbox.'set' (col + i) row (Termbox.'Cell' c 0 0)) . zip [0..] -- @ -- -- Other termbox features include cell attributes (style, color), cursor -- display, and mouse click handling. -- -- This module is intended to be imported qualified. module Termbox ( -- * Initialization run, InitError (..), -- * Terminal contents set, Cells, Cell (..), Cursor (..), -- * Event handling Event (..), Key (..), -- $key-aliases pattern KeyCtrlH, pattern KeyCtrlLsqBracket, pattern KeyCtrl2, pattern KeyCtrl3, pattern KeyCtrl4, pattern KeyCtrl5, pattern KeyCtrl7, pattern KeyCtrlM, pattern KeyCtrlI, pattern KeyCtrlUnderscore, Mouse (..), PollError (..), -- * Attributes Attr, black, red, green, yellow, blue, magenta, cyan, white, bold, underline, reverse, ) where import Control.Exception import Data.Semigroup (Semigroup (..)) import Termbox.Attr ( Attr, black, blue, bold, cyan, green, magenta, red, reverse, underline, white, yellow, ) import Termbox.Cell (Cell (Cell)) import Termbox.Cells (Cells (Cells), set) import Termbox.Event (Event (..), PollError (..), poll) import Termbox.Internal import Termbox.Key ( Key (..), pattern KeyCtrl2, pattern KeyCtrl3, pattern KeyCtrl4, pattern KeyCtrl5, pattern KeyCtrl7, pattern KeyCtrlH, pattern KeyCtrlI, pattern KeyCtrlLsqBracket, pattern KeyCtrlM, pattern KeyCtrlUnderscore, ) import Termbox.Mouse (Mouse (..)) import Prelude hiding (reverse) -- | A cursor. data Cursor = -- | Column, then row Cursor !Int !Int | NoCursor -- $key-aliases -- In a few cases, distinct key sequences map to equivalent key events. The pattern synonyms below are provided for an -- alternate syntax in these cases, if desired. -- | Termbox initialization errors. data InitError = FailedToOpenTTY | PipeTrapError | UnsupportedTerminal deriving (Show) instance Exception InitError -- | Run a @termbox@ program and restore the terminal state afterwards. -- -- The function provided to @run@ is provided: -- -- * The initial terminal width -- * The initial terminal height -- * An action that renders a scene -- * An action that polls for an event indefinitely -- -- /Throws/: 'InitError' run :: (Int -> Int -> (Cells -> Cursor -> IO ()) -> IO Event -> IO a) -> IO a run action = do mask $ \unmask -> do initResult <- tb_init case () of _ | initResult == 0 -> do result <- unmask ( do _ <- tb_select_input_mode tB_INPUT_MOUSE _ <- tb_select_output_mode tB_OUTPUT_256 width <- tb_width height <- tb_height action width height render poll ) `onException` shutdown shutdown pure result _ | initResult == tB_EFAILED_TO_OPEN_TTY -> throwIO FailedToOpenTTY _ | initResult == tB_EPIPE_TRAP_ERROR -> throwIO PipeTrapError _ | initResult == tB_EUNSUPPORTED_TERMINAL -> throwIO UnsupportedTerminal _ -> error ("termbox: unknown tb_init error " ++ show initResult) -- | Render a scene. render :: Cells -> Cursor -> IO () render (Cells cells) cursor = do tb_set_clear_attributes 0 0 tb_clear cells case cursor of Cursor col row -> tb_set_cursor col row NoCursor -> tb_set_cursor tB_HIDE_CURSOR tB_HIDE_CURSOR tb_present shutdown :: IO () shutdown = do _ <- tb_select_output_mode tB_OUTPUT_NORMAL tb_shutdown