brick icon indicating copy to clipboard operation
brick copied to clipboard

Strange suspend and halt behavior on MacOS, when using /dev/tty as inputFd

Open danchoi opened this issue 2 years ago • 6 comments

This github project demonstrates an issue I've discovered with suspending and halting in Brick.

I modified Brick's demo program brick-suspend-resume-demo to use the /dev/tty handle directly as the configured inputFd:

-- void $ defaultMain theApp initialState
ttyHandle <- openFile "/dev/tty" ReadMode
ttyFd <- IO.handleToFd ttyHandle
defConfig <- V.standardIOConfig
let builder = V.mkVty $ defConfig {
                V.inputFd = Just ttyFd
              }
initialVty <- builder
void $ customMain initialVty builder Nothing theApp initialState

The reason I wanted to do this was so I can use data piped from STDIN into a Brick program. Because the inputFd is assigned to stdin by default, I need it to be assigned explicitly to /dev/tty in order to make the terminal UI interactive.

If you compile and run my demo executable in the Ubuntu Terminal (tested on Ubuntu 20.04), pressing SPACE once suspends and pressing ESC once quits -- just like the original demo.

If you run the executable in the MacOS Terminal (macOS Montery, Apple M1 Pro), pressing SPACE or ESC once isn't sufficient. You need to press another key afterward to make the suspend or quit command register. Is this bug, or is there something wrong with my Haskell code?

danchoi avatar Apr 06 '22 22:04 danchoi

As I look at the snippet you provided above, I wonder if this will work as intended if you only ever open /dev/tty once. The way that code is written, you hold on to a handle to the terminal while Vty uses it. I don’t recall off hand what it’s going to do to the input descriptor, but my hunch is that it would be best to open /dev/tty each time a new Vty handle is built rather than once as is done above. Since Vty might change input buffering and other behaviors, it may make assumptions about the file handle it is given. Since Vty and Brick are going to evaluate the builder action each time the terminal needs to be controlled again, the code above will result in your ttyFd getting re-used across Vty builds, and that might cause problems.

So my suggestion is to take the two lines starting with ttyHandle <- … and move them into builder and see if that helps. If not, we can dig in further.

jtdaugherty avatar Apr 13 '22 07:04 jtdaugherty

@danchoi I wanted to check in on this again and see if you have had any time to try out my suggestion above.

jtdaugherty avatar Apr 27 '22 03:04 jtdaugherty

@jtdaugherty sorry for the delay. Thank you for your suggestion, but when trying it the issue I reported still happens. Here is the modified code:

main = do
    -- void $ defaultMain theApp initialState
    defConfig <- V.standardIOConfig
    let builder = do
                    ttyHandle <- openFile "/dev/tty" ReadMode
                    ttyFd <- IO.handleToFd ttyHandle
                    V.mkVty $ defConfig {
                      V.inputFd = Just ttyFd
                    }
    initialVty <- builder
    void $ customMain initialVty builder Nothing theApp initialState

danchoi avatar May 03 '22 23:05 danchoi

Thanks! I'm doing some investigation. You can check out my minimal demo program here:

https://github.com/jtdaugherty/brick-suspend-issue/blob/main/Main2.hs

I think that the program shows that it isn't a Brick or Vty problem (although once I understand the problem, then we'll see where the solution belongs; maybe in Vty). That demo program is the essence of what is going on inside of Vty when you are having the problem that you reported in this ticket. That demo program behaves the same for its stdin and tty arguments on Linux (analogous to your report), but on MacOS the behavior changes in such a way that it results in the issue you're seeing. I'll let you know when I find out more about what is going on.

jtdaugherty avatar May 04 '22 14:05 jtdaugherty

Thank you very much @jtdaugherty for investigating this.

danchoi avatar May 04 '22 14:05 danchoi

There's now this related GHC ticket: https://gitlab.haskell.org/ghc/ghc/-/issues/21503

jtdaugherty avatar May 05 '22 15:05 jtdaugherty