Capture characters from standard input without waiting for enter to be pressed


Question

I can never remember how I do this because it comes up so infrequently for me. But in C or C++, what is the best way to read a character from standard input without waiting for a newline (press enter).

Also ideally it wouldn't echo the input character to the screen. I just want to capture keystrokes with out effecting the console screen.

1
158
9/20/2014 7:44:41 AM

Accepted Answer

That's not possible in a portable manner in pure C++, because it depends too much on the terminal used that may be connected with stdin (they are usually line buffered). You can, however use a library for that:

  1. conio available with Windows compilers. Use the _getch() function to give you a character without waiting for the Enter key. I'm not a frequent Windows developer, but I've seen my classmates just include <conio.h> and use it. See conio.h at Wikipedia. It lists getch(), which is declared deprecated in Visual C++.

  2. curses available for Linux. Compatible curses implementations are available for Windows too. It has also a getch() function. (try man getch to view its manpage). See Curses at Wikipedia.

I would recommend you to use curses if you aim for cross platform compatibility. That said, I'm sure there are functions that you can use to switch off line buffering (I believe that's called "raw mode", as opposed to "cooked mode" - look into man stty). Curses would handle that for you in a portable manner, if I'm not mistaken.

89
6/27/2019 12:17:59 AM

On Linux (and other unix-like systems) this can be done in following way:

#include <unistd.h>
#include <termios.h>

char getch() {
        char buf = 0;
        struct termios old = {0};
        if (tcgetattr(0, &old) < 0)
                perror("tcsetattr()");
        old.c_lflag &= ~ICANON;
        old.c_lflag &= ~ECHO;
        old.c_cc[VMIN] = 1;
        old.c_cc[VTIME] = 0;
        if (tcsetattr(0, TCSANOW, &old) < 0)
                perror("tcsetattr ICANON");
        if (read(0, &buf, 1) < 0)
                perror ("read()");
        old.c_lflag |= ICANON;
        old.c_lflag |= ECHO;
        if (tcsetattr(0, TCSADRAIN, &old) < 0)
                perror ("tcsetattr ~ICANON");
        return (buf);
}

Basically you have to turn off canonical mode (and echo mode to suppress echoing).


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon