packages feed

LambdaHack-0.2.10: Game/LambdaHack/Frontend/Vty.hs

-- | Text frontend based on Vty.
module Game.LambdaHack.Frontend.Vty
  ( -- * Session data type for the frontend
    FrontendSession
    -- * The output and input operations
  , fdisplay, fpromptGetKey
    -- * Frontend administration tools
  , frontendName, startup
  ) where

import qualified Data.List as L
import Data.Text.Encoding (encodeUtf8)
import Graphics.Vty
import qualified Graphics.Vty as Vty

import Game.LambdaHack.Common.Animation (DebugModeCli (..), SingleFrame (..))
import qualified Game.LambdaHack.Common.Color as Color
import qualified Game.LambdaHack.Common.Key as K

-- | Session data maintained by the frontend.
data FrontendSession = FrontendSession
  { svty      :: !Vty  -- internal vty session
  , sdebugCli :: !DebugModeCli  -- ^ client configuration
      -- ^ Configuration of the frontend session.
  }

-- | The name of the frontend.
frontendName :: String
frontendName = "vty"

-- | Starts the main program loop using the frontend input and output.
startup :: DebugModeCli -> (FrontendSession -> IO ()) -> IO ()
startup sdebugCli k = do
  svty <- mkVty
  k FrontendSession{..}
  Vty.shutdown svty

-- | Output to the screen via the frontend.
fdisplay :: FrontendSession    -- ^ frontend session data
         -> Bool
         -> Maybe SingleFrame  -- ^ the screen frame to draw
         -> IO ()
fdisplay _ _ Nothing = return ()
fdisplay FrontendSession{svty} _ (Just SingleFrame{..}) =
  let img = (foldr (<->) empty_image
             . L.map (foldr (<|>) empty_image
                      . L.map (\ Color.AttrChar{..} ->
                                char (setAttr acAttr) acChar)))
            sfLevel
      pic = pic_for_image $
              utf8_bytestring (setAttr Color.defAttr) (encodeUtf8 sfTop)
              <-> img <->
              utf8_bytestring (setAttr Color.defAttr) (encodeUtf8 sfBottom)
  in update svty pic

-- | Input key via the frontend.
nextEvent :: FrontendSession -> IO K.KM
nextEvent sess@FrontendSession{svty, sdebugCli=DebugModeCli{snoMore}} =
  if snoMore then return K.escKey
  else do
    e <- next_event svty
    case e of
      EvKey n mods -> do
        let key = keyTranslate n
            modifier = modifierTranslate mods
        return K.KM {key, modifier}
      _ -> nextEvent sess

-- | Display a prompt, wait for any key.
fpromptGetKey :: FrontendSession -> SingleFrame -> IO K.KM
fpromptGetKey sess frame = do
  fdisplay sess True $ Just frame
  nextEvent sess

keyTranslate :: Key -> K.Key
keyTranslate n =
  case n of
    KEsc          -> K.Esc
    KEnter        -> K.Return
    (KASCII ' ')  -> K.Space
    (KASCII '\t') -> K.Tab
    KBackTab      -> K.BackTab
    KUp           -> K.Up
    KDown         -> K.Down
    KLeft         -> K.Left
    KRight        -> K.Right
    KHome         -> K.Home
    KPageUp       -> K.PgUp
    KEnd          -> K.End
    KPageDown     -> K.PgDn
    KBegin        -> K.Begin
    (KASCII c)    -> K.Char c
    _             -> K.Unknown (show n)

-- | Translates modifiers to our own encoding.
modifierTranslate :: [Modifier] -> K.Modifier
modifierTranslate mods =
  if MCtrl `elem` mods then K.Control else K.NoModifier

-- A hack to get bright colors via the bold attribute. Depending on terminal
-- settings this is needed or not and the characters really get bold or not.
-- HSCurses does this by default, but in Vty you have to request the hack.
hack :: Color.Color -> Attr -> Attr
hack c a = if Color.isBright c then with_style a bold else a

setAttr :: Color.Attr -> Attr
setAttr Color.Attr{fg, bg} =
-- This optimization breaks display for white background terminals:
--  if (fg, bg) == Color.defAttr
--  then def_attr
--  else
  hack fg $ hack bg $
    def_attr { attr_fore_color = SetTo (aToc fg)
             , attr_back_color = SetTo (aToc bg) }

aToc :: Color.Color -> Color
aToc Color.Black     = black
aToc Color.Red       = red
aToc Color.Green     = green
aToc Color.Brown     = yellow
aToc Color.Blue      = blue
aToc Color.Magenta   = magenta
aToc Color.Cyan      = cyan
aToc Color.White     = white
aToc Color.BrBlack   = bright_black
aToc Color.BrRed     = bright_red
aToc Color.BrGreen   = bright_green
aToc Color.BrYellow  = bright_yellow
aToc Color.BrBlue    = bright_blue
aToc Color.BrMagenta = bright_magenta
aToc Color.BrCyan    = bright_cyan
aToc Color.BrWhite   = bright_white