How To Read Arrow Key

i am trying to use

const stdin = std.io.getStdIn();
var buf: [1]u8 = undefined;
const bytes_read = try stdin.read(&buf);

but whenever i press arrow keys buf gives 27 instead of \x1b

how to read and process arrow key press

Hello @shahidkhan

27 decimal and 1b hexadecimal is the same number; it is the number for ascii escape code.

Terminals send a sequence of multiple bytes for the special keys that are pressed (such as arrows, function keys and home, end, page up, page down, etc.)
To get the entire sequence you need a bigger buffer.

The terminal, by default, is in a special editing mode (called cooked or canonical mode) that requires the Enter key to be pressed for the read call to return. You can change this mode to raw mode, by turning off echo and canonical flags, using:

See also:

2 Likes

Unless you specifically want to hand write a terminal IO layer for some reason I recommend you use a library instead. Terminal encoding of various types of input and output is a pretty deep rabbit hole and probably not very interesting.

I can highly recommand GitHub - rockorager/libvaxis: a modern tui library written in zig

2 Likes

i am learning zig by developing a text editor so i think it is ok


pub fn enableRawMode() void {
    if (c.tcgetattr(std.c.STDIN_FILENO, &E.orig_termios) == -1) {
        die("tcgetattr") catch {};
    }

    var raw = E.orig_termios;
    raw.c_iflag &= ~(@as(c_uint, c.BRKINT) | @as(c_uint, c.ICRNL) | @as(c_uint, c.INPCK) | @as(c_uint, c.ISTRIP) | @as(c_uint, c.IXON));
    raw.c_oflag &= ~(@as(c_uint, c.OPOST));
    raw.c_cflag |= ~@as(c_uint, c.CS8);
    raw.c_lflag &= ~(@as(c_uint, c.ECHO) | @as(c_uint, c.ICANON) | @as(c_uint, c.IEXTEN) | @as(c_uint, c.ISIG));
    raw.c_cc[c.VMIN] = 1;
    raw.c_cc[c.VTIME] = 0;

    if (c.tcsetattr(std.c.STDIN_FILENO, c.TCSAFLUSH, &raw) == -1) {
        std.debug.print("Error: tcsetattr failed\n", .{});
        die("tcsetattr") catch {};
    }

    // std.process.cleanExit(disableRawMode);
}
``` i did the same here but i dont know why this canocial form is not tunred on

If you write your text editor with raw terminal IO 90% of your code will be your terminal library.

If you write your text editor with libvaxis 90% of your code will be your text editor.

So it just depends what you want to write.

ok for sake of learning and understanding i would like to know why my arrow key presses are not working as i expected can you please help

You probably want to set CS8 and not all the other bits (~ is binary not):

    raw.c_cflag |= @as(c_uint, c.CS8);

Also, you might want to use the Zig API instead of the C API. It’s a lot more idiomatic and should work without linking libc.

Here is a nice example of how it’s done:

Also, keep in mind that arrow (and other) keys will produce multiple bytes, which you will need to interpret – maybe with a little state machine.

Also, depending on the terminal, you will not be able to capture / differentiate some key presses (Shift vs Alt vs Ctrl vs Shift+Alt, etc).

Here is a nice example of a CSI escape sequence parser which you’ll need for reading arrow keys:

1 Like

I’ve been working on some cross platform non-blocking keyboard handling code recently. It might be useful for what you’re doing.

In raw_mode_start(), I’m setting the terminal to raw mode commy/src/main.zig at main Ā· ringtailsoftware/commy Ā· GitHub

For posix I’m using std.posix.pollfd() to check if stdin has data before doing a read. This prevents execution blocking until there is input available. commy/src/main.zig at main Ā· ringtailsoftware/commy Ā· GitHub

Then, once I have bytes of data I’m checking for the escape codes which correspond to arrow keys. commy/src/main.zig at main Ā· ringtailsoftware/commy Ā· GitHub

Key escape sequences can be different lengths, so I’m running the bytes through a sliding window where I can attempt to string match the escape sequences. commy/src/keywindow.zig at main Ā· ringtailsoftware/commy Ā· GitHub

1 Like

Keyboard event handling is a fun area. If you are fully intent on learning how that works, then I recommend reading through code that has done it, to understand what is needed.

This post contains some resources for learning:

@neurocyte thanks for the link i was searching for ways to avoid c apis becuase i want to learn zig using only zig things thanks lot for the link

1 Like

actually there was an issue from my side i was checking

if (try stdin.read(&seq) != 3) return '\x1b'; instead of 2 that is why

sorry for wrong question but i learned new things here

2 Likes

Don’t listen to the complainers because it’s an excellent approach to studying the Zig language.

you have something to read

oh great thanks for the link