customMain: restore initial terminal input state on shutdown

This change ensures that the initial terminal input state flags are
restored on shutdown (regardless of exceptions) even if changes to the
state flags have occurred via 'suspendAndResume'. Prior to this change,
breaking changes to the input state flags in the last 'suspendAndResume'
before program exit were propagated to the end user's terminal
environment which could lead to broken or garbled terminal I/O.

For more details, see

https://github.com/jtdaugherty/vty/issues/187

This change also increases the lower bound on 'vty' to get access to the
API required to support this behavior.
This commit is contained in:
Jonathan Daugherty 2020-07-09 15:10:12 -07:00
parent 9ab39545a2
commit a8e48d9655
2 changed files with 17 additions and 1 deletions

View File

@ -114,7 +114,7 @@ library
Brick.Widgets.Internal
build-depends: base <= 4.14.0.0,
vty >= 5.24,
vty >= 5.29,
transformers,
data-clist >= 0.1,
directory >= 1.2.5.0,

View File

@ -75,6 +75,8 @@ import Graphics.Vty
, nextEvent
, mkVty
, defaultConfig
, restoreInputState
, inputIface
)
import Graphics.Vty.Attributes (defAttr)
@ -202,6 +204,10 @@ runWithVty vty brickChan mUserChan app initialRS initialSt = do
-- | The custom event loop entry point to use when the simpler ones
-- don't permit enough control. Returns the final application state
-- after the application halts.
--
-- Note that this function guarantees that the terminal input state
-- prior to the first Vty initialization is the terminal input state
-- that is restored on shutdown (regardless of exceptions).
customMain :: (Ord n)
=> Vty
-- ^ The initial Vty handle to use.
@ -221,8 +227,13 @@ customMain :: (Ord n)
-- ^ The initial application state.
-> IO s
customMain initialVty buildVty mUserChan app initialAppState = do
let restoreInitialState = restoreInputState $ inputIface initialVty
(s, vty) <- customMainWithVty initialVty buildVty mUserChan app initialAppState
`E.catch` (\(e::E.SomeException) -> restoreInitialState >> E.throw e)
shutdown vty
restoreInitialState
return s
-- | Like 'customMain', except the last 'Vty' handle used by the
@ -526,5 +537,10 @@ halt = return . Halt
-- specified action. When it returns an application state value, restore
-- the terminal state, empty the rendering cache, redraw the application
-- from the new state, and resume the event loop.
--
-- Note that any changes made to the terminal's input state are ignored
-- when Brick resumes execution and are not preserved in the final
-- terminal input state after the Brick application returns the terminal
-- to the user.
suspendAndResume :: IO s -> EventM n (Next s)
suspendAndResume = return . SuspendAndResume