How can I test whether stdin has input available in julia?

Viewed 434

I would like to detect whether there is input on stdin in a short time window, and continue execution either way, with the outcome stored in a Bool. (My real goal is to implement a pause button on a simulation that runs in the terminal. A second keypress should unpause the program, and it should continue executing.) I have tried to use poll_fd but it does not work on stdin:

julia> FileWatching.poll_fd(stdin, readable=true)
ERROR: MethodError: no method matching poll_fd(::Base.TTY; readable=true)

Is there a way that will work on julia? I have found a solution that works in python, and I have considered using this via PyCall, but I am looking for

  1. a cleaner, pure-julia way; and
  2. a way that does not fight or potentially interfere with julia's use of libuv.
2 Answers
bytesavailable(stdin)

Here is a sample usage. Note that if you capture the keyboard you also need to handle Ctrl+C yourself (in this example only the first byte of chunk is checked).

If you want to run it fully asynchronously put @async in front of the while loop. However if there will be no more code in this case this program will just exit.

import REPL
term = REPL.Terminals.TTYTerminal("xterm",stdin,stdout,stderr)
REPL.Terminals.raw!(term,true)
Base.start_reading(stdin)

while (true)
    sleep(1)
    bb = bytesavailable(stdin)
    if bb > 0
        data = read(stdin, bb)
        if data[1] == UInt(3)
            println("Ctrl+C - exiting")
            exit()
        end
        println("Got $bb bytes: $(string(data))")
    end
end

Following @Przemyslaw Szufel's response, here is a full solution that allows a keypress to pause/unpause the iteration of a loop:

import REPL
term = REPL.Terminals.TTYTerminal("xterm",stdin,stdout,stderr)
REPL.Terminals.raw!(term,true)
Base.start_reading(stdin)

function read_and_handle_control_c()
    b = bytesavailable(stdin)
    @assert b > 0
    data = read(stdin, b)
    if data[1] == UInt(3)
        println("Ctrl+C - exiting")
        exit()
    end
    nothing
end

function check_for_and_handle_pause()
    if bytesavailable(stdin) > 0
        read_and_handle_control_c()
        while bytesavailable(stdin) == 0
            sleep(0.1)
        end
        read_and_handle_control_c()
    end
    nothing
end

while true
    # [do stuff]
    sleep(0.05)
    check_for_and_handle_pause()
end

This is somewhat suboptimal in that it requires the process to wake up regularly even when paused, but it achieves my goal nevertheless.

Related