# Timed wait for key press

**URL:** https://discourse.julialang.org/t/timed-wait-for-key-press/100856
**Category:** General Usage
**Created:** [June 26, 2023, 6:50pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856 "2023-06-26T18:50:06Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 26, 2023, 6:50pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/1 "2023-06-26T18:50:06Z")

</div>

I am new to Julia but I can’t seem to find out how to do this…  
I want a function that allows an input of number of seconds to wait for a key to be pressed, if zero it returns the next key pressed in the buffer. Then it returns info as to what key was pressed, ASCII is ok but the Ins (Insert) key returns 27 + something else. The Esc (Escape) key returns just 27. The function keys don’t return ASCII but something else. This would be similar to inkey() in FoxPlus (if anybody knows what that is) which doesn’t work quite the way I want it to, either. The return can be 1-127 for ASCII and higher numbers for the rest of the keys. I am not looking for just the Shift, Ctrl, or Alt keys by themselves, but I want the up-arrow, down-arrow, Home, PgUp, etc. to return info as to which one was pressed. It would be nice if I could clear the type ahead buffer when I want to but I can’t find a command that does that. Maybe I just don’t know where to look.

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [June 26, 2023, 6:54pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/2 "2023-06-26T18:54:29Z")

</div>

This isn’t so much a language thing as it is an operating system / graphics framework sort of thing.

What sort of context do you want this to work/run in?

---

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 26, 2023, 7:13pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/3 "2023-06-26T19:13:59Z")

</div>

This is to be a terminal type interface, wasn’t even thinking of mouse interaction, using Linux as the server. I hadn’t even looked at a graphics interface. The programs i’m trying to convert are written in FoxPlus, totally text, no graphics.

---

<div class="post-metadata">

### Author: ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)
#### Post date: [June 27, 2023, 12:52am UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/4 "2023-06-27T00:52:29Z")

</div>

I think I have a rough idea of what you’re talking about, I’m on my phone but can give a more full answer tomorrow. But here is my rough idea:

```julia
println("Please enter a key")

sleep(2)
if bytesavailable(stdin) > 0 # if there are any bytes, there has been terminal input
    println("input:") 
    while bytesavailable(stdin) > 0
        c = read(stdin, Char)
        # different keys like home or arrow keys are mapped to different sequences of characters
        print(escape_string(string(c)))
    end
    println()
else
    println("No input") 
end

```

This code likely doesn’t work as I haven’t tested it, but this is my rough idea.

In order to determine which key has been hit, you would need to test each key and see what character sequence it corresponds to.

Not sure it’s much help, I’ll try to revisit this and give a more thorough answer when I’m at my computer.

---

<div class="post-metadata">

### Author: ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)
#### Post date: [June 27, 2023, 12:59am UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/5 "2023-06-27T00:59:11Z")

</div>

Yes, I just tried it out over an ssh connection and it’s not behaving like I thought. If this is still unsolved tomorrow I’ll try to provide a better answer.

It is certainly possible though; this is done in the package VimBindings.jl. As you say, the Escape key sends a special character, whereas (for example) an arrow key sends that same special character followed by a character sequence. In order to tell whether the key was just an Escape key press or another key, the terminal must wait to see if any new data comes in.

---

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 27, 2023, 4:17am UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/6 "2023-06-27T04:17:31Z")

</div>

c = read(stdin, Char)  
doesn’t like BackSpace

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [June 27, 2023, 3:03pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/7 "2023-06-27T15:03:40Z")

</div>

Right, that’s exactly why I talked about operating systems and graphical toolkits. Command line interfaces (regardless of the language) only get the _text_ that the controlling terminal sends them. This can _sometimes_ include _some_ control chars like backspace, but it’s very configuration-dependent in the terminal itself. If you need to get the raw keypresses, you typically need to have a framework that talks more directly with the operating system, like a [graphics framework](https://docs.makie.org/stable/documentation/events/#the_events_struct) or entire [windowing system](https://juliagraphics.github.io/Gtk.jl/latest/) or [web frontend](https://github.com/JuliaGizmos/Blink.jl) or somesuch.

---

<div class="post-metadata">

### Author: ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)
#### Post date: [June 27, 2023, 7:43pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/8 "2023-06-27T19:43:03Z")

</div>

I’ve been able to sit down at my machine and figure this out, I think.

Perhaps surprisingly, this actually doesn’t relate to graphical toolkits (although it is somewhat related to operating systems and the way an OS terminal handles characters).

> [@mbauman](#):
>
> Command line interfaces (regardless of the language) only get the _text_ that the controlling terminal sends them.

This is not the case; programs which are run at the terminal are able to access all bytes sent to `stdin` by setting the terminal to “raw mode”, also called “non-canonical mode”. In this mode, the terminal sends input byte-by-byte to the program, with no buffering or processing of keys or control sequences. Even the Julia REPL [runs in raw mode](https://github.com/JuliaLang/julia/blob/0e147eb8be67898afb424a900df96229ad558997/stdlib/REPL/src/LineEdit.jl#L2734), and the REPL code processes and handles control sequences byte for byte.

Still, that doesn’t solve the ability to read the raw terminal input directly from `stdin`. While julia code can call `read(stdin, Char)`, it seems to only be able to do with line-by-line buffering, effectively in “canonical” mode (also called “cooked” mode). But the code below uses the utility function `with_raw_tty` to set the input to raw mode, and read raw bytes directly from the terminal, adapted from the function `getpass` in [base/utils.jl](https://github.com/JuliaLang/julia/blob/0e147eb8be67898afb424a900df96229ad558997/base/util.jl#L343)

Just as a warning, this bypasses all of the processing and safety measures Julia offers, so `Ctrl-c` does not work! You are reading directly from the terminal input. In this example, you can escape the loop with the `Enter` key.

```julia

function raw_input_example()
    # based on the function getpass in base/utils.jl#343
    Base.with_raw_tty(stdin) do
        while true
            c = Base._getch() # get raw character. ONLY works in with_raw_tty
            print("Byte: $c\r\n")
            print("Char: " * escape_string(string(Char(c))) * "\r\n\n")
            # use Enter to escape this loop
            if c == 0xff || c == UInt8('\n') || c == UInt8('\r') || c == 0x04
                break # EOF or return
            end
        end
    end
end

```

And here is the output when I strike the following keys on my keyboard: `a b c <Backspace> <Up Arrow> <Escape> <Enter>`, with annotated comments. Note that this successfully records the `<Backspace>` key strike. Also note the full control sequence with the `<Up Arrow>` key strike.

```julia
julia> raw_input_example()
Byte: 97
Char: a

Byte: 98
Char: b

Byte: 99
Char: c

Byte: 127 # Backspace
Char: \x7f

Byte: 27 # Up Arrow
Char: \e

Byte: 91
Char: [

Byte: 65
Char: A

Byte: 27 # Escape
Char: \e

Byte: 13 # Enter
Char: \r

```

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [June 27, 2023, 8:17pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/9 "2023-06-27T20:17:02Z")

</div>

Ah yes, I had forgotten about raw mode! That won’t get the fine-grained keydown/keyup events or the modifier keys by themselves as I understand it, though. Not sure if that’s a requirement here, but that’s why my head went to UI frameworks.

---

<div class="post-metadata">

### Author: ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)
#### Post date: [June 27, 2023, 8:26pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/10 "2023-06-27T20:26:09Z")

</div>

> [@mbauman](#):
>
> keydown/keyup events or the modifier keys by themselves as I understand it, though

Ah yes definitely - not something you could write a more modern UI application with for sure. And you are right, no modifier key events, unlike something like an `X11` application.

---

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 27, 2023, 9:16pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/11 "2023-06-27T21:16:18Z")

</div>

Based on the information you gave me last time I wrote a function that works like a little bit like inkey() in FoxPlus except it returns strings instead of numbers. If you call it with inkey(“”) it checks for a key pressed one time and returns with “” (empty string), if no key has been pressed. If a letter key (32-127) was pressed, it returns with that letter. If the tab key was pressed it returns with “Tab”, likewise for “Ctrl-A” - “Ctrl-Z” except for “BackSpace” and “Enter”, function keys are returned with “F1” to “F12”, likewise for “Ins” “Del” “Home” “End” “PgUp” “PgDn” “UpArrow” “DnArrow” “LeftArrow” “RightArrow” If called with inkey(“60”) it will only wait for a key for 60 seconds, if called with inkey(“0”) it will wait for ever. The text file is 192 lines long and using include(inkey\_function.jl") allowed me to fix typos. Now I’m trying to figure out how to get it into REPL to try it out (I am new to julia)

---

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 27, 2023, 10:22pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/12 "2023-06-27T22:22:36Z")

</div>

How come c = read(stdin, Char) quit working? It didn’t used to require Enter… Even closed REPL an reloaded it to n avail… by the way, Caleb-Allen, your routine doesn’t differentiate between “A” and “F1” or “Home” (excuse me, it does work, I was only seeing the last byte) apparently bytesavailable doesn’t work. I have it sleep for a second and then try bytesavailable(stdin) \> 0 and it fails

---

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 28, 2023, 4:26pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/13 "2023-06-28T16:26:06Z")

</div>

Using your info, Caleb, and the info at

> <https://stackoverflow.com/questions/60954235/how-can-i-test-whether-stdin-has-input-available-in-julia>

I was able to write the function inkey() the way I want it to work. Somebody else might like this function, where do I put it? it’s 186 lines.

---

<div class="post-metadata">

### Author: ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)
#### Post date: [June 28, 2023, 5:17pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/15 "2023-06-28T17:17:17Z")

</div>

I’m glad to hear you got it working!

You can place the code directly in this thread and putting three tic marks (```) before and after it to indicate that it is a code snippet, that way it will be scrollable.

You could also upload it to [https://gist.github.com/](https://gist.github.com/) if you have a github account, and get a sharable link.

---

<div class="post-metadata">

### Author: ![Jim](https://avatars.discourse-cdn.com/v4/letter/j/ac91a4/32.png) [@Jim](https://discourse.julialang.org/u/Jim)
#### Post date: [June 28, 2023, 5:36pm UTC](https://discourse.julialang.org/t/timed-wait-for-key-press/100856/16 "2023-06-28T17:36:26Z")

</div>

```julia
function inkey(s)
    #=
    requires import REPL before using
    This is for a single key stroke and returns what key it was
    based on a terminal setting in PuTTY of Ctrl-H for BackSpace and
    Linux function keys
    s is a string
         if null, checks bytesavailable one time
         if parse(Int, s) == 0 , has no time out
         otherwise, time out after parse(Int, s) seconds
    key pressed is returned as:
         Key Char
         number return
         ------ ------
         1 - 7 Ctrl-A to Ctrl-G
         8 BackSpace (Ctrl-H)
         9 Tab (Ctrl-I)
         13 Enter (Ctrl-M)
         11 - 26 Ctrl-K to Ctrl-Z
         0x1b Esc
         0x1b 0x5b 0x31 0x31 0x7e F1
         0x1b 0x5b 0x31 0x32 0x7e F2
         0x1b 0x5b 0x31 0x33 0x7e F3
         0x1b 0x5b 0x31 0x34 0x7e F4
         0x1b 0x5b 0x31 0x35 0x7e F5
         0x1b 0x5b 0x31 0x37 0x7e F6
         0x1b 0x5b 0x31 0x38 0x7e F7
         0x1b 0x5b 0x31 0x39 0x7e F8
         0x1b 0x5b 0x32 0x30 0x7e F9
         0x1b 0x5b 0x32 0x31 0x7e F10
         0x1b 0x5b 0x32 0x33 0x7e F11
         0x1b 0x5b 0x32 0x34 0x7e F12
         0x1b 0x5b 0x32 0x7e Ins
         0x1b 0x5b 0x33 0x7e Del
         0x1b 0x5b 0x31 0x7e Home
         0x1b 0x5b 0x34 0x7e End
         0x1b 0x5b 0x35 0x7e PgUp
         0x1b 0x5b 0x36 0x7e PgDn
         0x1b 0x5b 0x41 UpArrow
         0x1b 0x5b 0x42 DnArrow
         0x1b 0x5b 0x43 RightArrow
         0x1b 0x5b 0x44 LeftArrow
    =#
    seconds = -1
    term = REPL.Terminals.TTYTerminal("xterm",stdin,stdout,stderr)
    REPL.Terminals.raw!(term,true)
    Base.start_reading(stdin)
    while true
        sleep(.1)
        ba = bytesavailable(stdin)
        if ba > 0
            str = read(stdin, ba)
            if str[1] == 0x1b
                # We have an Escape
                if length(str) == 1
                   return "ESC"
                end
                if length(str) > 4
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x31 &&
                                         str[5] == 0x7e
                        return "F1"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x32 &&
                                         str[5] == 0x7e
                        return "F2"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x33 &&
                                         str[5] == 0x7e
                        return "F3"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x34 &&
                                         str[5] == 0x7e
                        return "F4"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x35 &&
                                         str[5] == 0x7e
                        return "F5"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x37 &&
                                         str[5] == 0x7e
                        return "F6"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x38 &&
                                         str[5] == 0x7e
                        return "F7"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x39 &&
                                         str[5] == 0x7e
                        return "F8"
                    end
                    if str[2] == 0x5b && str[3] == 0x32 && str[4] == 0x30 &&
                                         str[5] == 0x7e
                        return "F9"
                    end
                    if str[2] == 0x5b && str[3] == 0x32 && str[4] == 0x31 &&
                                         str[5] == 0x7e
                        return "F10"
                    end
                    if str[2] == 0x5b && str[3] == 0x32 && str[4] == 0x33 &&
                                         str[5] == 0x7e
                        return "F11"
                    end
                    if str[2] == 0x5b && str[3] == 0x32 && str[4] == 0x34 &&
                                         str[5] == 0x7e
                        return "F12"
                    end
                    return ""
                end # if length(str) > 4
                if length(str) > 3
                    if str[2] == 0x5b && str[3] == 0x32 && str[4] == 0x7e
                       return "Ins"
                    end
                    if str[2] == 0x5b && str[3] == 0x33 && str[4] == 0x7e
                       return "Del"
                    end
                    if str[2] == 0x5b && str[3] == 0x31 && str[4] == 0x7e
                       return "Home"
                    end
                    if str[2] == 0x5b && str[3] == 0x34 && str[4] == 0x7e
                       return "End"
                    end
                    if str[2] == 0x5b && str[3] == 0x35 && str[4] == 0x7e
                       return "PgUp"
                    end
                    if str[2] == 0x5b && str[3] == 0x36 && str[4] == 0x7e
                       return "PgDn"
                    end
                    return ""
                end # if length(str) > 3
                if str[2] == 0x5b && sr[3] == 0x41
                    return "UpArrow"
                end
                if str[2] == 0x5b && sr[3] == 0x42
                    return "DnArrow"
                end
                if str[2] == 0x5b && sr[3] == 0x43
                    return "RightArrow"
                end
                if str[2] == 0x5b && sr[3] == 0x44
                    return "LeftArrow"
                end
                return ""
                # We had an Escape
            end # if str[1] == 0x1b
            x = convert(Int, str[1])
            if x == 8
                return "BackSpace"
            end
            if x == 9
                return "Tab"
            end
            if x == 13
                return "Enter"
            end
            if x < 27
                y = 64 + x
                return "Ctrl-" * Char(y)
            end
            if x < 128 && x > 31
                return String(str)
            end
            return ""
            # We had a key pressed!
        end # if ba > 0
        if s == ""
           # this is a one time test
           return s
        end
        if s != "0"
            # this means we have a timed wait
            if seconds < 0
                # first time
                seconds = parse(Int, s)
            else
                seconds = seconds - 1
                if seconds == 0
                   return ""
                end
            end
    # else
            # this means we wait until aa key is pressed
        end
        sleep(.9)
    end # while true
end # function inkey(s)

```
