I have a “simple” problem, but I can’t seem to find a simple solution.
Basically what I want is to monitor key strokes for user input in an interactive application, with a couple requirements:
it should not “hang” the application when the user is not providing an input
it should not disrupt the terminal output while it’s running (I’m working on a Terminal application). For example the user input should not be displayed.
I’ve seen a few solutions like this and others on SO, and had a look at how REPL.TerminalMenus does it, but none of it seems to work yet.
I think I’ve found a solution to make it work without halting, using byteavailable before reading from stdin. Though for some reason this seems to only work with Base.start_reading(stdin), otherwise it returns 0.
This seems to work fine in a normal Terminal, though it’s not working in VSCode yet
Then perhaps it’s up to vscode extension and probably those issues are not of interest. Maybe you should metion more detail things what you doing, exactly you want.
You could run a get-input loop asynchronously from the main application task. E.g. the loop could read(stdin, Char) continually and put it into a Channel, which the main task could check periodically.
I’m working on Term.jl adding interactive terminal visualizations.
Basically, you have a function that creates some output to the terminal which gets updated continuously, and I need to capture user input to modify what gets displayed.
For example in the example below:
I have a Pager display which shows some (random) text and the user can move up and down using the arrow keys. So I want to capture the user input to modify what gets displayed.
To do that , I start with:
Base.start_reading(stdin)
# prepare terminal
raw_mode_enabled = try
REPL.Terminals.raw!(terminal, true)
true
catch err
@warn "Unable to enter raw mode: " exception=(err, catch_backtrace())
false
end
# hide the cursor
raw_mode_enabled && print(terminal.out_stream, "\x1b[?25l")
and then in the code that handles updating the display I call this function
function keyboard_input(live::AbstractLiveDisplay)
if bytesavailable(terminal.in_stream) > 0
c = readkey(terminal.in_stream) |> Int
return if Int(c) in keys(KEYs)
out = key_press(live, KEYs[Int(c)])
out isa Bool && return out
return true
else
Char(c)
end
end
return true
end
using bytesavailable I can avoid calling readkey if there’s no input so I don’t end up halting the program. In a normal terminal it all works correctly: the user input is not shown but can be captured to change the display.
In VSCode it doesn’t work at all… the user input is not detected, but rather it messes up the REPL - not sure how to get around that
In VSCode, it makes a difference whether you run code blocks from the editor or type commands directly into the repl. This seems to be due to the communication pipeline and still sometimes trips me up, things like Infiltrator should always be run from the repl. Does that make a difference for you here?
The terminal part from Gameoji from @c42f could be of inspiration too. I’ve highjacked some part of it for what I’m very sporadically (read: I would if I had more time) playing with in TUIseer. I’m trying to develop some sort of modal tui there.