vty 5.1.4 → 5.2.0
raw patch · 32 files changed
+315/−216 lines, 32 filesdep ~Cabal
Dependency ranges changed: Cabal
Files
- CHANGELOG +16/−3
- dist/build/verify-attribute-opsStub/verify-attribute-opsStub-tmp/verify-attribute-opsStub.hs +5/−0
- dist/build/verify-crop-span-generationStub/verify-crop-span-generationStub-tmp/verify-crop-span-generationStub.hs +5/−0
- dist/build/verify-display-attributesStub/verify-display-attributesStub-tmp/verify-display-attributesStub.hs +5/−0
- dist/build/verify-empty-image-propsStub/verify-empty-image-propsStub-tmp/verify-empty-image-propsStub.hs +5/−0
- dist/build/verify-eval-terminfo-capsStub/verify-eval-terminfo-capsStub-tmp/verify-eval-terminfo-capsStub.hs +5/−0
- dist/build/verify-image-opsStub/verify-image-opsStub-tmp/verify-image-opsStub.hs +5/−0
- dist/build/verify-image-transStub/verify-image-transStub-tmp/verify-image-transStub.hs +5/−0
- dist/build/verify-inlineStub/verify-inlineStub-tmp/verify-inlineStub.hs +5/−0
- dist/build/verify-layers-span-generationStub/verify-layers-span-generationStub-tmp/verify-layers-span-generationStub.hs +5/−0
- dist/build/verify-parse-terminfo-capsStub/verify-parse-terminfo-capsStub-tmp/verify-parse-terminfo-capsStub.hs +5/−0
- dist/build/verify-simple-span-generationStub/verify-simple-span-generationStub-tmp/verify-simple-span-generationStub.hs +5/−0
- dist/build/verify-terminalStub/verify-terminalStub-tmp/verify-terminalStub.hs +5/−0
- dist/build/verify-using-mock-terminalStub/verify-using-mock-terminalStub-tmp/verify-using-mock-terminalStub.hs +5/−0
- dist/build/verify-utf8-widthStub/verify-utf8-widthStub-tmp/verify-utf8-widthStub.hs +5/−0
- src/Graphics/Vty.hs +3/−3
- src/Graphics/Vty/Config.hs +55/−32
- src/Graphics/Vty/Inline/Unsafe.hs +23/−3
- src/Graphics/Vty/Input.hs +14/−18
- src/Graphics/Vty/Input/Loop.hs +20/−18
- src/Graphics/Vty/Input/Terminfo.hs +1/−0
- src/Graphics/Vty/Output.hs +24/−37
- src/Graphics/Vty/Output/Interface.hs +3/−3
- src/Graphics/Vty/Output/MacOSX.hs +7/−7
- src/Graphics/Vty/Output/TerminfoBased.hs +33/−54
- src/Graphics/Vty/Output/XTermColor.hs +7/−7
- src/Graphics/Vty/PictureToSpans.hs +8/−2
- test/Verify.hs +1/−1
- test/VerifyConfig.hs +0/−7
- test/VerifyOutput.hs +6/−3
- test/VerifyUsingMockInput.hs +6/−1
- vty.cabal +18/−17
CHANGELOG view
@@ -1,3 +1,16 @@+5.2.0+ - Config structure now specifies file descriptor to use. The default is stdInput and stdOutput+ file descriptors. Previously Vty used stdInput for input and the follow code for output:+ - hDuplicate stdout >>= handleToFd >>= (`hSetBuffering` NoBuffering)+ - the difference was required by Vty.Inline. Now, Vty.Inline uses the Config structure options+ to acheive the same effect.+ - removed: derivedVtime, derivedVmin, inputForCurrentTerminal, inputForNameAndIO,+ outputForCurrentTerminal, outputForNameAndIO+ - added: inputForConfig, outputForConfig+ - updates to vty-rogue from jtdaugherty. Thanks! + - the oldest version of GHC tested to support vty is 7.6.2.+ - the oldest version of GHC that vty compiles under is 7.4.2+ 5.1.4 - merged https://github.com/coreyoconnor/vty/pull/51 thanks trofi! @@ -65,9 +78,9 @@ - vty-event-echo - view a input event log for vty. Example of interacting with user. - test/EventEcho.hs- - vty-rouge- - The start of a rouge-like game. Example of layers and image build operations.- - test/Rouge.hs+ - vty-rogue+ - The start of a rogue-like game. Example of layers and image build operations.+ - test/Rogue.hs - vty-benchmark - benchmarks vty. A series of tests that push random pictures to the terminal. The random pictures are generated using QuickCheck. The same generators used in the
+ dist/build/verify-attribute-opsStub/verify-attribute-opsStub-tmp/verify-attribute-opsStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyAttributeOps ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-crop-span-generationStub/verify-crop-span-generationStub-tmp/verify-crop-span-generationStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyCropSpanGeneration ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-display-attributesStub/verify-display-attributesStub-tmp/verify-display-attributesStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyDisplayAttributes ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-empty-image-propsStub/verify-empty-image-propsStub-tmp/verify-empty-image-propsStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyEmptyImageProps ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-eval-terminfo-capsStub/verify-eval-terminfo-capsStub-tmp/verify-eval-terminfo-capsStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyEvalTerminfoCaps ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-image-opsStub/verify-image-opsStub-tmp/verify-image-opsStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyImageOps ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-image-transStub/verify-image-transStub-tmp/verify-image-transStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyImageTrans ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-inlineStub/verify-inlineStub-tmp/verify-inlineStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyInline ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-layers-span-generationStub/verify-layers-span-generationStub-tmp/verify-layers-span-generationStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyLayersSpanGeneration ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-parse-terminfo-capsStub/verify-parse-terminfo-capsStub-tmp/verify-parse-terminfo-capsStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyParseTerminfoCaps ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-simple-span-generationStub/verify-simple-span-generationStub-tmp/verify-simple-span-generationStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifySimpleSpanGeneration ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-terminalStub/verify-terminalStub-tmp/verify-terminalStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyOutput ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-using-mock-terminalStub/verify-using-mock-terminalStub-tmp/verify-using-mock-terminalStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyUsingMockTerminal ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/verify-utf8-widthStub/verify-utf8-widthStub-tmp/verify-utf8-widthStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import VerifyUtf8Width ( tests )+main :: IO ()+main = stubMain tests
src/Graphics/Vty.hs view
@@ -108,9 +108,9 @@ -- For most applications @mkVty def@ is sufficient. mkVty :: Config -> IO Vty mkVty appConfig = do- config <- mappend <$> pure appConfig <*> userConfig- input <- inputForCurrentTerminal config- out <- outputForCurrentTerminal config+ config <- (<> appConfig) <$> userConfig+ input <- inputForConfig config+ out <- outputForConfig config intMkVty input out intMkVty :: Input -> Output -> IO Vty
src/Graphics/Vty/Config.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE KindSignatures #-}@@ -65,13 +66,16 @@ -- module Graphics.Vty.Config where --- ignore warning on GHC 7.6+. Required for GHC 7.4+#if __GLASGOW_HASKELL__ > 704+import Prelude+#else import Prelude hiding (catch)+#endif import Control.Applicative hiding (many) -import Control.Exception (tryJust, catch, IOException)-import Control.Monad (void, guard)+import Control.Exception (catch, IOException)+import Control.Monad (void) import Control.Monad.Trans.Class import Control.Monad.Trans.Writer @@ -82,8 +86,9 @@ import Graphics.Vty.Input.Events import System.Directory (getAppUserDataDirectory)-import System.Environment (getEnv)-import System.IO.Error (isDoesNotExistError)+import System.Posix.Env (getEnv)+import System.Posix.IO (stdInput, stdOutput)+import System.Posix.Types (Fd(..)) import Text.Parsec hiding ((<|>)) import Text.Parsec.Token ( GenLanguageDef(..) )@@ -94,8 +99,11 @@ type InputMap = [(Maybe String, String, Event)] data Config = Config- { vmin :: Maybe Int -- < See 'derivedVmin'- , vtime :: Maybe Int -- < See 'derivedVtime'+ {+ -- | The default is 1 character.+ vmin :: Maybe Int+ -- | The default is 100 milliseconds, 0.1 seconds.+ , vtime :: Maybe Int -- | Debug information is appended to this file if not Nothing. , debugLog :: Maybe FilePath -- | The (input byte, output event) pairs extend the internal input table of VTY and the table@@ -103,34 +111,37 @@ -- -- See "Graphics.Vty.Config" module documentation for documentation of the @map@ directive. , inputMap :: InputMap+ -- | The input file descriptor to use. The default is 'System.Posix.IO.stdInput'+ , inputFd :: Maybe Fd+ -- | The output file descriptor to use. The default is 'System.Posix.IO.stdOutput'+ , outputFd :: Maybe Fd+ -- | The terminal name used to look up terminfo capabilities.+ -- The default is the value of the TERM environment variable.+ , termName :: Maybe String } deriving (Show, Eq) --- | The default is 100 milliseconds, 0.1 seconds.--- Set using the 'vtime' field of 'Config'-derivedVtime :: Config -> Int-derivedVtime = maybe 100 id . vtime---- | The default is 1 character.--- Set using the 'vmin' field of 'Config'-derivedVmin :: Config -> Int-derivedVmin = maybe 1 id . vmin- instance Default Config where def = mempty instance Monoid Config where mempty = Config- { vmin = Nothing- , vtime = Nothing- , debugLog = mempty- , inputMap = mempty+ { vmin = Nothing+ , vtime = Nothing+ , debugLog = mempty+ , inputMap = mempty+ , inputFd = Nothing+ , outputFd = Nothing+ , termName = Nothing } mappend c0 c1 = Config- -- latter config takes priority in vmin, vtime, debugInputLog- { vmin = vmin c1 <|> vmin c0- , vtime = vtime c1 <|> vtime c0- , debugLog = debugLog c1 <|> debugLog c0- , inputMap = inputMap c0 <> inputMap c1+ -- latter config takes priority for everything but inputMap+ { vmin = vmin c1 <|> vmin c0+ , vtime = vtime c1 <|> vtime c0+ , debugLog = debugLog c1 <|> debugLog c0+ , inputMap = inputMap c0 <> inputMap c1+ , inputFd = inputFd c1 <|> inputFd c1+ , outputFd = outputFd c1 <|> outputFd c1+ , termName = termName c1 <|> termName c0 } type ConfigParser s a = ParsecT s () (Writer Config) a@@ -139,12 +150,24 @@ userConfig :: IO Config userConfig = do configFile <- (mappend <$> getAppUserDataDirectory "vty" <*> pure "/config") >>= parseConfigFile- let maybeEnv = tryJust (guard . isDoesNotExistError) . getEnv- overridePath <- maybeEnv "VTY_CONFIG_FILE"- overrideConfig <- either (const $ return def) parseConfigFile overridePath- debugLogPath <- maybeEnv "VTY_DEBUG_LOG"- let debugLogConfig = either (const def) (\p -> def { debugLog = Just p }) debugLogPath- return $ mconcat [configFile, overrideConfig, debugLogConfig]+ overrideConfig <- maybe (return def) parseConfigFile =<< getEnv "VTY_CONFIG_FILE"+ base <- (<> configFile <> overrideConfig) <$> standardIOConfig+ (mappend base) <$> overrideEnvConfig++overrideEnvConfig :: IO Config+overrideEnvConfig = do+ d <- getEnv "VTY_DEBUG_LOG"+ return $ def { debugLog = d }++standardIOConfig :: IO Config+standardIOConfig = do+ Just t <- getEnv "TERM"+ return $ def { vmin = Just 1+ , vtime = Just 100+ , inputFd = Just stdInput+ , outputFd = Just stdOutput+ , termName = Just t+ } parseConfigFile :: FilePath -> IO Config parseConfigFile path = do
src/Graphics/Vty/Inline/Unsafe.hs view
@@ -1,13 +1,23 @@+{-# LANGUAGE CPP #-} {-# OPTIONS_HADDOCK hide #-} module Graphics.Vty.Inline.Unsafe where import Graphics.Vty +import Control.Applicative+ import Data.Default+import Data.Monoid import Data.IORef +import GHC.IO.Handle (hDuplicate)++import System.IO (stdin, stdout, hSetBuffering, BufferMode(NoBuffering))+ import System.IO.Unsafe +import System.Posix.IO (handleToFd)+ globalVty :: IORef (Maybe Vty) {-# NOINLINE globalVty #-} globalVty = unsafePerformIO $ newIORef Nothing@@ -16,14 +26,24 @@ {-# NOINLINE globalOutput #-} globalOutput = unsafePerformIO $ newIORef Nothing +mkDupeConfig :: IO Config+mkDupeConfig = do+ hSetBuffering stdout NoBuffering+ hSetBuffering stdin NoBuffering+ stdinDupe <- hDuplicate stdin >>= handleToFd+ stdoutDupe <- hDuplicate stdout >>= handleToFd+ return $ def { inputFd = Just stdinDupe, outputFd = Just stdoutDupe }+ -- | This will create a Vty instance using 'mkVty' and execute an IO action provided that instance. -- The created Vty instance will be stored to the unsafe 'IORef' 'globalVty'.+--+-- This instance will use duplicates of the stdin and stdout Handles. withVty :: (Vty -> IO b) -> IO b withVty f = do mvty <- readIORef globalVty vty <- case mvty of Nothing -> do- vty <- mkVty $ def+ vty <- mkDupeConfig >>= mkVty writeIORef globalVty (Just vty) return vty Just vty -> return vty@@ -34,8 +54,8 @@ mout <- readIORef globalOutput out <- case mout of Nothing -> do- config <- userConfig- out <- outputForCurrentTerminal config+ config <- (<>) <$> userConfig <*> mkDupeConfig+ out <- outputForConfig config writeIORef globalOutput (Just out) return out Just out -> return out
src/Graphics/Vty/Input.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE RecordWildCards #-} -- | The input layer for VTY. This provides methods for initializing an 'Input' structure which can -- then be used to read 'Event's from the terminal. --@@ -120,8 +121,7 @@ , Button(..) , Event(..) , Input(..)- , inputForCurrentTerminal- , inputForNameAndIO+ , inputForConfig ) where @@ -133,22 +133,13 @@ import Control.Concurrent import Control.Lens +import Data.Functor ((<$>)) import Data.Monoid import qualified System.Console.Terminfo as Terminfo-import System.Environment import System.Posix.Signals.Exts-import System.Posix.IO (stdInput)-import System.Posix.Types (Fd) --- | Set up the current terminal for input.--- This determines the current terminal then invokes 'inputForNameAndIO'-inputForCurrentTerminal :: Config -> IO Input-inputForCurrentTerminal config = do- termName <- getEnv "TERM"- inputForNameAndIO config termName stdInput---- | Set up the terminal attached to the given Fd for input. Returns a 'Input'.+-- | Set up the terminal with file descriptor `inputFd` for input. Returns a 'Input'. -- -- The table used to determine the 'Events' to produce for the input bytes comes from -- 'classifyMapForTerm'. Which is then overridden by the the applicable entries from@@ -175,16 +166,20 @@ -- - canonical mode (line mode) input is not used. TODO: should be a dynamic option. -- -- * IEXTEN disabled--- - extended functions are disabled. TODO: Uh. Whatever these are.+-- - extended functions are disabled. TODO: I don't know what those are. ---inputForNameAndIO :: Config -> String -> Fd -> IO Input-inputForNameAndIO config termName termFd = do+inputForConfig :: Config -> IO Input+inputForConfig config@Config{ termName = Just termName+ , inputFd = Just termFd+ , vmin = Just _+ , vtime = Just _+ , .. } = do terminal <- Terminfo.setupTerm termName- let inputOverrides = [(s,e) | (t,s,e) <- inputMap config, t == Nothing || t == Just termName]+ let inputOverrides = [(s,e) | (t,s,e) <- inputMap, t == Nothing || t == Just termName] activeInputMap = classifyMapForTerm termName terminal `mappend` inputOverrides (setAttrs,unsetAttrs) <- attributeControl termFd setAttrs- input <- initInputForFd config termName activeInputMap termFd + input <- initInput config activeInputMap let pokeIO = Catch $ do let e = error "vty internal failure: this value should not propagate to users" setAttrs@@ -198,3 +193,4 @@ _ <- installHandler continueProcess Ignore Nothing unsetAttrs }+inputForConfig config = mappend config <$> standardIOConfig >>= inputForConfig
src/Graphics/Vty/Input/Loop.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE RecordWildCards #-} {-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE TemplateHaskell #-}@@ -47,8 +48,6 @@ , shutdownInput :: IO () -- | Changes to this value are reflected after the next event. , _configRef :: IORef Config- -- | File descriptor used for input.- , _inputFd :: Fd -- | input debug log , _inputDebug :: Maybe Handle }@@ -108,10 +107,10 @@ readFromDevice = do newConfig <- view configRef >>= liftIO . readIORef oldConfig <- use appliedConfig- fd <- view inputFd+ let Just fd = inputFd newConfig when (newConfig /= oldConfig) $ do logMsg $ "new config: " ++ show newConfig- liftIO $ applyTimingConfig fd newConfig+ liftIO $ applyConfig fd newConfig appliedConfig .= newConfig bufferPtr <- use $ inputBuffer.ptr maxBytes <- use $ inputBuffer.size@@ -123,9 +122,10 @@ when (not $ null stringRep) $ logMsg $ "input bytes: " ++ show stringRep return stringRep -applyTimingConfig :: Fd -> Config -> IO ()-applyTimingConfig fd config = setTermTiming fd (derivedVmin config)- (derivedVtime config `div` 100)+applyConfig :: Fd -> Config -> IO ()+applyConfig fd (Config{ vmin = Just theVmin, vtime = Just theVtime })+ = setTermTiming fd theVmin (theVtime `div` 100)+applyConfig _ _ = fail "(vty) applyConfig was not provided a complete configuration" parseEvent :: InputM Event parseEvent = do@@ -172,32 +172,34 @@ unsetAttrs = setTerminalAttributes fd original Immediately return (setAttrs,unsetAttrs) -logInitialInputState :: Input -> String -> ClassifyMap -> IO()-logInitialInputState input termName classifyTable = case _inputDebug input of+logInitialInputState :: Input -> ClassifyMap -> IO()+logInitialInputState input classifyTable = case _inputDebug input of Nothing -> return () Just h -> do- config <- readIORef $ _configRef input- _ <- hPrintf h "initial (vmin,vtime): %s\n" (show (vmin config, vtime config))+ Config{ vmin = Just theVmin+ , vtime = Just theVtime+ , termName = Just theTerm, .. } <- readIORef $ _configRef input+ _ <- hPrintf h "initial (vmin,vtime): %s\n" (show (theVmin, theVtime)) forM_ classifyTable $ \i -> case i of- (inBytes, EvKey k mods) -> hPrintf h "map %s %s %s %s\n" (show termName)+ (inBytes, EvKey k mods) -> hPrintf h "map %s %s %s %s\n" (show theTerm) (show inBytes) (show k) (show mods) _ -> return () -initInputForFd :: Config -> String -> ClassifyMap -> Fd -> IO Input-initInputForFd config termName classifyTable inFd = do- setFdOption inFd NonBlockingRead False- applyTimingConfig inFd config+initInput :: Config -> ClassifyMap -> IO Input+initInput config classifyTable = do+ let Just fd = inputFd config+ setFdOption fd NonBlockingRead False+ applyConfig fd config stopFlag <- newIORef False input <- Input <$> newChan <*> pure (writeIORef stopFlag True) <*> newIORef config- <*> pure inFd <*> maybe (return Nothing) (\f -> Just <$> openFile f AppendMode) (debugLog config)- logInitialInputState input termName classifyTable+ logInitialInputState input classifyTable _ <- forkOS $ runInputProcessorLoop classifyTable input stopFlag return input
src/Graphics/Vty/Input/Terminfo.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} module Graphics.Vty.Input.Terminfo where import Graphics.Vty.Input.Events
src/Graphics/Vty/Output.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NamedFieldPuns #-} -- | Output interface. -- -- Access to the current terminal or a specific terminal device.@@ -37,25 +39,23 @@ import Blaze.ByteString.Builder (writeToByteString) -import Control.Exception ( SomeException, try ) import Control.Monad.Trans -import Data.List ( isPrefixOf )--import GHC.IO.Handle+import Data.Monoid (mappend)+import Data.List (isPrefixOf) -import System.Environment-import System.IO+import System.Posix.Env (getEnv) --- | Returns a `Output` for the current terminal as determined by TERM.+-- | Returns a `Output` for the terminal specified in `Config` -- -- The specific Output implementation used is hidden from the API user. All terminal implementations--- are assumed to perform more, or less, the same. Currently all implementations use terminfo for at--- least some terminal specific information. This is why platforms without terminfo are not--- supported. However, as mentioned before, any specifics about it being based on terminfo are--- hidden from the API user. If a terminal implementation is developed for a terminal for a--- platform without terminfo support then Vty should work as expected on that terminal.+-- are assumed to perform more, or less, the same. Currently, all implementations use terminfo for at+-- least some terminal specific information. --+-- Specifics about it being based on terminfo are hidden from the API user. If a terminal+-- implementation is developed for a terminal without terminfo support then Vty should work as+-- expected on that terminal.+-- -- Selection of a terminal is done as follows: -- -- * If TERM == xterm@@ -72,43 +72,30 @@ -- environment variables (I think?) this assumes that XTERM_VERSION will never be set for a true -- Terminal.app or iTerm.app session. ----- The file descriptor used for output will a be a duplicate of the current stdout file descriptor.--- -- \todo add an implementation for windows that does not depend on terminfo. Should be installable -- with only what is provided in the haskell platform. Use ansi-terminal-outputForCurrentTerminal :: ( Applicative m, MonadIO m ) => Config -> m Output-outputForCurrentTerminal _config = do- termType <- liftIO $ getEnv "TERM"- outHandle <- liftIO $ hDuplicate stdout- outputForNameAndIO termType outHandle---- | gives an output method structure for a terminal with the given name and the given 'Handle'.-outputForNameAndIO :: (Applicative m, MonadIO m) => String -> Handle -> m Output-outputForNameAndIO termType outHandle = do- t <- if "xterm" `isPrefixOf` termType+outputForConfig :: Config -> IO Output+outputForConfig Config{ outputFd = Just fd, termName = Just termName, .. } = do+ t <- if "xterm" `isPrefixOf` termName then do- maybeTerminalApp <- mGetEnv "TERM_PROGRAM"+ -- the explicit nature of the code below was nice for development, not needed anymore.+ maybeTerminalApp <- getEnv "TERM_PROGRAM" case maybeTerminalApp of Nothing- -> XTermColor.reserveTerminal termType outHandle+ -> XTermColor.reserveTerminal termName fd Just v | v == "Apple_Terminal" || v == "iTerm.app" -> do- maybeXterm <- mGetEnv "XTERM_VERSION"+ maybeXterm <- getEnv "XTERM_VERSION" case maybeXterm of- Nothing -> MacOSX.reserveTerminal v outHandle- Just _ -> XTermColor.reserveTerminal termType outHandle+ Nothing -> MacOSX.reserveTerminal v fd+ Just _ -> XTermColor.reserveTerminal termName fd -- Assume any other terminal that sets TERM_PROGRAM to not be an OS X terminal.app -- like terminal?- _ -> XTermColor.reserveTerminal termType outHandle+ _ -> XTermColor.reserveTerminal termName fd -- Not an xterm-like terminal. try for generic terminfo.- else TerminfoBased.reserveTerminal termType outHandle+ else TerminfoBased.reserveTerminal termName fd return t- where- mGetEnv var = do- mv <- liftIO $ try $ getEnv var- case mv of- Left (_e :: SomeException) -> return $ Nothing- Right v -> return $ Just v+outputForConfig config = mappend config <$> standardIOConfig >>= outputForConfig -- | Sets the cursor position to the given output column and row. --
src/Graphics/Vty/Output/Interface.hs view
@@ -247,9 +247,9 @@ | otherwise = SetTo $ ISOColor v clampColor' (Color240 v) -- TODO: Choose closes ISO color?- | contextColorCount t < 8 = Default- | contextColorCount t < 16 = Default- | contextColorCount t == 240 = SetTo $ Color240 v+ | contextColorCount t < 8 = Default+ | contextColorCount t < 16 = Default+ | contextColorCount t <= 256 = SetTo $ Color240 v | otherwise = let p :: Double = fromIntegral v / 240.0 v' = floor $ p * (fromIntegral $ contextColorCount t)
src/Graphics/Vty/Output/MacOSX.hs view
@@ -15,23 +15,23 @@ import qualified Graphics.Vty.Output.TerminfoBased as TerminfoBased import Control.Applicative+import Control.Monad (void) import Control.Monad.Trans -import System.IO+import System.Posix.IO (fdWrite)+import System.Posix.Types (Fd) -- | for Terminal.app the terminal identifier "xterm" is used. For iTerm.app the terminal identifier -- "xterm-256color" is used. -- -- This effects the terminfo lookup.-reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Handle -> m Output-reserveTerminal v outHandle = do+reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> m Output+reserveTerminal v outFd = do let remapTerm "iTerm.app" = "xterm-256color" remapTerm _ = "xterm" flushedPut :: String -> IO ()- flushedPut str = do- hPutStr outHandle str- hFlush outHandle- t <- TerminfoBased.reserveTerminal (remapTerm v) outHandle+ flushedPut = void . fdWrite outFd+ t <- TerminfoBased.reserveTerminal (remapTerm v) outFd return $ t { terminalID = terminalID t ++ " (Mac)" , reserveDisplay = terminalAppReserveDisplay flushedPut , releaseDisplay = terminalAppReleaseDisplay flushedPut
src/Graphics/Vty/Output/TerminfoBased.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS_GHC -D_XOPEN_SOURCE=500 #-}+{-# OPTIONS_GHC -D_XOPEN_SOURCE=500 -fno-warn-warnings-deprecations #-} {-# CFILES gwinsz.c #-} -- | Terminfo based terminal handling. --@@ -15,7 +15,7 @@ import Graphics.Vty.Prelude -import qualified Data.ByteString as BS+import Data.ByteString.Internal (toForeignPtr) import Data.Terminfo.Parse import Data.Terminfo.Eval @@ -32,31 +32,14 @@ import Data.IORef import Data.Maybe (isJust, isNothing, fromJust) import Data.Monoid+import Data.Word import Foreign.C.Types ( CInt(..), CLong(..) )--import GHC.IO.Handle-#ifdef __GLASGOW_HASKELL__-#if __GLASGOW_HASKELL__ >= 611-import GHC.IO.Handle.Internals (withHandle_)-import GHC.IO.Handle.Types (Handle__(..))-import qualified GHC.IO.FD as FD--- import qualified GHC.IO.Handle.FD as FD-import GHC.IO.Exception-import Data.Typeable (cast)-#else-import GHC.IOBase-import GHC.Handle hiding (fdToHandle)-import qualified GHC.Handle-#endif-#endif+import Foreign.ForeignPtr (withForeignPtr)+import Foreign.Ptr (Ptr, plusPtr) import qualified System.Console.Terminfo as Terminfo-#ifdef __GLASGOW_HASKELL__-#if __GLASGOW_HASKELL__ >= 611-import System.IO.Error-#endif-#endif +import System.Posix.IO (fdWriteBuf) import System.Posix.Types (Fd(..)) data TerminfoCaps = TerminfoCaps @@ -86,6 +69,19 @@ , enterBoldMode :: Maybe CapExpression } +-- kinda like: https://code.google.com/p/vim/source/browse/src/fileio.c#10422+-- fdWriteBuf will throw on error. Unless the error is EINTR. On EINTR the write will be retried.+fdWriteAll :: Fd -> Ptr Word8 -> Int -> Int -> IO Int+fdWriteAll outFd ptr len count+ | len < 0 = fail "fdWriteAll: len is less than 0"+ | len == 0 = return count+ | otherwise = do+ writeCount <- fromEnum <$> fdWriteBuf outFd ptr (toEnum len)+ let len' = len - writeCount+ ptr' = ptr `plusPtr` writeCount+ count' = count + writeCount+ fdWriteAll outFd ptr' len' count'+ sendCapToTerminal :: Output -> CapExpression -> [CapParam] -> IO () sendCapToTerminal t cap capParams = do outputByteBuffer t $ writeToByteString $ writeCapExpr cap capParams@@ -101,9 +97,9 @@ - todo: Some display attributes like underline and bold have independent string capabilities that - should be used instead of the generic "sgr" string capability. -}-reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Handle -> m Output-reserveTerminal inID outHandle = liftIO $ do- ti <- Terminfo.setupTerm inID+reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> m Output+reserveTerminal termName outFd = liftIO $ do+ ti <- Terminfo.setupTerm termName -- assumes set foreground always implies set background exists. -- if set foreground is not set then all color changing style attributes are filtered. msetaf <- probeCap ti "setaf"@@ -113,14 +109,14 @@ Just setaf -> (False, False, setaf) Nothing -> case msetf of Just setf -> (False, True, setf)- Nothing -> (True, True, error $ "no fore color support for terminal " ++ inID)+ Nothing -> (True, True, error $ "no fore color support for terminal " ++ termName) msetab <- probeCap ti "setab" msetb <- probeCap ti "setb" let set_back_cap = case msetab of Nothing -> case msetb of Just setb -> setb- Nothing -> error $ "no back color support for terminal " ++ inID+ Nothing -> error $ "no back color support for terminal " ++ termName Just setab -> setab terminfoCaps <- pure TerminfoCaps <*> probeCap ti "smcup"@@ -138,28 +134,30 @@ <*> currentDisplayAttrCaps ti newAssumedStateRef <- newIORef initialAssumedState let t = Output- { terminalID = inID+ { terminalID = termName , releaseTerminal = liftIO $ do sendCap setDefaultAttr [] maybeSendCap cnorm []- hClose outHandle , reserveDisplay = liftIO $ do -- If there is no support for smcup: Clear the screen and then move the mouse to the -- home position to approximate the behavior. maybeSendCap smcup []- hFlush outHandle sendCap clearScreen [] , releaseDisplay = liftIO $ do maybeSendCap rmcup [] maybeSendCap cnorm [] , displayBounds = do- rawSize <- liftIO $ withFd outHandle getWindowSize+ rawSize <- liftIO $ getWindowSize outFd case rawSize of (w, h) | w < 0 || h < 0 -> fail $ "getwinsize returned < 0 : " ++ show rawSize | otherwise -> return (w,h) , outputByteBuffer = \outBytes -> do- BS.hPut outHandle outBytes- hFlush outHandle+ let (fptr, offset, len) = toForeignPtr outBytes+ actualLen <- withForeignPtr fptr+ $ \ptr -> fdWriteAll outFd (ptr `plusPtr` offset) len 0+ when (toEnum len /= actualLen) $ fail $ "Graphics.Vty.Output: outputByteBuffer "+ ++ "length mismatch. " ++ show len ++ " /= " ++ show actualLen+ ++ " Please report this bug to vty project." , contextColorCount = case supportsNoColors terminfoCaps of False -> case Terminfo.getCapability ti (Terminfo.tiGetNum "colors" ) of@@ -262,7 +260,7 @@ -- This equation implements the above logic. -- -- \todo This assumes the removal of color changes in the display attributes is done as expected--- with noColors == True. See `limit_attr_for_display`+-- with noColors == True. See `limitAttrForDisplay` -- -- \todo This assumes that fewer state changes, followed by fewer bytes, is what to optimize. I -- haven't measured this or even examined terminal implementations. *shrug*@@ -462,22 +460,3 @@ then [] else [op] --- from https://patch-tag.com/r/mae/sendfile/snapshot/current/content/pretty/src/Network/Socket/SendFile/Internal.hs--- The Fd should not be used after the action returns because the--- Handler may be garbage collected and than will cause the finalizer--- to close the fd.-withFd :: Handle -> (Fd -> IO a) -> IO a-#ifdef __GLASGOW_HASKELL__-#if __GLASGOW_HASKELL__ >= 611-withFd h f = withHandle_ "withFd" h $ \ Handle__{..} -> do- case cast haDevice of- Nothing -> ioError (ioeSetErrorString (mkIOError IllegalOperation- "withFd" (Just h) Nothing)- "handle is not a file descriptor")- Just fd -> f (Fd (fromIntegral (FD.fdFD fd)))-#else-withFd h f =- withHandle_ "withFd" h $ \ h_ ->- f (Fd (fromIntegral (haFD h_)))-#endif-#endif
src/Graphics/Vty/Output/XTermColor.hs view
@@ -9,23 +9,23 @@ import Blaze.ByteString.Builder.Word (writeWord8) import Control.Applicative+import Control.Monad (void) import Control.Monad.Trans import Data.Foldable (foldMap) -import System.IO+import System.Posix.IO (fdWrite)+import System.Posix.Types (Fd) -- | Initialize the display to UTF-8. -reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Handle -> m Output-reserveTerminal variant outHandle = liftIO $ do- let flushedPut str = do- hPutStr outHandle str- hFlush outHandle+reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> m Output+reserveTerminal variant outFd = liftIO $ do+ let flushedPut = void . fdWrite outFd -- If the terminal variant is xterm-color use xterm instead since, more often than not, -- xterm-color is broken. let variant' = if variant == "xterm-color" then "xterm" else variant flushedPut setUtf8CharSet- t <- TerminfoBased.reserveTerminal variant' outHandle+ t <- TerminfoBased.reserveTerminal variant' outFd let t' = t { terminalID = terminalID t ++ " (xterm-color)" , releaseTerminal = do
src/Graphics/Vty/PictureToSpans.hs view
@@ -1,9 +1,10 @@ -- Copyright Corey O'Connor<coreyoconnor@gmail.com>-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-} {- | Transforms an image into rows of operations. -} module Graphics.Vty.PictureToSpans where@@ -18,7 +19,12 @@ import Control.Lens hiding ( op ) import Control.Monad.Reader import Control.Monad.State.Strict hiding ( state )++#if __GLASGOW_HASKELL__ < 708 import Control.Monad.ST.Strict hiding ( unsafeIOToST )+#else+import Control.Monad.ST.Strict+#endif import qualified Data.Vector as Vector hiding ( take, replicate ) import Data.Monoid (mappend)
test/Verify.hs view
@@ -84,7 +84,7 @@ ++ [ toEnum 0x309B .. toEnum 0xA4CF ] liftIOResult :: Testable prop => IO prop -> Property-liftIOResult = morallyDubiousIOProperty+liftIOResult = ioProperty #if __GLASGOW_HASKELL__ <= 701 instance Random Word where
test/VerifyConfig.hs view
@@ -4,19 +4,12 @@ import Graphics.Vty.Config import Graphics.Vty.Input.Events -import Control.Applicative-import Control.Exception-import Control.Lens ((^.))-import Control.Monad- import Data.Default import Data.String.QQ import Test.Framework import Test.Framework.Providers.HUnit import Test.HUnit hiding (Test)--import Text.Printf exampleConfig :: String exampleConfig = [s|
test/VerifyOutput.hs view
@@ -12,9 +12,11 @@ import Control.Monad +import Data.Default+ import qualified System.Console.Terminfo as Terminfo import System.Posix.Env-import System.IO+import System.Posix.IO tests :: IO [Test] tests = concat <$> forM terminalsOfInterest (\termName -> do@@ -45,8 +47,8 @@ smokeTestTerm :: String -> Image -> IO Result smokeTestTerm termName i = do- nullOut <- openFile "/dev/null" WriteMode- t <- outputForNameAndIO termName nullOut+ nullOut <- openFd "/dev/null" WriteOnly Nothing defaultFileFlags+ t <- outputForConfig $ def { outputFd = Just nullOut, termName = Just termName } -- putStrLn $ "context color count: " ++ show (contextColorCount t) reserveDisplay t dc <- displayContext t (100,100)@@ -59,5 +61,6 @@ showCursor t releaseDisplay t releaseTerminal t+ closeFd nullOut return succeeded
test/VerifyUsingMockInput.hs view
@@ -110,7 +110,12 @@ (writeFd, readFd) <- openPseudoTerminal (setTermAttr,_) <- attributeControl readFd setTermAttr- input <- initInputForFd def "dummy" table readFd+ let testConfig = def { inputFd = Just readFd+ , termName = Just "dummy"+ , vmin = Just 1+ , vtime = Just 100+ }+ input <- initInput testConfig table eventsRef <- newIORef [] let writeWaitClose = do synthesizeInput inputSpec writeFd
vty.cabal view
@@ -1,5 +1,5 @@ name: vty-version: 5.1.4+version: 5.2.0 license: BSD3 license-file: LICENSE author: AUTHORS@@ -137,7 +137,7 @@ hs-source-dirs: test test-module: VerifyAttributeOps build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -169,7 +169,7 @@ Verify.Graphics.Vty.Output build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -202,12 +202,13 @@ Verify.Graphics.Vty.Output build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5, bytestring, containers,+ data-default >= 0.5.3, deepseq >= 1.1 && < 1.4, mtl >= 1.1.1.0 && < 2.3, terminfo >= 0.3 && < 0.5,@@ -235,7 +236,7 @@ Verify.Graphics.Vty.Span build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -261,7 +262,7 @@ other-modules: Verify build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -288,7 +289,7 @@ Verify.Graphics.Vty.Output build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -318,7 +319,7 @@ Verify.Graphics.Vty.Image build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -346,7 +347,7 @@ Verify.Graphics.Vty.Image build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -373,7 +374,7 @@ Verify.Graphics.Vty.Output build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -401,7 +402,7 @@ Verify.Graphics.Vty.Output build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -433,7 +434,7 @@ Verify.Graphics.Vty.Span build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -465,7 +466,7 @@ Verify.Graphics.Vty.Span build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -497,7 +498,7 @@ Verify.Graphics.Vty.Span build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -523,7 +524,7 @@ other-modules: Verify build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, QuickCheck >= 2.4, random == 1.0.*, base >= 4 && < 5,@@ -547,7 +548,7 @@ main-is: VerifyUsingMockInput.hs build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, data-default >= 0.5.3, QuickCheck >= 2.4, smallcheck == 1.*,@@ -580,7 +581,7 @@ main-is: VerifyConfig.hs build-depends: vty,- Cabal == 1.20.*,+ Cabal >= 1.18, data-default >= 0.5.3, HUnit, QuickCheck >= 2.4,