    -- | The inline module provides a limited interface to changing the style of terminal output. The
    -- intention is for this interface to be used inline with other output systems.
    --
    -- The changes specified by the InlineM monad are applied to the terminals display attributes. These
    -- display attributes effect the display of all following text output to the terminal file
    -- descriptor.
    --
    -- For example, in an IO monad the following code with print the text \"Not styled. \" Followed by the
    -- text \" Styled! \" drawn over a red background and underlined.
    --
    -- @
    --      t <- terminal_handle
    --      putStr \"Not styled. \"
    --      put_attr_change t $ do
    --          back_color red
    --          apply_style underline
    --      putStr \" Styled! \"
    --      put_attr_change t $ default_all
    --      putStrLn \"Not styled.\"
    --      release_terminal t
    -- @
    --
    -- 'put_attr_change' outputs the control codes to the terminal device 'Handle'. This is a duplicate
    -- of the 'stdout' handle when the 'terminal_handle' was (first) acquired. If 'stdout' has since been
    -- changed then 'putStr', 'putStrLn', 'print' etc.. will output to a different 'Handle' than
    -- 'put_attr_change'
    --
    -- Copyright 2009-2010 Corey O'Connor
    {-# LANGUAGE BangPatterns #-}
    module Graphics.Vty.Inline ( module Graphics.Vty.Inline
                               )
        where

    import Graphics.Vty.Attributes
    import Graphics.Vty.DisplayAttributes
    import Graphics.Vty.Terminal.Generic

    import Control.Applicative
    import Control.Monad.State.Strict

    import Data.Bits ( (.&.), complement )
    import Data.IORef
    import Data.Monoid ( mappend )

    import System.IO

    type InlineM v = State Attr v

    -- | Set the background color to the provided 'Color'
    back_color :: Color -> InlineM ()
    back_color c = modify $ flip mappend ( current_attr `with_back_color` c )

    -- | Set the foreground color to the provided 'Color'
    fore_color :: Color -> InlineM ()
    fore_color c = modify $ flip mappend ( current_attr `with_fore_color` c )

    -- | Attempt to change the 'Style' of the following text.
    --
    -- If the terminal does not support the style change no error is produced. The style can still be
    -- removed.
    apply_style :: Style -> InlineM ()
    apply_style s = modify $ flip mappend ( current_attr `with_style` s )

    -- | Attempt to remove the specified 'Style' from the display of the following text.
    --
    -- This will fail if apply_style for the given style has not been previously called.
    remove_style :: Style -> InlineM ()
    remove_style s_mask = modify $ \attr ->
        let style' = case attr_style attr of
                        Default -> error $ "Graphics.Vty.Inline: Cannot remove_style if apply_style never used."
                        KeepCurrent -> error $ "Graphics.Vty.Inline: Cannot remove_style if apply_style never used."
                        SetTo s -> s .&. complement s_mask
        in attr { attr_style = SetTo style' }

    -- | Reset the display attributes
    default_all :: InlineM ()
    default_all = put def_attr

    -- | Apply the provided display attribute changes to the terminal.
    --
    -- This also flushes the 'stdout' handle.
    put_attr_change :: ( Applicative m, MonadIO m ) => TerminalHandle -> InlineM () -> m ()
    put_attr_change t c = do
        bounds <- display_bounds t
        d <- display_context t bounds
        mfattr <- liftIO $ known_fattr <$> readIORef ( state_ref t )
        fattr <- case mfattr of
                    Nothing -> do
                        liftIO $ marshall_to_terminal t (default_attr_required_bytes d) (serialize_default_attr d)
                        return $ FixedAttr default_style_mask Nothing Nothing
                    Just v -> return v
        let attr = execState c current_attr
            attr' = limit_attr_for_display d attr
            fattr' = fix_display_attr fattr attr'
            diffs = display_attr_diffs fattr fattr'
        liftIO $ hFlush stdout
        liftIO $ marshall_to_terminal t ( attr_required_bytes d fattr attr' diffs )
                                        ( serialize_set_attr d fattr attr' diffs )
        liftIO $ modifyIORef ( state_ref t ) $ \s -> s { known_fattr = Just fattr' }
        inline_hack d
        liftIO $ hFlush stdout