diff --git a/src/Codec/Binary/UTF8/Width.hs b/src/Codec/Binary/UTF8/Width.hs
--- a/src/Codec/Binary/UTF8/Width.hs
+++ b/src/Codec/Binary/UTF8/Width.hs
@@ -11,16 +11,12 @@
 import Foreign.Storable
 import Foreign.Ptr
 
--- import Numeric ( showHex )
-
 import System.IO.Unsafe
 
 wcwidth :: Char -> Int
 wcwidth c = unsafePerformIO (withCWString [c] $! \ws -> do
     wc <- peek ws
-    -- putStr $ "wcwidth(0x" ++ showHex (fromEnum wc) "" ++ ")"
     let !w = fromIntegral $! wcwidth' wc
-    -- putStrLn $ " -> " ++ show w
     return w
     )
 {-# NOINLINE wcwidth #-}
@@ -29,9 +25,7 @@
 
 wcswidth :: String -> Int
 wcswidth str = unsafePerformIO (withCWStringLen str $! \(ws, ws_len) -> do
-    -- putStr $ "wcswidth(...)"
     let !w = fromIntegral $! wcswidth' ws (fromIntegral ws_len)
-    -- putStrLn $ " -> " ++ show w
     return w
     )
 {-# NOINLINE wcswidth #-}
diff --git a/src/Data/Terminfo/Eval.hs b/src/Data/Terminfo/Eval.hs
--- a/src/Data/Terminfo/Eval.hs
+++ b/src/Data/Terminfo/Eval.hs
@@ -28,6 +28,7 @@
 import GHC.Prim
 import GHC.Word
 
+-- | capability evaluator state
 data EvalState = EvalState
     { eval_stack :: ![ CapParam ]
     , eval_expression :: !CapExpression
diff --git a/src/Graphics/Vty/Attributes.hs b/src/Graphics/Vty/Attributes.hs
--- a/src/Graphics/Vty/Attributes.hs
+++ b/src/Graphics/Vty/Attributes.hs
@@ -1,12 +1,11 @@
 {-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE RankNTypes #-}
--- Copyright 2009-2010 Corey O'Connor
--- Display attributes
+-- | Display attributes
 --
--- For efficiency this can be, in the future, encoded into a single 32 bit word. The 32 bit word is
--- first divided into 4 groups of 8 bits where:
---  The first group codes what action should be taken with regards to the other groups.
+-- For efficiency, this could be encoded into a single 32 bit word. The 32 bit word is first divided
+-- into 4 groups of 8 bits where: The first group codes what action should be taken with regards to
+-- the other groups.
 --      XXYYZZ__
 --      XX - style action
 --          00 => reset to default
diff --git a/src/Graphics/Vty/DisplayAttributes.hs b/src/Graphics/Vty/DisplayAttributes.hs
--- a/src/Graphics/Vty/DisplayAttributes.hs
+++ b/src/Graphics/Vty/DisplayAttributes.hs
@@ -25,6 +25,12 @@
         fix_color c KeepCurrent = c
         fix_color _c (SetTo c) = Just c
 
+-- | difference between two display attributes. Used in the calculation of the operations required
+-- to go from one display attribute to the next.
+--
+-- Previously, vty would reset display attributes to default then apply the new display attributes.
+-- This turned out to be very expensive: A *lot* more data would be sent to the terminal than
+-- required.
 data DisplayAttrDiff = DisplayAttrDiff
     { style_diffs :: [ StyleStateChange ]
     , fore_color_diff :: DisplayColorDiff
@@ -40,20 +46,30 @@
             bcd = simplify_color_diffs ( back_color_diff d_0 ) ( back_color_diff d_1 )
         in DisplayAttrDiff ds fcd bcd
 
+-- | Used in the computation of a final style attribute change.
+--
+-- TODO(corey): not really a simplify but a monoid instance.
 simplify_style_diffs :: [ StyleStateChange ] -> [ StyleStateChange ] -> [ StyleStateChange ]
 simplify_style_diffs cs_0 cs_1 = cs_0 `mappend` cs_1
 
+-- | Consider two display color attributes diffs. What display color attribute diff are these
+-- equivalent to?
+--
+-- TODO(corey): not really a simplify but a monoid instance.
 simplify_color_diffs :: DisplayColorDiff -> DisplayColorDiff -> DisplayColorDiff
 simplify_color_diffs _cd             ColorToDefault  = ColorToDefault
 simplify_color_diffs cd              NoColorChange   = cd
 simplify_color_diffs _cd             ( SetColor !c ) = SetColor c
 
+-- | Difference between two display color attribute changes.
 data DisplayColorDiff 
     = ColorToDefault
     | NoColorChange
     | SetColor !Color
     deriving ( Show, Eq )
 
+-- | Style attribute changes are transformed into a sequence of apply/removes of the individual
+-- attributes.
 data StyleStateChange 
     = ApplyStandout
     | RemoveStandout
@@ -69,6 +85,8 @@
     | RemoveBold
     deriving ( Show, Eq )
 
+-- | Determines the diff between two display&color attributes. This diff determines the operations
+-- that actually get output to the terminal.
 display_attr_diffs :: FixedAttr -> FixedAttr -> DisplayAttrDiff
 display_attr_diffs attr attr' = DisplayAttrDiff
     { style_diffs = diff_styles ( fixed_style attr ) ( fixed_style attr' )
diff --git a/src/Graphics/Vty/DisplayRegion.hs b/src/Graphics/Vty/DisplayRegion.hs
--- a/src/Graphics/Vty/DisplayRegion.hs
+++ b/src/Graphics/Vty/DisplayRegion.hs
@@ -4,6 +4,7 @@
 
 import Data.Word
 
+-- | Region of the terminal that vty will output to. Units are columns not characters.
 data DisplayRegion = DisplayRegion 
     { region_width :: !Word 
     , region_height :: !Word
diff --git a/src/Graphics/Vty/Picture.hs b/src/Graphics/Vty/Picture.hs
--- a/src/Graphics/Vty/Picture.hs
+++ b/src/Graphics/Vty/Picture.hs
@@ -28,7 +28,7 @@
 
 import Data.Word
 
--- |The type of images to be displayed using 'update'.  
+-- | The type of images to be displayed using 'update'.  
 -- Can be constructed directly or using `pic_for_image`. Which provides an initial instance with
 -- reasonable defaults for pic_cursor and pic_background.
 data Picture = Picture
@@ -66,10 +66,10 @@
 -- be used for a background fill then use `current_attr` for the background attribute. This is the
 -- default background display attribute.
 --
--- (tofix) The current attribute is always set to the default attributes at the start of updating the
+-- \todo The current attribute is always set to the default attributes at the start of updating the
 -- screen to a picture.
 --
--- (tofix) The background character *must* occupy a single column and no more.  
+-- \todo The background character *must* occupy a single column and no more.
 data Background = Background 
     { background_char :: Char 
     , background_attr :: Attr
diff --git a/src/Graphics/Vty/Span.hs b/src/Graphics/Vty/Span.hs
--- a/src/Graphics/Vty/Span.hs
+++ b/src/Graphics/Vty/Span.hs
@@ -16,7 +16,8 @@
 import Codec.Binary.UTF8.String ( encode )
 
 import Control.Monad ( forM_ )
-import Control.Monad.ST.Strict
+import Control.Monad.ST.Strict hiding ( unsafeIOToST )
+import Control.Monad.ST.Unsafe ( unsafeIOToST )
 
 import Data.Vector (Vector)
 import qualified Data.Vector as Vector hiding ( take, replicate )
@@ -48,12 +49,15 @@
     , display_ops :: RowOps
     }
 
--- vector of span operation vectors. One per row of the screen.
+-- | vector of span operation vectors. One per row of the screen.
 type RowOps = Vector SpanOps
 
 type MRowOps s = MVector s SpanOps
 
--- vector of span operations. executed in succession
+-- | vector of span operations. executed in succession. This represents the operations required to
+-- render a row of the terminal. The operations in one row may effect subsequent rows.
+-- EG: Setting the foreground color in one row will effect all subsequent rows until the foreground
+-- color is changed.
 type SpanOps = Vector SpanOp
 
 type MSpanOps s = MVector s SpanOp
@@ -66,19 +70,23 @@
     show (AttributeChange attr) = show attr
     show (TextSpan ow cw _) = "TextSpan " ++ show ow ++ " " ++ show cw
 
+-- | Number of columns the DisplayOps are defined for
 span_ops_columns :: DisplayOps -> Word
 span_ops_columns ops = region_width $ effected_region ops
 
+-- | Number of rows the DisplayOps are defined for
 span_ops_rows :: DisplayOps -> Word
 span_ops_rows ops = region_height $ effected_region ops
 
+-- | The number of columns a SpanOps effects.
 span_ops_effected_columns :: SpanOps -> Word
 span_ops_effected_columns in_ops = Vector.foldl' span_ops_effected_columns' 0 in_ops
     where 
         span_ops_effected_columns' t (TextSpan w _ _ ) = t + w
         span_ops_effected_columns' t _ = t
 
--- |
+-- | This represents an operation on the terminal. Either an attribute change or the output of a
+-- text string.
 -- 
 -- todo: This type may need to be restructured to increase sharing in the bytestring
 -- 
@@ -91,12 +99,12 @@
     | TextSpan !Word !Word (UTF8.UTF8 B.ByteString)
     deriving Eq
 
--- used to determine the width of a span operation , if it has one. 
+-- | The width of a single SpanOp in columns
 span_op_has_width :: SpanOp -> Maybe (Word, Word)
 span_op_has_width (TextSpan ow cw _) = Just (cw, ow)
 span_op_has_width _ = Nothing
 
--- returns the number of columns to the character at the given position in the span op
+-- | returns the number of columns to the character at the given position in the span op
 columns_to_char_offset :: Word -> SpanOp -> Word
 columns_to_char_offset cx (TextSpan _ _ utf8_str) =
     let str = UTF8.toString utf8_str
@@ -108,11 +116,14 @@
 spans_for_pic :: Picture -> DisplayRegion -> DisplayOps
 spans_for_pic pic r = DisplayOps r $ Vector.create (build_spans pic r)
 
+-- | Builds a vector of row operations that will output the given picture to the terminal.
+--
+-- Crops to the given display region.
 build_spans :: Picture -> DisplayRegion -> ST s (MRowOps s)
 build_spans pic region = do
-    -- m for mutable! ;-)
+    -- First we create a mutable vector for each rows output operations.
     mrow_ops <- Vector.replicate (fromEnum $ region_height region) Vector.empty
-    -- XXX: I think building the span operations in display order would provide better performance.
+    -- \todo I think building the span operations in display order would provide better performance.
     -- However, I got stuck trying to implement an algorithm that did this. This will be considered
     -- as a possible future optimization. 
     --
@@ -146,6 +157,7 @@
         else return ()
     return mrow_ops
 
+-- | Add the operations required to build a given image to the current set of row operations.
 row_ops_for_image :: MRowOps s -> Image -> Background -> DisplayRegion -> (Word, Word) -> Int -> Word -> Int -> ST s (Word, Word)
 row_ops_for_image mrow_ops                      -- the image to output the ops to
                   image                         -- the image to rasterize in column order to mrow_ops
@@ -305,6 +317,11 @@
                                       , used_char_count
                                       )
 
+-- | Add a background fill of the given column width to the row display operations.
+--
+-- This has a fast path for background characters that are a single column and a single byte.
+-- Otherwise this has to compute the width of the background character and replicate a sequence of
+-- bytes to fill in the required width.
 snoc_bg_fill :: MRowOps s -> Background -> Word -> Int -> ST s ()
 snoc_bg_fill _row_ops _bg 0 _row 
     = return ()
@@ -328,6 +345,7 @@
                                                 $ zip [0 .. fromEnum (fill_length - 1)] (cycle c_bytes)
         snoc_op mrow_ops row $ TextSpan fill_length fill_length (UTF8.fromRep utf8_bs)
 
+-- | snocs the operation to the operations for the given row.
 snoc_op :: MRowOps s -> Int -> SpanOp -> ST s ()
 snoc_op !mrow_ops !row !op = do
     ops <- Vector.read mrow_ops row
diff --git a/src/Graphics/Vty/Terminal/Generic.hs b/src/Graphics/Vty/Terminal/Generic.hs
--- a/src/Graphics/Vty/Terminal/Generic.hs
+++ b/src/Graphics/Vty/Terminal/Generic.hs
@@ -28,6 +28,7 @@
 
 import System.IO
 
+-- | An handle to a terminal that hides the implementation.
 data TerminalHandle where
     TerminalHandle :: Terminal t => t -> IORef TerminalState -> TerminalHandle
 
@@ -39,11 +40,12 @@
     s_ref <- liftIO $ newIORef initial_terminal_state
     return $ TerminalHandle t s_ref
 
+-- | The current terminal state. This may not exactly be known.
 data TerminalState = TerminalState
-    { -- | The current terminal display attributes or Nothing if they are not known.
-      known_fattr :: Maybe FixedAttr
+    { known_fattr :: Maybe FixedAttr
     }
 
+-- | Initially we know nothing about a terminal's state.
 initial_terminal_state :: TerminalState
 initial_terminal_state = TerminalState Nothing
 
@@ -352,6 +354,8 @@
     free start_ptr
     return ()
 
+-- | The cursor position is given in X,Y character offsets. Due to multi-column characters this
+-- needs to be translated to column, row positions.
 data CursorOutputMap = CursorOutputMap
     { char_to_output_pos :: (Word, Word) -> (Word, Word)
     } 
@@ -386,6 +390,8 @@
                       cursor_row_ops
     in out_offset
 
+-- | Not all terminals support all display attributes. This filters a display attribute to what the
+-- given terminal can display.
 limit_attr_for_display :: DisplayTerminal d => d -> Attr -> Attr
 limit_attr_for_display d attr 
     = attr { attr_fore_color = clamp_color $ attr_fore_color attr
diff --git a/src/Graphics/Vty/Terminal/MacOSX.hs b/src/Graphics/Vty/Terminal/MacOSX.hs
--- a/src/Graphics/Vty/Terminal/MacOSX.hs
+++ b/src/Graphics/Vty/Terminal/MacOSX.hs
@@ -22,12 +22,16 @@
 
 import System.IO
 
+-- | A Mac terminal is assumed to be an xterm based terminal.
 data Term = Term 
     { super_term :: TerminalHandle
     , term_app :: String
     }
 
--- for Terminal.app use "xterm". For iTerm.app use "xterm-256color"
+-- | for Terminal.app the terminal identifier "xterm" is used. For iTerm.app the terminal identifier
+-- "xterm-256color" is used.
+--
+-- This effects the terminfo lookup.
 terminal_instance :: ( Applicative m, MonadIO m ) => String -> m Term
 terminal_instance v = do
     let base_term "iTerm.app" = "xterm-256color"
@@ -40,12 +44,13 @@
     liftIO $ hPutStr stdout str
     liftIO $ hFlush stdout
 
--- Terminal.app really does want the xterm-color smcup and rmcup caps. Not the generic xterm ones.
+-- | Terminal.app requires the xterm-color smcup and rmcup caps. Not the generic xterm ones.
+-- Otherwise, Terminal.app expects the xterm caps.
 smcup_str, rmcup_str :: String
 smcup_str = "\ESC7\ESC[?47h"
 rmcup_str = "\ESC[2J\ESC[?47l\ESC8"
 
--- iTerm needs a clear screen after smcup as well?
+-- | iTerm needs a clear screen after smcup as well.
 clear_screen_str :: String
 clear_screen_str = "\ESC[H\ESC[2J"
 
diff --git a/src/Graphics/Vty/Terminal/TerminfoBased.hs b/src/Graphics/Vty/Terminal/TerminfoBased.hs
--- a/src/Graphics/Vty/Terminal/TerminfoBased.hs
+++ b/src/Graphics/Vty/Terminal/TerminfoBased.hs
@@ -215,7 +215,7 @@
     serialize_hide_cursor d out_ptr 
         = liftIO $ serialize_cap_expression (civis $ term d) [] out_ptr
 
-    -- Instead of evaluating all the rules related to setting display attributes twice (once in
+    -- | Instead of evaluating all the rules related to setting display attributes twice (once in
     -- required bytes and again in serialize) or some memoization scheme just return a size
     -- requirement as big the longest possible control string.
     -- 
@@ -224,14 +224,16 @@
     -- \todo Not verified as safe and wastes memory.
     attr_required_bytes _d _prev_attr _req_attr _diffs = 512
 
-    -- Portably setting the display attributes is a giant pain in the ass.
+    -- | Portably setting the display attributes is a giant pain in the ass.
+    --
     -- If the terminal supports the sgr capability (which sets the on/off state of each style
-    -- directly ; and, for no good reason, resets the colors to the default) this always works:
+    -- directly ; and, for no good reason, resets the colors to the default) this procedure is used: 
+    --
     --  0. set the style attributes. This resets the fore and back color.
     --  1, If a foreground color is to be set then set the foreground color
     --  2. likewise with the background color
     -- 
-    -- If the terminal does not support the sgr then 
+    -- If the terminal does not support the sgr cap then:
     --  if there is a change from an applied color to the default (in either the fore or back color)
     --  then:
     --      0. reset all display attributes (sgr0)
@@ -242,7 +244,7 @@
     -- Entering the required style modes could require a reset of the display attributes. If this is
     -- the case then the back and fore colors always need to be set if not default.
     --
-    -- All this (I think) is satisfied by the following logic:
+    -- This equation implements the above logic.
     serialize_set_attr d prev_attr req_attr diffs out_ptr = do
         case (fore_color_diff diffs == ColorToDefault) || (back_color_diff diffs == ColorToDefault) of
             -- The only way to reset either color, portably, to the default is to use either the set
@@ -321,11 +323,16 @@
     serialize_default_attr d out_ptr = do
         liftIO $ serialize_cap_expression ( set_default_attr $ term d ) [] out_ptr
 
+-- | The color table used by a terminal is a 16 color set followed by a 240 color set that might not
+-- be supported by the terminal.
+--
+-- This takes a Color which clearly identifies which pallete to use and computes the index
+-- into the full 256 color pallete.
 ansi_color_index :: Color -> Word
 ansi_color_index (ISOColor v) = toEnum $ fromEnum v
 ansi_color_index (Color240 v) = 16 + ( toEnum $ fromEnum v )
 
-{- The sequence of terminfo caps to apply a given style are determined according to these rules.
+{- | The sequence of terminfo caps to apply a given style are determined according to these rules.
  -
  -  1. The assumption is that it's preferable to use the simpler enter/exit mode capabilities than
  -  the full set display attribute state capability. 
@@ -340,7 +347,6 @@
  -  apply/remove.
  -
  -}
-
 data DisplayAttrSeq
     = EnterExitSeq [CapExpression]
     | SetState DisplayAttrState
diff --git a/src/Graphics/Vty/Terminal/XTermColor.hs b/src/Graphics/Vty/Terminal/XTermColor.hs
--- a/src/Graphics/Vty/Terminal/XTermColor.hs
+++ b/src/Graphics/Vty/Terminal/XTermColor.hs
@@ -18,13 +18,16 @@
 
 import System.IO
 
+-- | An xterm color based terminal is some variant paired with a standard TerminfoBased terminal
+-- interface.
+--
+-- For the most part, xterm terminals just use the TerminfoBased implementation.
 data XTermColor = XTermColor 
     { xterm_variant :: String
     , super_term :: TerminalHandle
     }
 
--- Initialize the display to UTF-8
--- Regardless of what is output the text encoding is assumed to be UTF-8
+-- | Initialize the display to UTF-8. 
 terminal_instance :: ( Applicative m, MonadIO m ) => String -> m XTermColor
 terminal_instance variant = do
     -- If the terminal variant is xterm-color use xterm instead since, more often than not,
@@ -34,12 +37,17 @@
     t <- TerminfoBased.terminal_instance variant' >>= new_terminal_handle
     return $ XTermColor variant' t
 
+-- | Output immediately followed by a flush.
+--
+-- \todo move out of this module.
 flushed_put :: MonadIO m => String -> m ()
 flushed_put str = do
     liftIO $ hPutStr stdout str
     liftIO $ hFlush stdout
 
--- Since I don't know of a terminfo string cap that produces these strings these are hardcoded.
+-- | These sequences set xterm based terminals to UTF-8 output.
+--
+-- \todo I don't know of a terminfo cap that is equivalent to this.
 set_utf8_char_set, set_default_char_set :: String
 set_utf8_char_set = "\ESC%G"
 set_default_char_set = "\ESC%@"
@@ -89,9 +97,9 @@
     default_attr_required_bytes d = default_attr_required_bytes (super_display d)
     serialize_default_attr d = serialize_default_attr (super_display d)
 
-    -- I think xterm is broken: Reseting the background color as the first bytes serialized on a new
-    -- line does not effect the background color xterm uses to clear the line. Which is used *after*
-    -- the next newline.
+    -- | I think xterm is broken: Reseting the background color as the first bytes serialized on a
+    -- new line does not effect the background color xterm uses to clear the line. Which is used
+    -- *after* the next newline.
     inline_hack d = do
         let t = case super_display d of
                     DisplayHandle _ t_ _ -> t_
diff --git a/test/interactive_terminal_test.hs b/test/interactive_terminal_test.hs
new file mode 100644
--- /dev/null
+++ b/test/interactive_terminal_test.hs
@@ -0,0 +1,953 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Main where
+
+import Graphics.Vty
+import Graphics.Vty.Attributes
+import Graphics.Vty.Inline
+import Graphics.Vty.Picture
+import Graphics.Vty.Terminal
+import Graphics.Vty.DisplayRegion
+
+import Control.Exception
+import Control.Monad
+
+import Data.List ( lookup )
+import Data.Maybe ( isJust, fromJust )
+import Data.Monoid
+import Data.String.QQ
+import Data.Word
+
+import Foreign.Marshal.Array 
+
+import qualified System.Environment as Env
+
+import System.IO ( hFlush, hPutStr, hPutBuf, stdout )
+
+main = do
+    print_intro
+
+output_file_path = "test_results.list"
+
+print_intro = do
+    putStr $ [s| 
+This is an interactive verification program for the terminal input and output
+support of the VTY library. This will ask a series of questions about what you
+see on screen. The goal is to verify that VTY's output and input support
+performs as expected with your terminal.
+
+This program produces a file named 
+    |] ++ output_file_path ++ [s| 
+in the current directory that contains the results for each test assertion. This
+can  be emailed to coreyoconnor@gmail.com and used by the VTY authors to improve
+support for your terminal. No personal information is contained in the report.
+
+Each test follows, more or less, the following format:
+    0. A description of the test is printed which will include a detailed
+    description of what VTY is going to try and what the expected results are.
+    Press return to move on.
+    1. The program will produce some output or ask for you to press a key.
+    2. You will then be asked to confirm if the behavior matched the provided
+    description.  Just pressing enter implies the default response that
+    everything was as expected. 
+
+All the tests assume the following about the terminal display:
+    0. The terminal display will not be resized during a test and is at least 80 
+    characters in width. 
+    1. The terminal display is using a monospaced font for both single width and
+    double width characters.
+    2. A double width character is displayed with exactly twice the width of a 
+    single column character. This may require adjusting the font used by the
+    terminal. At least, that is the case using xterm. 
+    3. Fonts are installed, and usable by the terminal, that define glyphs for
+    a good range of the unicode characters. Each test involving unicode display
+    describes the expected appearance of each glyph. 
+
+Thanks for the help! :-D
+To exit the test early enter "q" anytime at the following menu screen.
+
+If any test failed then please post an issue to
+    https://github.com/coreyoconnor/vty/issues
+with the test_results.list file pasted into the issue. The issue summary can
+mention the specific tests that failed or just say "interactive terminal test
+failure".
+|]
+    wait_for_return
+    results <- do_test_menu 1
+    env_attributes <- mapM ( \env_name -> catch ( Env.getEnv env_name >>= return . (,) env_name ) 
+                                                ( \ (_ :: SomeException) -> return (env_name, "") ) 
+                           ) 
+                           [ "TERM", "COLORTERM", "LANG", "TERM_PROGRAM", "XTERM_VERSION" ]
+    t <- terminal_handle
+    let results_txt = show env_attributes ++ "\n" 
+                      ++ terminal_ID t ++ "\n"
+                      ++ show results ++ "\n"
+    release_terminal t
+    writeFile output_file_path results_txt
+
+wait_for_return = do
+    putStr "\n(press return to continue)"
+    hFlush stdout
+    getLine
+
+test_menu :: [(String, Test)]
+test_menu = zip (map show [1..]) all_tests
+
+do_test_menu :: Int -> IO [(String, Bool)]
+do_test_menu next_ID 
+    | next_ID > length all_tests = do
+        putStrLn $ "Done! Please email the " ++ output_file_path ++ " file to coreyoconnor@gmail.com"
+        return []
+    | otherwise = do
+        display_test_menu
+        putStrLn $ "Press return to start with #" ++ show next_ID ++ "."
+        putStrLn "Enter a test number to perform only that test."
+        putStrLn "q (or control-C) to quit."
+        putStr "> "
+        hFlush stdout
+        s <- getLine >>= return . filter (/= '\n')
+        case s of
+            "q" -> return mempty
+            "" -> do 
+                r <- run_test $ show next_ID 
+                rs <- do_test_menu ( next_ID + 1 )
+                return $ r : rs
+            i | isJust ( lookup i test_menu ) -> do
+                r <- run_test i 
+                rs <- do_test_menu ( read i + 1 )
+                return $ r : rs
+        where
+            display_test_menu 
+                = mapM_ display_test_menu' test_menu
+            display_test_menu' ( i, t ) 
+                = putStrLn $ ( if i == show next_ID 
+                                then "> " 
+                                else "  "
+                             ) ++ i ++ ". " ++ test_name t
+
+run_test :: String -> IO (String, Bool)
+run_test i = do
+    let t = fromJust $ lookup i test_menu 
+    print_summary t
+    wait_for_return
+    test_action t
+    r <- confirm_results t
+    return (test_ID t, r)
+
+default_success_confirm_results = do
+    putStr "\n"
+    putStr "[Y/n] "
+    hFlush stdout
+    r <- getLine
+    case r of
+        "" -> return True
+        "y" -> return True
+        "Y" -> return True
+        "n" -> return False
+
+data Test = Test
+    { test_name :: String
+    , test_ID :: String
+    , test_action :: IO ()
+    , print_summary :: IO ()
+    , confirm_results :: IO Bool
+    }
+
+all_tests 
+    = [ reserve_output_test 
+      , display_bounds_test_0
+      , display_bounds_test_1
+      , display_bounds_test_2
+      , display_bounds_test_3
+      , unicode_single_width_0
+      , unicode_single_width_1
+      , unicode_double_width_0
+      , unicode_double_width_1
+      , attributes_test_0
+      , attributes_test_1
+      , attributes_test_2
+      , attributes_test_3
+      , attributes_test_4
+      , attributes_test_5
+      , inline_test_0
+      , inline_test_1
+      , inline_test_2
+      , cursor_hide_test_0
+      ]
+
+reserve_output_test = Test 
+    { test_name = "Initialize and reserve terminal output then restore previous state."
+    , test_ID = "reserve_output_test"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        putStrLn "Line 1"
+        putStrLn "Line 2"
+        putStrLn "Line 3"
+        putStrLn "Line 4 (press return)"
+        hFlush stdout
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared. 
+    1. Four lines of text should be visible.
+    1. The cursor should be visible and at the start of the fifth line.
+
+After return is pressed for the second time this test then:
+    * The screen containing the test summary should be restored;
+    * The cursor is visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+display_bounds_test_0 = Test
+    { test_name = "Verify display bounds are correct test 0: Using spaces."
+    , test_ID = "display_bounds_test_0"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        DisplayRegion w h <- display_bounds t
+        let row_0 = replicate (fromEnum w) 'X' ++ "\n"
+            row_h = replicate (fromEnum w - 1) 'X'
+            row_n = "X" ++ replicate (fromEnum w - 2) ' ' ++ "X\n"
+            image = row_0 ++ (concat $ replicate (fromEnum h - 2) row_n) ++ row_h
+        putStr image
+        hFlush stdout
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = display_bounds_test_summary True
+    , confirm_results = generic_output_match_confirm
+    }
+
+display_bounds_test_1 = Test
+    { test_name = "Verify display bounds are correct test 0: Using cursor movement."
+    , test_ID = "display_bounds_test_1"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        DisplayRegion w h <- display_bounds t
+        set_cursor_pos t 0 0
+        let row_0 = replicate (fromEnum w) 'X' ++ "\n"
+        putStr row_0
+        forM_ [1 .. h - 2] $ \y -> do
+            set_cursor_pos t 0 y
+            putStr "X"
+            hFlush stdout
+            set_cursor_pos t (w - 1) y
+            putStr "X"
+            hFlush stdout
+        set_cursor_pos t 0 (h - 1)
+        let row_h = replicate (fromEnum w - 1) 'X'
+        putStr row_h
+        hFlush stdout
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = display_bounds_test_summary True
+    , confirm_results = generic_output_match_confirm
+    }
+
+display_bounds_test_2 = Test
+    { test_name = "Verify display bounds are correct test 0: Using Image ops."
+    , test_ID = "display_bounds_test_2"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        bounds@(DisplayRegion w h) <- display_bounds t
+        let first_row = horiz_cat $ replicate (fromEnum w) (char def_attr 'X')
+            middle_rows = vert_cat $ replicate (fromEnum h - 2) middle_row
+            middle_row = (char def_attr 'X') <|> background_fill (w - 2) 1 <|> (char def_attr 'X')
+            end_row = first_row
+            image = first_row <-> middle_rows <-> end_row
+            pic = (pic_for_image image) { pic_cursor = Cursor (w - 1) (h - 1) }
+        d <- display_context t bounds
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = display_bounds_test_summary True
+    , confirm_results = generic_output_match_confirm
+    }
+
+display_bounds_test_3 = Test
+    { test_name = "Verify display bounds are correct test 0: Hide cursor; Set cursor pos."
+    , test_ID = "display_bounds_test_3"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        DisplayRegion w h <- display_bounds t
+        hide_cursor t
+        set_cursor_pos t 0 0
+        let row_0 = replicate (fromEnum w) 'X'
+        putStrLn row_0
+        forM_ [1 .. h - 2] $ \y -> do
+            set_cursor_pos t 0 y
+            putStr "X"
+            hFlush stdout
+            set_cursor_pos t (w - 1) y
+            putStr "X"
+            hFlush stdout
+        set_cursor_pos t 0 (h - 1)
+        let row_h = row_0
+        putStr row_h
+        hFlush stdout
+        getLine
+        show_cursor t
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = display_bounds_test_summary False
+    , confirm_results = generic_output_match_confirm
+    }
+
+display_bounds_test_summary has_cursor = do
+    putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+|]
+    if has_cursor
+        then putStr "    1. The cursor will be visible."
+        else putStr "    1. The cursor will NOT be visible."
+    putStr [s|
+    2. The border of the display will be outlined in Xs. 
+       So if - and | represented the edge of the terminal window:
+         |-------------|
+         |XXXXXXXXXXXXX|
+         |X           X||]
+
+    if has_cursor
+        then putStr $ [s|
+         |XXXXXXXXXXXXC| |]
+        else putStr $ [s|
+         |XXXXXXXXXXXXX| |]
+
+    putStr $ [s|
+         |-------------|
+
+        ( Where C is the final position of the cursor. There may be an X drawn
+        under the cursor. )
+    3. The display will remain in this state until return is pressed again.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+
+generic_output_match_confirm = do
+    putStr $ [s|
+Did the test output match the description?
+|]
+    default_success_confirm_results
+
+-- Explicitely definethe bytes that encode each example text.
+-- This avoids any issues with how the compiler represents string literals.
+--
+-- This document is UTF-8 encoded so the UTF-8 string is still included for
+-- reference
+--
+-- It's assumed the compiler will at least not barf on UTF-8 encoded text in
+-- comments ;-)
+--
+-- txt_0 = ↑↑↓↓←→←→BA
+
+utf8_txt_0 :: [[Word8]]
+utf8_txt_0 = [ [ 0xe2 , 0x86 , 0x91 ]
+             , [ 0xe2 , 0x86 , 0x91 ]
+             , [ 0xe2 , 0x86 , 0x93 ]
+             , [ 0xe2 , 0x86 , 0x93 ]
+             , [ 0xe2 , 0x86 , 0x90 ]
+             , [ 0xe2 , 0x86 , 0x92 ]
+             , [ 0xe2 , 0x86 , 0x90 ]
+             , [ 0xe2 , 0x86 , 0x92 ]
+             , [ 0x42 ]
+             , [ 0x41 ]
+             ]
+
+iso_10646_txt_0 :: String
+iso_10646_txt_0 = map toEnum
+    [ 8593 
+    , 8593
+    , 8595
+    , 8595
+    , 8592
+    , 8594
+    , 8592
+    , 8594
+    , 66
+    , 65
+    ]
+
+unicode_single_width_0 = Test
+    { test_name = "Verify terminal can display unicode single-width characters. (Direct UTF-8)"
+    , test_ID = "unicode_single_width_0"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        hide_cursor t
+        withArrayLen (concat utf8_txt_0) (flip $ hPutBuf stdout)
+        hPutStr stdout "\n"
+        hPutStr stdout "0123456789\n"
+        hFlush stdout
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = unicode_single_width_summary
+    , confirm_results = generic_output_match_confirm
+    }
+
+unicode_single_width_1 = Test
+    { test_name = "Verify terminal can display unicode single-width characters. (Image ops)"
+    , test_ID = "unicode_single_width_1"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = line_0 <-> line_1
+            line_0 = iso_10646_string def_attr iso_10646_txt_0
+            line_1 = string def_attr "0123456789"
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = unicode_single_width_summary
+    , confirm_results = generic_output_match_confirm
+    }
+
+unicode_single_width_summary = putStr [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. Two horizontal lines of text will be displayed:
+        a. The first will be a sequence of glyphs in UTF-8 encoding. Each glyph
+        will occupy one column of space. The order and appearance of the glyphs
+        will be:
+            | column | appearance    |
+            ==========================
+            | 0      | up arrow      |
+            | 1      | up arrow      |
+            | 2      | down arrow    |
+            | 3      | down arrow    |
+            | 4      | left arrow    |
+            | 5      | right arrow   |
+            | 6      | left arrow    |
+            | 7      | right arrow   |
+            | 8      | B             |
+            | 9      | A             |
+            ( see: http://en.wikipedia.org/wiki/Arrow_(symbol) )
+        b. The second will be: 0123456789. 
+
+Verify: 
+    * The far right extent of the glyphs on both lines are equal; 
+    * The glyphs are as described.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+
+-- The second example is a unicode string containing double-width glyphs
+-- 你好吗
+utf8_txt_1 :: [[Word8]]
+utf8_txt_1 = [ [0xe4,0xbd,0xa0]
+             , [0xe5,0xa5,0xbd]
+             , [0xe5,0x90,0x97]
+             ]
+
+iso_10646_txt_1 :: String
+iso_10646_txt_1 = map toEnum [20320,22909,21527]
+
+unicode_double_width_0 = Test
+    { test_name = "Verify terminal can display unicode double-width characters. (Direct UTF-8)"
+    , test_ID = "unicode_double_width_0"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        hide_cursor t
+        withArrayLen (concat utf8_txt_1) (flip $ hPutBuf stdout)
+        hPutStr stdout "\n"
+        hPutStr stdout "012345\n"
+        hFlush stdout
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = unicode_double_width_summary
+    , confirm_results = generic_output_match_confirm
+    }
+
+unicode_double_width_1 = Test
+    { test_name = "Verify terminal can display unicode double-width characters. (Image ops)"
+    , test_ID = "unicode_double_width_1"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = line_0 <-> line_1
+            line_0 = iso_10646_string def_attr iso_10646_txt_1
+            line_1 = string def_attr "012345"
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = unicode_double_width_summary
+    , confirm_results = generic_output_match_confirm
+    }
+
+unicode_double_width_summary = putStr [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. Two horizontal lines of text will be displayed:
+        a. The first will be a sequence of glyphs in UTF-8 encoding. Each glyph
+        will occupy two columns of space. The order and appearance of the glyphs
+        will be:
+            | column | appearance                |
+            ======================================
+            | 0      | first half of ni3         |
+            | 1      | second half of ni3        |
+            | 2      | first half of hao3        |
+            | 3      | second half of hao3       |
+            | 4      | first half of ma          |
+            | 5      | second half of ma         |
+        b. The second will be: 012345. 
+
+Verify: 
+    * The far right extent of the glyphs on both lines are equal; 
+    * The glyphs are as described.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+
+all_colors = zip [ black, red, green, yellow, blue, magenta, cyan, white ]
+                 [ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" ]
+
+all_bright_colors 
+    = zip [ bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white ]
+          [ "bright black", "bright red", "bright green", "bright yellow", "bright blue", "bright magenta", "bright cyan", "bright white" ]
+
+attributes_test_0 = Test 
+    { test_name = "Character attributes: foreground colors."
+    , test_ID = "attributes_test_0"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = border <|> column_0 <|> border <|> column_1 <|> border
+            column_0 = vert_cat $ map line_with_color all_colors
+            border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
+            column_1 = vert_cat $ map (string def_attr . snd) all_colors
+            line_with_color (c, c_name) = string (def_attr `with_fore_color` c) c_name
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. 9 lines of text in two columns will be drawn. The first column will be a
+    name of a standard color (for an 8 color terminal) rendered in that color.
+    For instance, one line will be the word "magenta" and that word should be
+    rendered in the magenta color. The second column will be the name of a
+    standard color rendered with the default attributes.
+
+Verify: 
+    * In the first column: The foreground color matches the named color.
+    * The second column: All text is rendered with the default attributes.
+    * The vertical bars used in each line to mark the border of a column are
+    lined up.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+attributes_test_1 = Test 
+    { test_name = "Character attributes: background colors."
+    , test_ID = "attributes_test_1"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = border <|> column_0 <|> border <|> column_1 <|> border
+            column_0 = vert_cat $ map line_with_color all_colors
+            border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
+            column_1 = vert_cat $ map (string def_attr . snd) all_colors
+            line_with_color (c, c_name) = string (def_attr `with_back_color` c) c_name
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. 9 lines of text in two columns will be drawn. The first column will
+    contain be a name of a standard color for an 8 color terminal rendered with
+    the default foreground color with a background the named color.  For
+    instance, one line will contain be the word "magenta" and the word should
+    be rendered in the default foreground color over a magenta background. The
+    second column will be the name of a standard color rendered with the default
+    attributes.
+
+Verify: 
+    * The first column: The background color matches the named color.
+    * The second column: All text is rendered with the default attributes.
+    * The vertical bars used in each line to mark the border of a column are
+    lined up.
+
+Note: I haven't decided if, in this case, the background color should extend to
+fills added for alignment. Right now the selected background color is only
+applied to the background where the word is actually rendered. Since each word
+is not of the same length VTY adds background fills to make the width of each
+row effectively the same. These added fills are all currently rendered with the
+default background pattern.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+attributes_test_2 = Test 
+    { test_name = "Character attributes: Vivid foreground colors."
+    , test_ID = "attributes_test_2"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = horiz_cat [border, column_0, border, column_1, border, column_2, border]
+            border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
+            column_0 = vert_cat $ map line_with_color_0 all_colors
+            column_1 = vert_cat $ map line_with_color_1 all_bright_colors
+            column_2 = vert_cat $ map (string def_attr . snd) all_colors
+            line_with_color_0 (c, c_name) = string (def_attr `with_fore_color` c) c_name
+            line_with_color_1 (c, c_name) = string (def_attr `with_fore_color` c) c_name
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. 9 lines of text in three columns will be drawn:
+        a. The first column will be a name of a standard color (for an 8 color
+        terminal) rendered with that color as the foreground color.  
+        b. The next column will be also be the name of a standard color rendered
+        with that color as the foreground color but the shade used should be
+        more vivid than the shade used in the first column.    
+        c. The final column will be the name of a color rendered with the
+        default attributes.
+
+For instance, one line will be the word "magenta" and that word should be
+rendered in the magenta color. 
+
+I'm not actually sure exactly what "vivid" means in this context. For xterm the
+vivid colors are brighter.  
+
+Verify: 
+    * The first column: The foreground color matches the named color.
+    * The second column: The foreground color matches the named color but is
+    more vivid than the color used in the first column.  
+    * The third column: All text is rendered with the default attributes.
+    * The vertical bars used in each line to mark the border of a column are
+    lined up.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+attributes_test_3 = Test 
+    { test_name = "Character attributes: Vivid background colors."
+    , test_ID = "attributes_test_3"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = horiz_cat [border, column_0, border, column_1, border, column_2, border]
+            border = vert_cat $ replicate (length all_colors) $ string def_attr " | "
+            column_0 = vert_cat $ map line_with_color_0 all_colors
+            column_1 = vert_cat $ map line_with_color_1 all_bright_colors
+            column_2 = vert_cat $ map (string def_attr . snd) all_colors
+            line_with_color_0 (c, c_name) = string (def_attr `with_back_color` c) c_name
+            line_with_color_1 (c, c_name) = string (def_attr `with_back_color` c) c_name
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. 9 lines of text in three columns will be drawn:
+        a. The first column will contain be a name of a standard color for an 8
+        color terminal rendered with the default foreground color with a
+        background the named color.  
+        b. The first column will contain be a name of a standard color for an 8
+        color terminal rendered with the default foreground color with the
+        background a vivid version of the named color. 
+        c. The third column will be the name of a standard color rendered with
+        the default attributes.
+        
+For instance, one line will contain be the word "magenta" and the word should
+be rendered in the default foreground color over a magenta background. 
+
+I'm not actually sure exactly what "vivid" means in this context. For xterm the
+vivid colors are brighter.
+
+Verify: 
+    * The first column: The background color matches the named color.
+    * The second column: The background color matches the named color and is
+    more vivid than the color used in the first column.  
+    * The third column column: All text is rendered with the default attributes.
+    * The vertical bars used in each line to mark the border of a column are
+    lined up.
+
+Note: I haven't decided if, in this case, the background color should extend to
+fills added for alignment. Right now the selected background color is only
+applied to the background where the word is actually rendered. Since each word
+is not of the same length VTY adds background fills to make the width of each
+row effectively the same. These added fills are all currently rendered with the
+default background pattern.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+attr_combos = 
+    [ ( "default", id )
+    , ( "bold", flip with_style bold )
+    , ( "blink", flip with_style blink )
+    , ( "underline", flip with_style underline )
+    , ( "bold + blink", flip with_style (bold + blink) )
+    , ( "bold + underline", flip with_style (bold + underline) )
+    , ( "underline + blink", flip with_style (underline + blink) )
+    , ( "bold + blink + underline", flip with_style (bold + blink + underline) )
+    ]
+
+attributes_test_4 = Test 
+    { test_name = "Character attributes: Bold; Blink; Underline."
+    , test_ID = "attributes_test_4"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = horiz_cat [border, column_0, border, column_1, border]
+            border = vert_cat $ replicate (length attr_combos) $ string def_attr " | "
+            column_0 = vert_cat $ map line_with_attrs attr_combos
+            column_1 = vert_cat $ map (string def_attr . fst) attr_combos
+            line_with_attrs (desc, attr_f) = string (attr_f def_attr) desc
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. 8 rows of text in two columns. 
+    The rows will contain the following text:
+        default
+        bold 
+        blink
+        underline
+        bold + blink
+        bold + underline
+        underline + blink
+        bold + blink + underline
+    The first column will be rendered with the described attributes. The second
+    column will be rendered with the default attributes.
+        
+Verify: 
+    * The vertical bars used in each line to mark the border of a column are
+    lined up.
+    * The text in the first column is rendered as described.
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+attributes_test_5 = Test 
+    { test_name = "Character attributes: 240 color palette"
+    , test_ID = "attributes_test_5"
+    , test_action = do
+        t <- terminal_handle
+        reserve_display t
+        let pic = pic_for_image image
+            image = vert_cat $ map horiz_cat $ split_color_images color_images
+            color_images = map (\i -> string (current_attr `with_back_color` Color240 i) " ") [0..239]
+            split_color_images [] = []
+            split_color_images is = (take 20 is ++ [string def_attr " "]) : (split_color_images (drop 20 is))
+        d <- display_bounds t >>= display_context t
+        output_picture d pic
+        getLine
+        release_display t
+        release_terminal t
+        return ()
+    , print_summary = do
+        putStr $ [s|
+Once return is pressed:
+    0. The screen will be cleared.
+    1. The cursor will be hidden.
+    2. A 20 character wide and 12 row high block of color squares. This should look like a palette
+    of some sort. I'm not exactly sure if all color terminals use the same palette. I doubt it...
+
+Verify: 
+
+After return is pressed for the second time:
+    0. The screen containing the test summary should be restored.
+    1. The cursor should be visible.
+|]
+    , confirm_results = do
+        putStr $ [s|
+Did the test output match the description?
+|]
+        default_success_confirm_results
+    }
+
+inline_test_0 = Test
+    { test_name = "Verify styled output can be performed without clearing the screen."
+    , test_ID = "inline_test_0"
+    , test_action = do
+        t <- terminal_handle
+        putStrLn "line 1."
+        put_attr_change t $ back_color red >> apply_style underline
+        putStrLn "line 2."
+        put_attr_change t $ default_all
+        putStrLn "line 3."
+        release_terminal t
+        return ()
+    , print_summary = putStr $ [s|
+lines are in order.
+The second line "line 2" should have a red background and the text underline.
+The third line "line 3" should be drawn in the same style as the first line.
+|]
+
+    , confirm_results = generic_output_match_confirm
+    }
+
+inline_test_1 = Test
+    { test_name = "Verify styled output can be performed without clearing the screen."
+    , test_ID = "inline_test_1"
+    , test_action = do
+        t <- terminal_handle
+        putStr "Not styled. "
+        put_attr_change t $ back_color red >> apply_style underline
+        putStr " Styled! "
+        put_attr_change t $ default_all
+        putStrLn "Not styled."
+        release_terminal t
+        return ()
+    , print_summary = putStr $ [s|
+|]
+
+    , confirm_results = generic_output_match_confirm
+    }
+
+inline_test_2 = Test
+    { test_name = "Verify styled output can be performed without clearing the screen."
+    , test_ID = "inline_test_1"
+    , test_action = do
+        t <- terminal_handle
+        putStr "Not styled. "
+        put_attr_change t $ back_color red >> apply_style underline
+        putStr " Styled! "
+        put_attr_change t $ default_all
+        putStr "Not styled.\n"
+        release_terminal t
+        return ()
+    , print_summary = putStr $ [s|
+|]
+    , confirm_results = generic_output_match_confirm
+    }
+
+cursor_hide_test_0 :: Test
+cursor_hide_test_0 = Test
+    { test_name = "Verify the cursor is hid and re-shown. issue #7"
+    , test_ID = "cursor_hide_test_0"
+    , test_action = do
+        vty <- mkVty
+        show_cursor $ terminal vty
+        set_cursor_pos (terminal vty) 5 5
+        next_event vty
+        hide_cursor $ terminal vty
+        next_event vty
+        shutdown vty
+        return ()
+    , print_summary = putStr $ [s|
+    1. verify the cursor is displayed.
+    2. press enter
+    3. verify the cursor is hid.
+    4. press enter.
+    5. the display should return to the state before the test.
+|]
+    , confirm_results = generic_output_match_confirm
+    }
+
diff --git a/vty.cabal b/vty.cabal
--- a/vty.cabal
+++ b/vty.cabal
@@ -1,5 +1,5 @@
 name:                vty
-version:             4.7.0.20
+version:             4.7.1
 license:             BSD3
 license-file:        LICENSE
 author:              AUTHORS
@@ -36,7 +36,6 @@
                      CHANGELOG,
                      LICENSE
 
-
 library
   default-language:    Haskell2010
   build-depends:       base >= 4 && < 5,
@@ -92,7 +91,7 @@
   hs-source-dirs:      src test
   test-module:         VerifyAttributeOps
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -143,7 +142,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -194,7 +193,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -222,7 +221,7 @@
                        Verify
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -253,7 +252,7 @@
                        Verify
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -296,7 +295,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -333,7 +332,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -380,7 +379,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -411,7 +410,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -441,7 +440,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -492,7 +491,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -543,7 +542,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -577,7 +576,7 @@
   include-dirs:        cbits
 
   build-depends:       Cabal == 1.17.*,
-                       QuickCheck == 2.4.*,
+                       QuickCheck >= 2.4,
                        random == 1.0.*,
                        base >= 4 && < 5,
                        bytestring,
@@ -592,6 +591,36 @@
                        utf8-string >= 0.3 && < 0.4,
                        vector >= 0.7
 
+executable vty-interactive-terminal-test
+  main-is:             interactive_terminal_test.hs
+
+  default-language:    Haskell2010
+
+  hs-source-dirs:      src test
+
+  c-sources:           cbits/gwinsz.c
+                       cbits/set_term_timing.c
+                       cbits/mk_wcwidth.c
+
+  include-dirs:        cbits
+
+  build-depends:       Cabal == 1.17.*,
+                       QuickCheck >= 2.4,
+                       random == 1.0.*,
+                       base >= 4 && < 5,
+                       bytestring,
+                       containers,
+                       deepseq >= 1.1 && < 1.4,
+                       ghc-prim,
+                       mtl >= 1.1.1.0 && < 2.2,
+                       parallel >= 2.2 && < 3.3,
+                       parsec >= 2 && < 4,
+                       string-qq,
+                       terminfo >= 0.3 && < 0.4,
+                       unix,
+                       utf8-string >= 0.3 && < 0.4,
+                       vector >= 0.7
+
 -- Bench.hs
 -- Bench2.hs
 -- BenchRenderChar.hs
@@ -606,7 +635,6 @@
 -- Verify/Graphics/Vty/Image.hs
 -- Verify/Graphics/Vty/Picture.hs
 -- Verify/Graphics/Vty/Span.hs
--- interactive_terminal_test.hs
 -- vty_inline_example.hs
 -- vty_issue_18.hs
 -- yi_issue_264.hs
